package server

import (
	"context"

	"github.com/keys-pub/keys"
	"github.com/keys-pub/keys-ext/http/api"
	"github.com/keys-pub/keys/dstore"
	"github.com/keys-pub/keys/http"
	"github.com/labstack/echo/v4"
	"github.com/pkg/errors"
)

func (s *Server) putFollow(c echo.Context) error {
	s.logger.Infof("Server %s %s", c.Request().Method, c.Request().URL.String())
	ctx := c.Request().Context()

	auth, err := s.auth(c, newAuth("Authorization", "sender", nil))
	if err != nil {
		return s.ErrForbidden(c, err)
	}

	recipient, err := keys.ParseID(c.Param("recipient"))
	if err != nil {
		return s.ErrBadRequest(c, errors.Errorf("invalid recipient"))
	}

	follow := &api.Follow{Sender: auth.KID, Recipient: recipient}
	if err := s.fi.Set(ctx, dstore.Path("follows", recipient, "users", auth.KID), dstore.From(follow)); err != nil {
		return s.ErrInternalServer(c, err)
	}

	var out struct{}
	return JSON(c, http.StatusOK, out)
}

func (s *Server) getFollow(c echo.Context) error {
	s.logger.Infof("Server %s %s", c.Request().Method, c.Request().URL.String())
	ctx := c.Request().Context()

	auth, err := s.auth(c, newAuth("Authorization", "recipient", nil))
	if err != nil {
		return s.ErrForbidden(c, err)
	}
	sender, err := keys.ParseID(c.Param("sender"))
	if err != nil {
		return s.ErrBadRequest(c, errors.Errorf("invalid sender"))
	}

	follow, err := s.follow(ctx, sender, auth.KID)
	if err != nil {
		return s.ErrInternalServer(c, err)
	}

	out := api.FollowResponse{Follow: follow}
	return JSON(c, http.StatusOK, out)
}

func (s *Server) follow(ctx context.Context, sender keys.ID, recipient keys.ID) (*api.Follow, error) {
	var follow api.Follow
	ok, err := s.fi.Load(ctx, dstore.Path("follows", recipient, "users", sender), &follow)
	if err != nil {
		return nil, err
	}
	if !ok {
		return nil, nil
	}
	return &follow, nil
}

func (s *Server) getFollows(c echo.Context) error {
	s.logger.Infof("Server %s %s", c.Request().Method, c.Request().URL.String())
	ctx := c.Request().Context()

	auth, err := s.auth(c, newAuth("Authorization", "recipient", nil))
	if err != nil {
		return s.ErrForbidden(c, err)
	}

	iter, err := s.fi.DocumentIterator(ctx, dstore.Path("follows", auth.KID, "users"))
	if err != nil {
		return s.ErrInternalServer(c, err)
	}
	follows := []*api.Follow{}
	for {
		doc, err := iter.Next()
		if err != nil {
			return s.ErrInternalServer(c, err)
		}
		if doc == nil {
			break
		}
		var follow api.Follow
		if err := doc.To(&follow); err != nil {
			return s.ErrInternalServer(c, err)
		}
		follows = append(follows, &follow)
	}

	out := api.FollowsResponse{Follows: follows}
	return JSON(c, http.StatusOK, out)
}

func (s *Server) deleteFollow(c echo.Context) error {
	s.logger.Infof("Server %s %s", c.Request().Method, c.Request().URL.String())
	ctx := c.Request().Context()

	auth, err := s.auth(c, newAuth("Authorization", "sender", nil))
	if err != nil {
		return s.ErrForbidden(c, err)
	}

	recipient, err := keys.ParseID(c.Param("recipient"))
	if err != nil {
		return s.ErrBadRequest(c, errors.Errorf("invalid recipient"))
	}

	ok, err := s.fi.Delete(ctx, dstore.Path("follows", recipient, "users", auth.KID))
	if err != nil {
		return s.ErrInternalServer(c, err)
	}
	if !ok {
		return s.ErrNotFound(c, errors.Errorf("follow not found"))
	}

	var out struct{}
	return JSON(c, http.StatusOK, out)
}
