package keychains

import (
	"errors"
	"net/http"

	"github.com/gofiber/fiber/v2"
	"rul.sh/vaulterm/server/models"
	"rul.sh/vaulterm/server/utils"
)

func Router(app fiber.Router) {
	router := app.Group("/keychains")

	router.Get("/", getAll)
	router.Post("/", create)
	router.Put("/:id", update)
}

type GetAllResult struct {
	*models.Keychain
	Data map[string]interface{} `json:"data"`
}

func getAll(c *fiber.Ctx) error {
	teamId := c.Query("teamId")
	withData := c.Query("withData")

	user := utils.GetUser(c)
	repo := NewRepository(&Keychains{User: user})

	if teamId != "" && !user.IsInTeam(&teamId) {
		return utils.ResponseError(c, errors.New("no access"), 403)
	}

	rows, err := repo.GetAll(GetAllOpt{TeamID: teamId})
	if err != nil {
		return utils.ResponseError(c, err, 500)
	}

	if withData != "true" || (teamId != "" && !user.TeamCanWrite(&teamId)) {
		return c.JSON(fiber.Map{"rows": rows})
	}

	res := make([]*GetAllResult, len(rows))
	doneCh := make(chan struct{})

	// Decrypt data
	for i, item := range rows {
		go func(i int, item *models.Keychain) {
			var data map[string]interface{}
			item.DecryptData(&data)

			res[i] = &GetAllResult{item, data}
			doneCh <- struct{}{}
		}(i, item)
	}

	for range rows {
		<-doneCh
	}

	return c.JSON(fiber.Map{"rows": res})
}

func create(c *fiber.Ctx) error {
	var body CreateKeychainSchema
	if err := c.BodyParser(&body); err != nil {
		return utils.ResponseError(c, err, 500)
	}

	user := utils.GetUser(c)
	repo := NewRepository(&Keychains{User: user})

	if body.TeamID != nil && !user.TeamCanWrite(body.TeamID) {
		return utils.ResponseError(c, errors.New("no access"), 403)
	}

	item := &models.Keychain{
		OwnerID: &user.ID,
		TeamID:  body.TeamID,
		Type:    body.Type,
		Label:   body.Label,
	}

	if err := item.EncryptData(body.Data); err != nil {
		return utils.ResponseError(c, err, 500)
	}

	if err := repo.Create(item); err != nil {
		return utils.ResponseError(c, err, 500)
	}

	return c.Status(http.StatusCreated).JSON(item)
}

func update(c *fiber.Ctx) error {
	var body CreateKeychainSchema
	if err := c.BodyParser(&body); err != nil {
		return utils.ResponseError(c, err, 500)
	}

	user := utils.GetUser(c)
	repo := NewRepository(&Keychains{User: user})

	id := c.Params("id")
	data, _ := repo.Get(id)
	if data == nil {
		return utils.ResponseError(c, errors.New("key not found"), 404)
	}
	if !data.CanWrite(&user.User) || !user.TeamCanWrite(body.TeamID) {
		return utils.ResponseError(c, errors.New("no access"), 403)
	}

	item := &models.Keychain{
		TeamID: body.TeamID,
		Type:   body.Type,
		Label:  body.Label,
	}

	if err := item.EncryptData(body.Data); err != nil {
		return utils.ResponseError(c, err, 500)
	}

	if err := repo.Update(id, item); err != nil {
		return utils.ResponseError(c, err, 500)
	}

	return c.JSON(item)
}