// Package handlers wires templates, auth, and database queries for the
// WhereWoof GOAT front-end.
package handlers

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

	"wherewoof/frontend/internal/auth"
	"wherewoof/frontend/internal/db"
	"wherewoof/frontend/internal/sms"
)

// Templates maps a page key to its parsed template set (base + page + partials).
// Each page is parsed as its own set so the shared "content" block name
// doesn't collide across pages.
type Templates map[string]*template.Template

// App holds dependencies shared by all handlers.
type App struct {
	Queries *db.Queries
	Tpl     Templates
	Sender  sms.Sender
}

// New returns an App with the given query layer, template sets, and SMS sender.
func New(queries *db.Queries, tpl Templates, sender sms.Sender) *App {
	return &App{Queries: queries, Tpl: tpl, Sender: sender}
}

// PageData is the root data passed to the base layout.
type PageData struct {
	CurrentUser *db.User
	Title       string
	Error       string
	Data        any
}

func titleCase(s string) string {
	if s == "" {
		return s
	}
	return strings.ToUpper(s[:1]) + s[1:]
}

// LoadTemplates parses every page's template set from templates/.
func LoadTemplates() (Templates, error) {
	const dir = "templates"
	base := dir + "/base.html"
	pages := map[string][]string{
		"index":    {dir + "/index.html"},
		"register": {dir + "/register.html"},
		"login":    {dir + "/login.html"},
		"account":  {dir + "/account.html", dir + "/account-panel.html", dir + "/tag-list.html"},
		"edit":     {dir + "/tag-edit.html"},
		"public":   {dir + "/tag-public.html"},
		"notfound": {dir + "/not-found.html"},
	}
	funcs := template.FuncMap{"title": titleCase}
	tpl := make(Templates, len(pages))
	for name, files := range pages {
		paths := append([]string{base}, files...)
		t, err := template.New(name).Funcs(funcs).ParseFiles(paths...)
		if err != nil {
			return nil, err
		}
		tpl[name] = t
	}
	return tpl, nil
}

// render executes the page's base layout with a PageData populated from the
// authenticated user (if any).
func (a *App) render(w http.ResponseWriter, r *http.Request, page, title string, data any, errMsg string) {
	pd := PageData{Title: title, Data: data, Error: errMsg}
	if uid, ok := auth.GetUserID(r); ok {
		if u, err := a.Queries.GetUserByID(r.Context(), uid); err == nil {
			pd.CurrentUser = &u
		}
	}
	w.Header().Set("Content-Type", "text/html; charset=utf-8")
	if err := a.Tpl[page].ExecuteTemplate(w, "base", pd); err != nil {
		http.Error(w, "template error: "+err.Error(), http.StatusInternalServerError)
	}
}

// renderPartial executes a named partial (e.g. "account-panel") for HTMX swaps.
func (a *App) renderPartial(w http.ResponseWriter, page, partial string, data any) {
	w.Header().Set("Content-Type", "text/html; charset=utf-8")
	if err := a.Tpl[page].ExecuteTemplate(w, partial, data); err != nil {
		http.Error(w, "template error: "+err.Error(), http.StatusInternalServerError)
	}
}

// RequireAuth redirects unauthenticated requests to /login.
func (a *App) RequireAuth(next http.HandlerFunc) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		if _, ok := auth.GetUserID(r); !ok {
			http.Redirect(w, r, "/login", http.StatusSeeOther)
			return
		}
		next(w, r)
	}
}
