package v1

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

	"github.com/labstack/echo/v4"
	"github.com/usememos/memos/store"
)

type MemoRelationType string

const (
	MemoRelationReference  MemoRelationType = "REFERENCE"
	MemoRelationAdditional MemoRelationType = "ADDITIONAL"
)

type MemoRelation struct {
	MemoID        int              `json:"memoId"`
	RelatedMemoID int              `json:"relatedMemoId"`
	Type          MemoRelationType `json:"type"`
}

type UpsertMemoRelationRequest struct {
	RelatedMemoID int              `json:"relatedMemoId"`
	Type          MemoRelationType `json:"type"`
}

func (s *APIV1Service) registerMemoRelationRoutes(g *echo.Group) {
	g.POST("/memo/:memoId/relation", func(c echo.Context) error {
		ctx := c.Request().Context()
		memoID, err := strconv.Atoi(c.Param("memoId"))
		if err != nil {
			return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("ID is not a number: %s", c.Param("memoId"))).SetInternal(err)
		}

		request := &UpsertMemoRelationRequest{}
		if err := json.NewDecoder(c.Request().Body).Decode(request); err != nil {
			return echo.NewHTTPError(http.StatusBadRequest, "Malformatted post memo relation request").SetInternal(err)
		}

		memoRelation, err := s.Store.UpsertMemoRelation(ctx, &store.MemoRelation{
			MemoID:        memoID,
			RelatedMemoID: request.RelatedMemoID,
			Type:          store.MemoRelationType(request.Type),
		})
		if err != nil {
			return echo.NewHTTPError(http.StatusInternalServerError, "Failed to upsert memo relation").SetInternal(err)
		}
		return c.JSON(http.StatusOK, memoRelation)
	})

	g.GET("/memo/:memoId/relation", func(c echo.Context) error {
		ctx := c.Request().Context()
		memoID, err := strconv.Atoi(c.Param("memoId"))
		if err != nil {
			return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("ID is not a number: %s", c.Param("memoId"))).SetInternal(err)
		}

		memoRelationList, err := s.Store.ListMemoRelations(ctx, &store.FindMemoRelation{
			MemoID: &memoID,
		})
		if err != nil {
			return echo.NewHTTPError(http.StatusInternalServerError, "Failed to list memo relations").SetInternal(err)
		}
		return c.JSON(http.StatusOK, memoRelationList)
	})

	g.DELETE("/memo/:memoId/relation/:relatedMemoId/type/:relationType", func(c echo.Context) error {
		ctx := c.Request().Context()
		memoID, err := strconv.Atoi(c.Param("memoId"))
		if err != nil {
			return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Memo ID is not a number: %s", c.Param("memoId"))).SetInternal(err)
		}
		relatedMemoID, err := strconv.Atoi(c.Param("relatedMemoId"))
		if err != nil {
			return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Related memo ID is not a number: %s", c.Param("resourceId"))).SetInternal(err)
		}
		relationType := store.MemoRelationType(c.Param("relationType"))

		if err := s.Store.DeleteMemoRelation(ctx, &store.DeleteMemoRelation{
			MemoID:        &memoID,
			RelatedMemoID: &relatedMemoID,
			Type:          &relationType,
		}); err != nil {
			return echo.NewHTTPError(http.StatusInternalServerError, "Failed to delete memo relation").SetInternal(err)
		}
		return c.JSON(http.StatusOK, true)
	})
}

func convertMemoRelationFromStore(memoRelation *store.MemoRelation) *MemoRelation {
	return &MemoRelation{
		MemoID:        memoRelation.MemoID,
		RelatedMemoID: memoRelation.RelatedMemoID,
		Type:          MemoRelationType(memoRelation.Type),
	}
}