// Copyright (C) 2023 Marius Schellenberger

package server

import (
	"encoding/json"
	"net/http"

	"git.giftfish.de/ston1th/keyctl/pkg/api/types"
	"git.giftfish.de/ston1th/keyctl/pkg/api/v1/schema"
	"git.giftfish.de/ston1th/keyctl/pkg/store"
)

func local(h types.CtxHandler) types.CtxHandler {
	return func(ctx *types.Context) {
		ip := ctx.ClientIP()
		if ip == "@" {
			h(ctx)
			return
		}
		ctx.Err(types.ErrForbidden)
	}
}

func newHandler(ctx *types.Context) {
	if ctx.Method() == "POST" {
		b, err := ctx.ReadBody()
		if err != nil {
			ctx.Log.Error(err, "error reading request body")
			ctx.Err(types.ErrISE)
			return
		}
		var newKey schema.NewKey
		err = json.Unmarshal(b, &newKey)
		if err != nil {
			ctx.Log.Error(err, "error decoding request body")
			ctx.Err(types.ErrInvalid)
			return
		}
		err = newKey.Validate()
		if err != nil {
			ctx.Log.Error(err, "error empty name for new key")
			ctx.Err(types.ErrInvalid)
			return
		}
		key, err := ctx.Data.DB.CreateKey(newKey.Name, newKey.Existing, newKey.Size, newKey.Encoding, newKey.Type)
		if err != nil {
			ctx.Log.Error(err, "error writing key", "name", newKey.Name)
			ctx.Err(types.ErrISE)
			return
		}
		ctx.JSON(schema.Key(key))
	}
}

func keyHandler(ctx *types.Context) {
	id := ctx.Var("id")
	ip := ctx.ClientIP()
	k, err := ctx.Data.DB.GetKey(id)
	if err != nil {
		ctx.Log.Error(err, "error reading key", "id", id)
		if err == store.ErrKeyNotFound {
			ctx.Err(types.ErrKeyNotFound)
			return
		}
		ctx.Err(types.ErrISE)
		return
	}
	switch ctx.Method() {
	case "GET":
		aid := ctx.Var("aid")
		if aid == "" {
			aid, err = ctx.Data.Approver.New(id, k.Name, ip)
			if err != nil {
				ctx.Log.Error(err, "error creating approval", "id", id, "name", k.Name)
				ctx.Err(types.ErrTooManyApprovals)
				return
			}
			ctx.Redirect(schema.ApproveURL(id, aid), http.StatusFound)
			return
		}
		s := ctx.Data.Approver.Status(id, aid, ip)
		switch s {
		case types.Pending:
			ctx.Redirect(schema.ApproveURL(id, aid), http.StatusFound)
			return
		case types.Rejected:
			ctx.Err(types.ErrKeyRequestRejected)
			return
		case types.NotFound:
			ctx.Err(types.ErrReqNotFound)
			return
		}
		k, err := ctx.Data.DB.GetKeyWithSecret(id)
		if err != nil {
			ctx.Log.Error(err, "error reading key", "id", id)
			if err == store.ErrKeyNotFound {
				ctx.Err(types.ErrNotFound)
				return
			}
			ctx.Err(types.ErrISE)
			return
		}
		ctx.JSON(schema.Key(k))
	case "DELETE":
		err = ctx.Data.DB.DeleteKey(id)
		if err != nil {
			ctx.Log.Error(err, "error deleting key", "id", id, "name", k.Name)
			if err == store.ErrKeyNotFound {
				ctx.Err(types.ErrNotFound)
				return
			}
			ctx.Err(types.ErrISE)
			return
		}
		ctx.OK()
	}
}

func reqListHandler(ctx *types.Context) {
	l := ctx.Data.Approver.List()
	ctx.JSON(schema.NewApprovals(l))
}

func keyListHandler(ctx *types.Context) {
	k, err := ctx.Data.DB.GetKeys()
	if err != nil {
		ctx.Log.Error(err, "error reading keys")
		ctx.Err(types.ErrISE)
		return
	}
	ctx.JSON(schema.NewKeys(k))
}

func reqHandler(ctx *types.Context) {
	id := ctx.Var("id")
	status := types.Pending
	switch ctx.Method() {
	case "PUT":
		status = types.Approved
	case "DELETE":
		status = types.Rejected
	}
	err := ctx.Data.Approver.Update(id, status)
	if err != nil {
		ctx.Log.Error(err, "error updating key request")
		ctx.Err(types.ErrReqNotFound)
		return
	}
	ctx.OK()
}
