package bot

import (
	"errors"
	"net/http"

	"codeberg.org/nextgo/dbots/internal/db"
	"codeberg.org/nextgo/dbots/internal/discord"
	"codeberg.org/nextgo/dbots/internal/errorutil"
	"codeberg.org/nextgo/dbots/internal/middleware"
	"codeberg.org/nextgo/dbots/internal/paginate"

	"github.com/go-chi/chi/v5"
	"github.com/go-chi/render"
)

type Router struct {
	bots   *Service
	router chi.Router
}

func NewRouter(q *db.Queries, client *discord.Client) *Router {
	return &Router{
		bots:   NewService(q, client),
		router: chi.NewRouter(),
	}
}

func (r *Router) Routes() http.Handler {
	r.router.Get("/search", r.searchBots)
	r.router.With(middleware.AuthGuardMiddleware).Post("/", r.submitBot)
	r.router.Route("/{botID}", func(router chi.Router) {
		router.Use(middleware.BotContext(r.bots.q))
		router.With(r.BotCache).Get("/", r.getBot)
		router.Route("/co-owners", func(c chi.Router) {
			c.Use(middleware.AuthGuardMiddleware)
			c.Get("/", r.listCoOwners)
			c.Post("/{userID}", r.addCoOwner)
			c.Delete("/{userID}", r.removeCoOwner)
		})
	})

	return r.router
}

func (r *Router) submitBot(w http.ResponseWriter, req *http.Request) {
	var data CreateBotRequest

	if err := render.Bind(req, &data); err != nil {
		render.Render(w, req, errorutil.ErrInvalidRequest(err))
		return
	}

	ctx := req.Context()
	bot, err := r.bots.Submit(ctx, data)
	if err != nil {
		if errors.Is(err, errorutil.ErrBotAlreadyExists) {
			render.Render(w, req, errorutil.ErrInvalidRequest(err))
		} else {
			render.Render(w, req, errorutil.ErrInternal(err))
		}
		return
	}

	render.Status(req, http.StatusCreated)
	render.JSON(w, req, bot)
}

func (r *Router) getBot(w http.ResponseWriter, req *http.Request) {
	ctx := req.Context()
	bot := middleware.GetBot(ctx)

	render.JSON(w, req, bot)
}

func (r *Router) searchBots(w http.ResponseWriter, req *http.Request) {
	ctx := req.Context()
	query := req.URL.Query().Get("q")
	p := paginate.ParseParams(req)

	page, err := r.bots.List(ctx, query, p)
	if err != nil {
		render.Render(w, req, errorutil.ErrInvalidRequest(err))
		return
	}

	render.JSON(w, req, page)
}

func (r *Router) listCoOwners(w http.ResponseWriter, req *http.Request) {
	ctx := req.Context()
	bot := middleware.GetBot(ctx)

	owners, err := r.bots.ListCoOwners(ctx, bot.ID)
	if err != nil {
		render.Render(w, req, errorutil.ErrInternal(err))
		return
	}

	render.JSON(w, req, owners)
}

func (r *Router) addCoOwner(w http.ResponseWriter, req *http.Request) {
	ctx := req.Context()
	bot := middleware.GetBot(ctx)
	userID := chi.URLParam(req, "userID")

	if err := r.bots.AddCoOwner(ctx, bot.ID, userID); err != nil {
		switch {
		case errors.Is(err, errorutil.ErrNotFound.Err):
			render.Render(w, req, errorutil.ErrNotFound)
		case errors.Is(err, errorutil.ErrForbidden.Err):
			render.Render(w, req, errorutil.ErrForbidden)
		default:
			render.Render(w, req, errorutil.ErrInvalidRequest(err))
		}
		return
	}

	render.NoContent(w, req)
}

func (r *Router) removeCoOwner(w http.ResponseWriter, req *http.Request) {
	ctx := req.Context()
	bot := middleware.GetBot(ctx)
	userID := chi.URLParam(req, "userID")

	if err := r.bots.RemoveCoOwner(ctx, bot.ID, userID); err != nil {
		switch {
		case errors.Is(err, errorutil.ErrNotFound.Err):
			render.Render(w, req, errorutil.ErrNotFound)
		case errors.Is(err, errorutil.ErrForbidden.Err):
			render.Render(w, req, errorutil.ErrForbidden)
		default:
			render.Render(w, req, errorutil.ErrInternal(err))
		}
		return
	}

	render.NoContent(w, req)
}

func (r *Router) BotCache(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
		w.Header().Add("Cache-Control", "max-age=3600")
		next.ServeHTTP(w, req)
	})
}
