package panel

import (
	"context"
	"html/template"
	"net/http"

	"github.com/FAU-CDI/wisski-distillery/internal/dis/component/auth"
	"github.com/FAU-CDI/wisski-distillery/internal/dis/component/server/assets"
	"github.com/FAU-CDI/wisski-distillery/internal/dis/component/server/templating"
	"github.com/FAU-CDI/wisski-distillery/internal/models"
	"github.com/rs/zerolog"
	"github.com/tkw1536/pkglib/httpx"
	"github.com/tkw1536/pkglib/httpx/field"

	_ "embed"
)

//go:embed "templates/tokens.html"
var tokensHTML []byte
var tokensTemplate = templating.Parse[TokenTemplateContext](
	"tokens.html", tokensHTML, nil,

	templating.Title("Tokens"),
	templating.Assets(assets.AssetsUser),
)

type TokenTemplateContext struct {
	templating.RuntimeFlags

	Domain template.URL // server base URL
	Tokens []models.Token
}

func (panel *UserPanel) tokensRoute(ctx context.Context) http.Handler {
	tpl := tokensTemplate.Prepare(
		panel.dependencies.Templating,
		templating.Crumbs(
			menuUser,
			menuTokens,
		),
		templating.Actions(
			menuTokensAdd,
		),
	)

	return tpl.HTMLHandler(func(r *http.Request) (tc TokenTemplateContext, err error) {
		// list the user
		user, err := panel.dependencies.Auth.UserOfSession(r)
		if err != nil || user == nil {
			return tc, err
		}

		tc.Domain = template.URL(panel.Config.HTTP.JoinPath().String())

		// get the tokens
		tc.Tokens, err = panel.dependencies.Tokens.Tokens(r.Context(), user.User.User)
		return tc, err
	})
}

func (panel *UserPanel) tokensDeleteRoute(ctx context.Context) http.Handler {
	logger := zerolog.Ctx(ctx)
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if err := r.ParseForm(); err != nil {
			logger.Err(err).Str("action", "delete token").Msg("failed to parse form")
			httpx.HTMLInterceptor.Fallback.ServeHTTP(w, r)
			return
		}
		user, err := panel.dependencies.Auth.UserOfSession(r)
		if err != nil {
			logger.Err(err).Str("action", "delete token").Msg("failed to get current user")
			httpx.HTMLInterceptor.Fallback.ServeHTTP(w, r)
			return
		}

		id := r.PostFormValue("id")
		if id == "" {
			logger.Err(err).Str("action", "delete token").Msg("failed to get token")
			httpx.HTMLInterceptor.Fallback.ServeHTTP(w, r)
			return
		}

		if err := panel.dependencies.Tokens.Remove(r.Context(), user.User.User, id); err != nil {
			logger.Err(err).Str("action", "delete token").Msg("failed to delete token")
			httpx.HTMLInterceptor.Fallback.ServeHTTP(w, r)
			return
		}

		http.Redirect(w, r, string(menuTokens.Path), http.StatusSeeOther)
	})
}

//go:embed "templates/tokens_add.html"
var tokensAddHTML []byte
var tokensAddTemplate = templating.ParseForm(
	"tokens_add.html", tokensAddHTML, httpx.FormTemplate,
	templating.Title("Add Token"),
	templating.Assets(assets.AssetsUser),
)

type addTokenResult struct {
	User        *auth.AuthUser
	Description string
	Scopes      []string
}

//go:embed "templates/token_created.html"
var tokenCreatedHTML []byte
var tokenCreateTemplate = templating.Parse[TokenCreateContext](
	"token_created.html", tokenCreatedHTML, httpx.FormTemplate,
	templating.Title("Add Token"),
	templating.Assets(assets.AssetsUser),
)

type TokenCreateContext struct {
	templating.RuntimeFlags

	Domain template.URL // server base URL
	Token  *models.Token
}

func (panel *UserPanel) tokensAddRoute(ctx context.Context) http.Handler {
	tplForm := tokensAddTemplate.Prepare(
		panel.dependencies.Templating,
		templating.Crumbs(
			menuUser,
			menuTokens,
			menuTokensAdd,
		),
	)

	tplDone := tokenCreateTemplate.Prepare(
		panel.dependencies.Templating,
		templating.Crumbs(
			menuUser,
			menuTokens,
			menuTokensAdd,
		),
	)

	return &httpx.Form[addTokenResult]{
		Fields: []field.Field{
			{Name: "description", Type: field.Text, Label: "Description"},
		},
		FieldTemplate: field.PureCSSFieldTemplate,

		RenderTemplate:        tplForm.Template(),
		RenderTemplateContext: templating.FormTemplateContext(tplForm),

		Validate: func(r *http.Request, values map[string]string) (at addTokenResult, err error) {
			at.User, err = panel.dependencies.Auth.UserOfSession(r)
			if err != nil || at.User == nil {
				return at, errInvalidUser
			}

			at.Description = values["description"]
			if at.Description == "" {
				at.Description = "API Key"
			}

			at.Scopes = nil

			return at, nil
		},

		RenderSuccess: func(at addTokenResult, values map[string]string, w http.ResponseWriter, r *http.Request) error {
			// add the key to the user
			tok, err := panel.dependencies.Tokens.Add(r.Context(), at.User.User.User, at.Description, at.Scopes)
			if err != nil {
				return err
			}
			if err != nil {
				return errAddToken
			}

			// render the created context
			return httpx.WriteHTML(tplDone.Context(r, TokenCreateContext{
				Domain: template.URL(panel.Config.HTTP.JoinPath().String()),
				Token:  tok,
			}), nil, tplDone.Template(), "", w, r)
		},
	}
}
