package handlers

import (
	"context"
	"fmt"
	"net/http"
	"slices"
	"strings"

	"github.com/go-playground/validator/v10"
	"github.com/gosimple/slug"
	"github.com/labstack/echo/v4"
	"github.com/mentos1386/zdravko/database/models"
	"github.com/mentos1386/zdravko/internal/server/services"
	"github.com/mentos1386/zdravko/web/templates/components"
)

type CreateTarget struct {
	Name       string `validate:"required"`
	Group      string `validate:"required"`
	Visibility string `validate:"required,oneof=PUBLIC PRIVATE"`
	Metadata   string `validate:"required"`
}

type UpdateTarget struct {
	Group      string `validate:"required"`
	Visibility string `validate:"required,oneof=PUBLIC PRIVATE"`
	Metadata   string `validate:"required"`
}

type SettingsTargets struct {
	*Settings
	Targets      map[string][]*models.Target
	TargetGroups []string
}

type SettingsTarget struct {
	*Settings
	Target  *models.Target
	History []*services.TargetHistory
}

type SettingsTargetCreate struct {
	*Settings
	Example string
}

func (h *BaseHandler) SettingsTargetsGET(c echo.Context) error {
	cc := c.(AuthenticatedContext)

	targets, err := services.GetTargets(context.Background(), h.db)
	if err != nil {
		return err
	}

	targetGroups := []string{}
	targetsByGroup := map[string][]*models.Target{}
	for _, target := range targets {
		targetsByGroup[target.Group] = append(targetsByGroup[target.Group], target)
		if !slices.Contains(targetGroups, target.Group) {
			targetGroups = append(targetGroups, target.Group)
		}
	}

	return c.Render(http.StatusOK, "settings_targets.tmpl", &SettingsTargets{
		Settings: NewSettings(
			cc.Principal.User,
			GetPageByTitle(SettingsPages, "Targets"),
			[]*components.Page{GetPageByTitle(SettingsPages, "Targets")},
		),
		Targets:      targetsByGroup,
		TargetGroups: targetGroups,
	})
}

func (h *BaseHandler) SettingsTargetsDescribeGET(c echo.Context) error {
	cc := c.(AuthenticatedContext)

	slug := c.Param("id")

	target, err := services.GetTarget(context.Background(), h.db, slug)
	if err != nil {
		return err
	}

	history, err := services.GetTargetHistoryForTarget(context.Background(), h.db, slug, services.TargetHistoryDateRange60Minutes)
	if err != nil {
		return err
	}

	maxElements := 10
	if len(history) < maxElements {
		maxElements = len(history)
	}

	return c.Render(http.StatusOK, "settings_targets_describe.tmpl", &SettingsTarget{
		Settings: NewSettings(
			cc.Principal.User,
			GetPageByTitle(SettingsPages, "Targets"),
			[]*components.Page{
				GetPageByTitle(SettingsPages, "Targets"),
				{
					Path:       fmt.Sprintf("/settings/targets/%s", slug),
					Title:      "Describe",
					Breadcrumb: target.Name,
				},
			}),
		Target:  target,
		History: history[:maxElements],
	})
}

func (h *BaseHandler) SettingsTargetsDescribeDELETE(c echo.Context) error {
	slug := c.Param("id")

	err := services.DeleteTarget(context.Background(), h.db, slug)
	if err != nil {
		return err
	}

	return c.Redirect(http.StatusSeeOther, "/settings/targets")
}

func (h *BaseHandler) SettingsTargetsDisableGET(c echo.Context) error {
	slug := c.Param("id")

	target, err := services.GetTarget(context.Background(), h.db, slug)
	if err != nil {
		return err
	}

	err = services.SetTargetState(context.Background(), h.db, target.Id, models.TargetStatePaused)
	if err != nil {
		return err
	}

	return c.Redirect(http.StatusSeeOther, fmt.Sprintf("/settings/targets/%s", slug))
}

func (h *BaseHandler) SettingsTargetsEnableGET(c echo.Context) error {
	slug := c.Param("id")

	target, err := services.GetTarget(context.Background(), h.db, slug)
	if err != nil {
		return err
	}

	err = services.SetTargetState(context.Background(), h.db, target.Id, models.TargetStateActive)
	if err != nil {
		return err
	}

	return c.Redirect(http.StatusSeeOther, fmt.Sprintf("/settings/targets/%s", slug))
}

func (h *BaseHandler) SettingsTargetsDescribePOST(c echo.Context) error {
	ctx := context.Background()
	targetId := c.Param("id")

	update := UpdateTarget{
		Group:      strings.ToLower(c.FormValue("group")),
		Visibility: c.FormValue("visibility"),
		Metadata:   c.FormValue("metadata"),
	}
	err := validator.New(validator.WithRequiredStructEnabled()).Struct(update)
	if err != nil {
		return err
	}

	target, err := services.GetTarget(ctx, h.db, targetId)
	if err != nil {
		return err
	}
	target.Group = update.Group
	target.Visibility = models.TargetVisibility(update.Visibility)
	target.Metadata = update.Metadata

	err = services.UpdateTarget(
		ctx,
		h.db,
		target,
	)
	if err != nil {
		return err
	}

	return c.Redirect(http.StatusSeeOther, fmt.Sprintf("/settings/targets/%s", targetId))
}

func (h *BaseHandler) SettingsTargetsCreateGET(c echo.Context) error {
	cc := c.(AuthenticatedContext)

	return c.Render(http.StatusOK, "settings_targets_create.tmpl", &SettingsTargetCreate{
		Settings: NewSettings(
			cc.Principal.User,
			GetPageByTitle(SettingsPages, "Targets"),
			[]*components.Page{
				GetPageByTitle(SettingsPages, "Targets"),
				GetPageByTitle(SettingsPages, "Targets Create"),
			},
		),
		Example: h.examples.Target,
	})
}

func (h *BaseHandler) SettingsTargetsCreatePOST(c echo.Context) error {
	ctx := context.Background()
	targetId := slug.Make(c.FormValue("name"))

	create := CreateTarget{
		Name:       c.FormValue("name"),
		Group:      strings.ToLower(c.FormValue("group")),
		Visibility: c.FormValue("visibility"),
		Metadata:   c.FormValue("metadata"),
	}
	err := validator.New(validator.WithRequiredStructEnabled()).Struct(create)
	if err != nil {
		return err
	}

	target := &models.Target{
		Name:       create.Name,
		Group:      create.Group,
		Id:         targetId,
		Visibility: models.TargetVisibility(create.Visibility),
		State:      models.TargetStateActive,
		Metadata:   create.Metadata,
	}

	err = services.CreateTarget(
		ctx,
		h.db,
		target,
	)
	if err != nil {
		return err
	}

	return c.Redirect(http.StatusSeeOther, "/settings/targets")
}
