package handlers

import (
	"encoding/json"
	"errors"
	"net/http"
	"time"

	"github.com/google/uuid"

	"mgit.msbls.de/m/paliad/internal/services"
)

// email_templates.go — backing endpoints for /admin/email-templates
// (t-paliad-072). Routes are registered behind RequireAdminFunc in
// handlers.go, so handlers assume the caller is global_admin and only the
// operation itself needs validation.
//
// All routes require DATABASE_URL — the editor only makes sense when there's
// somewhere to persist saves. Reads still serve embedded defaults via
// EmailTemplateService.GetActive when no DB row exists, but the editor as a
// surface is gated by requireDB just like /admin/team.

// emailTemplateSummary is one row in the list-templates response. Each (key,
// lang) pair gets its own summary so the editor's three-card list can show
// "Standard" or "Zuletzt geändert: <date>" per language.
type emailTemplateSummary struct {
	Key       string     `json:"key"`
	Lang      string     `json:"lang"`
	IsDefault bool       `json:"is_default"`
	UpdatedAt *time.Time `json:"updated_at,omitempty"`
	UpdatedBy *uuid.UUID `json:"updated_by,omitempty"`
}

// GET /api/admin/email-templates — summaries for every (canonical key × lang)
// pair, in canonical order. Used by the list page to render the per-template
// cards.
func handleAdminListEmailTemplates(w http.ResponseWriter, r *http.Request) {
	if !requireDB(w) {
		return
	}
	out := make([]emailTemplateSummary, 0, len(services.CanonicalEmailTemplateKeys)*len(services.EmailTemplateLanguages))
	for _, key := range services.CanonicalEmailTemplateKeys {
		for _, lang := range services.EmailTemplateLanguages {
			row, err := dbSvc.emailTemplate.GetActive(r.Context(), key, lang)
			if err != nil {
				writeJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()})
				return
			}
			out = append(out, emailTemplateSummary{
				Key:       key,
				Lang:      lang,
				IsDefault: row.IsDefault,
				UpdatedAt: row.UpdatedAt,
				UpdatedBy: row.UpdatedBy,
			})
		}
	}
	writeJSON(w, http.StatusOK, out)
}

// GET /api/admin/email-templates/{key}/{lang} — full active row (subject +
// body + IsDefault + updated_at). Editor uses this to populate the form.
func handleAdminGetEmailTemplate(w http.ResponseWriter, r *http.Request) {
	if !requireDB(w) {
		return
	}
	key := r.PathValue("key")
	lang := r.PathValue("lang")
	row, err := dbSvc.emailTemplate.GetActive(r.Context(), key, lang)
	if err != nil {
		writeEmailTemplateError(w, err)
		return
	}
	writeJSON(w, http.StatusOK, row)
}

// GET /api/admin/email-templates/{key}/variables — the variable contract for
// a key. Lang-agnostic (sample fields for both languages are in the payload).
func handleAdminEmailTemplateVariables(w http.ResponseWriter, r *http.Request) {
	if !requireDB(w) {
		return
	}
	key := r.PathValue("key")
	if !services.IsCanonicalKey(key) {
		writeJSON(w, http.StatusNotFound, map[string]string{"error": services.ErrTemplateUnknownKey.Error()})
		return
	}
	writeJSON(w, http.StatusOK, services.EmailTemplateVariables(key))
}

// previewRequest is the JSON shape for POST .../{key}/{lang}/preview.
type previewRequest struct {
	Subject string `json:"subject"`
	Body    string `json:"body"`
	// Slot is honoured for deadline_digest only ("morning" or "evening").
	Slot string `json:"slot,omitempty"`
}

type previewResponse struct {
	Subject string `json:"subject_rendered"`
	HTML    string `json:"html_rendered"`
}

// POST /api/admin/email-templates/{key}/{lang}/preview — render proposed
// subject + body against sample data, no persistence. 422 on parse error so
// the editor can surface the message inline.
func handleAdminPreviewEmailTemplate(w http.ResponseWriter, r *http.Request) {
	if !requireDB(w) {
		return
	}
	key := r.PathValue("key")
	lang := r.PathValue("lang")
	if !services.IsCanonicalKey(key) {
		writeJSON(w, http.StatusNotFound, map[string]string{"error": services.ErrTemplateUnknownKey.Error()})
		return
	}
	if lang != "de" && lang != "en" {
		writeJSON(w, http.StatusBadRequest, map[string]string{"error": services.ErrTemplateUnknownLang.Error()})
		return
	}
	var in previewRequest
	if err := json.NewDecoder(r.Body).Decode(&in); err != nil {
		writeJSON(w, http.StatusBadRequest, map[string]string{"error": "invalid JSON"})
		return
	}
	if err := services.ValidateTemplate(key, in.Subject, in.Body); err != nil {
		writeJSON(w, http.StatusUnprocessableEntity, map[string]string{"error": err.Error()})
		return
	}
	sample := services.EmailTemplateSampleData(key, lang, in.Slot)
	subj, html, err := dbSvc.mail.RenderPreview(key, lang, in.Subject, in.Body, sample)
	if err != nil {
		writeJSON(w, http.StatusUnprocessableEntity, map[string]string{"error": err.Error()})
		return
	}
	writeJSON(w, http.StatusOK, previewResponse{Subject: subj, HTML: html})
}

// saveRequest is the JSON shape for PUT .../{key}/{lang}.
type saveRequest struct {
	Subject string `json:"subject"`
	Body    string `json:"body"`
	Note    string `json:"note,omitempty"`
}

// PUT /api/admin/email-templates/{key}/{lang} — validate, upsert active row,
// append a version. Returns the new version row (incl. id). 422 on bad
// templates surfaces inline in the editor.
func handleAdminSaveEmailTemplate(w http.ResponseWriter, r *http.Request) {
	if !requireDB(w) {
		return
	}
	uid, ok := requireUser(w, r)
	if !ok {
		return
	}
	key := r.PathValue("key")
	lang := r.PathValue("lang")
	var in saveRequest
	if err := json.NewDecoder(r.Body).Decode(&in); err != nil {
		writeJSON(w, http.StatusBadRequest, map[string]string{"error": "invalid JSON"})
		return
	}
	ver, err := dbSvc.emailTemplate.Save(r.Context(), services.SaveInput{
		Key:     key,
		Lang:    lang,
		Subject: in.Subject,
		Body:    in.Body,
		Note:    in.Note,
		SavedBy: uid,
	})
	if err != nil {
		writeEmailTemplateError(w, err)
		return
	}
	writeJSON(w, http.StatusOK, ver)
}

// POST /api/admin/email-templates/{key}/{lang}/reset — drop the active row
// and append a 'reset' version. Subsequent renders fall through to the
// embedded default.
func handleAdminResetEmailTemplate(w http.ResponseWriter, r *http.Request) {
	if !requireDB(w) {
		return
	}
	uid, ok := requireUser(w, r)
	if !ok {
		return
	}
	ver, err := dbSvc.emailTemplate.Reset(r.Context(), r.PathValue("key"), r.PathValue("lang"), uid)
	if err != nil {
		writeEmailTemplateError(w, err)
		return
	}
	writeJSON(w, http.StatusOK, ver)
}

// GET /api/admin/email-templates/{key}/{lang}/versions — most-recent-first
// list, capped at EmailTemplateVersionRetention.
func handleAdminListEmailTemplateVersions(w http.ResponseWriter, r *http.Request) {
	if !requireDB(w) {
		return
	}
	rows, err := dbSvc.emailTemplate.ListVersions(r.Context(), r.PathValue("key"), r.PathValue("lang"))
	if err != nil {
		writeEmailTemplateError(w, err)
		return
	}
	writeJSON(w, http.StatusOK, rows)
}

// POST /api/admin/email-templates/{key}/{lang}/restore/{version_id} —
// copy a historical version back into the active row. Always appends a
// fresh version row that records the restore source.
func handleAdminRestoreEmailTemplateVersion(w http.ResponseWriter, r *http.Request) {
	if !requireDB(w) {
		return
	}
	uid, ok := requireUser(w, r)
	if !ok {
		return
	}
	versionID, err := uuid.Parse(r.PathValue("version_id"))
	if err != nil {
		writeJSON(w, http.StatusBadRequest, map[string]string{"error": "invalid version id"})
		return
	}
	ver, err := dbSvc.emailTemplate.RestoreVersion(r.Context(),
		r.PathValue("key"), r.PathValue("lang"), versionID, uid)
	if err != nil {
		writeEmailTemplateError(w, err)
		return
	}
	writeJSON(w, http.StatusOK, ver)
}

// handleAdminEmailTemplatesPage serves the SPA shell for the list page.
func handleAdminEmailTemplatesPage(w http.ResponseWriter, r *http.Request) {
	http.ServeFile(w, r, "dist/admin-email-templates.html")
}

// handleAdminEmailTemplatesEditPage serves the SPA shell for the editor.
// Same shell for every key — the client reads {key} from the URL and fetches
// the active row.
func handleAdminEmailTemplatesEditPage(w http.ResponseWriter, r *http.Request) {
	http.ServeFile(w, r, "dist/admin-email-templates-edit.html")
}

// writeEmailTemplateError maps EmailTemplateService sentinel errors to
// status codes the editor can react to. Anything else is 500.
func writeEmailTemplateError(w http.ResponseWriter, err error) {
	switch {
	case errors.Is(err, services.ErrTemplateUnknownKey),
		errors.Is(err, services.ErrTemplateVersionNotFound):
		writeJSON(w, http.StatusNotFound, map[string]string{"error": err.Error()})
	case errors.Is(err, services.ErrTemplateUnknownLang):
		writeJSON(w, http.StatusBadRequest, map[string]string{"error": err.Error()})
	case errors.Is(err, services.ErrTemplateBodySyntax),
		errors.Is(err, services.ErrTemplateSubjectSyntax),
		errors.Is(err, services.ErrTemplateMissingContent),
		errors.Is(err, services.ErrTemplateMissingBaseBlock):
		writeJSON(w, http.StatusUnprocessableEntity, map[string]string{"error": err.Error()})
	case errors.Is(err, services.ErrTemplateStoreUnavailable):
		writeJSON(w, http.StatusServiceUnavailable, map[string]string{"error": err.Error()})
	default:
		writeJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()})
	}
}
