package handlers

import (
	"fmt"
	"html/template"
	"strconv"
	"strings"

	qfassets "git.mchus.pro/mchus/quoteforge"
	"git.mchus.pro/mchus/quoteforge/internal/appmeta"
	"git.mchus.pro/mchus/quoteforge/internal/localdb"
	"git.mchus.pro/mchus/quoteforge/internal/models"
	"github.com/gin-gonic/gin"
)

type WebHandler struct {
	templates map[string]*template.Template
	localDB   *localdb.LocalDB
}

func NewWebHandler(_ string, localDB *localdb.LocalDB) (*WebHandler, error) {
	funcMap := template.FuncMap{
		"sub": func(a, b int) int { return a - b },
		"add": func(a, b int) int { return a + b },
		"mul": func(a, b int) int { return a * b },
		"div": func(a, b int) int {
			if b == 0 {
				return 0
			}
			return (a + b - 1) / b
		},
		"deref": func(f *float64) float64 {
			if f == nil {
				return 0
			}
			return *f
		},
		"jsesc": func(s string) string {
			// Escape string for safe use in JavaScript
			result := ""
			for _, r := range s {
				switch r {
				case '\\':
					result += "\\\\"
				case '\'':
					result += "\\'"
				case '"':
					result += "\\\""
				case '\n':
					result += "\\n"
				case '\r':
					result += "\\r"
				case '\t':
					result += "\\t"
				default:
					result += string(r)
				}
			}
			return result
		},
	}

	templates := make(map[string]*template.Template)
	// Load each page template with base
	simplePages := []string{"configs.html", "projects.html", "project_detail.html", "pricelists.html", "pricelist_detail.html", "config_revisions.html", "partnumber_books.html"}
	for _, page := range simplePages {
		var tmpl *template.Template
		var err error
		tmpl, err = template.New("").Funcs(funcMap).ParseFS(
			qfassets.TemplatesFS,
			"web/templates/base.html",
			"web/templates/"+page,
		)
		if err != nil {
			return nil, err
		}
		templates[page] = tmpl
	}

	// Index page needs components_list.html as well
	var indexTmpl *template.Template
	var err error
	indexTmpl, err = template.New("").Funcs(funcMap).ParseFS(
		qfassets.TemplatesFS,
		"web/templates/base.html",
		"web/templates/index.html",
		"web/templates/components_list.html",
	)
	if err != nil {
		return nil, err
	}
	templates["index.html"] = indexTmpl

	// Load partial templates (no base needed)
	partials := []string{"components_list.html"}
	for _, partial := range partials {
		var tmpl *template.Template
		var err error
		tmpl, err = template.New("").Funcs(funcMap).ParseFS(
			qfassets.TemplatesFS,
			"web/templates/"+partial,
		)
		if err != nil {
			return nil, err
		}
		templates[partial] = tmpl
	}

	return &WebHandler{
		templates: templates,
		localDB:   localDB,
	}, nil
}

func (h *WebHandler) render(c *gin.Context, name string, data gin.H) {
	data["AppVersion"] = appmeta.Version()
	c.Header("Content-Type", "text/html; charset=utf-8")
	tmpl, ok := h.templates[name]
	if !ok {
		_ = c.Error(fmt.Errorf("template %q not found", name))
		c.String(500, "Template error")
		return
	}
	// Execute the page template which will use base
	if err := tmpl.ExecuteTemplate(c.Writer, name, data); err != nil {
		_ = c.Error(err)
		c.String(500, "Template error")
	}
}

func (h *WebHandler) Index(c *gin.Context) {
	// Redirect to projects page - configurator is accessed via /configurator?uuid=...
	c.Redirect(302, "/projects")
}

func (h *WebHandler) Configurator(c *gin.Context) {
	uuid := c.Query("uuid")
	categories, _ := h.localCategories()
	components, total, err := h.localDB.ListComponents(localdb.ComponentFilter{}, 0, 20)

	data := gin.H{
		"ActivePage": "configurator",
		"Categories": categories,
		"Components": []localComponentView{},
		"Total":      int64(0),
		"Page":       1,
		"PerPage":    20,
		"ConfigUUID": uuid,
	}

	if err == nil {
		data["Components"] = toLocalComponentViews(components)
		data["Total"] = total
	}

	h.render(c, "index.html", data)
}

func (h *WebHandler) Configs(c *gin.Context) {
	h.render(c, "configs.html", gin.H{"ActivePage": "configs"})
}

func (h *WebHandler) Projects(c *gin.Context) {
	h.render(c, "projects.html", gin.H{"ActivePage": "projects"})
}

func (h *WebHandler) ProjectDetail(c *gin.Context) {
	h.render(c, "project_detail.html", gin.H{
		"ActivePage":  "projects",
		"ProjectUUID": c.Param("uuid"),
	})
}

func (h *WebHandler) ConfigRevisions(c *gin.Context) {
	h.render(c, "config_revisions.html", gin.H{
		"ActivePage": "configs",
		"ConfigUUID": c.Param("uuid"),
	})
}

func (h *WebHandler) Pricelists(c *gin.Context) {
	h.render(c, "pricelists.html", gin.H{"ActivePage": "pricelists"})
}

func (h *WebHandler) PricelistDetail(c *gin.Context) {
	h.render(c, "pricelist_detail.html", gin.H{"ActivePage": "pricelists"})
}

func (h *WebHandler) PartnumberBooks(c *gin.Context) {
	h.render(c, "partnumber_books.html", gin.H{"ActivePage": "partnumber-books"})
}

// Partials for htmx

func (h *WebHandler) ComponentsPartial(c *gin.Context) {
	page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
	if page < 1 {
		page = 1
	}

	filter := localdb.ComponentFilter{
		Category: c.Query("category"),
		Search:   c.Query("search"),
	}
	if c.Query("has_price") == "true" {
		filter.HasPrice = true
	}
	offset := (page - 1) * 20

	data := gin.H{
		"Components": []localComponentView{},
		"Total":      int64(0),
		"Page":       page,
		"PerPage":    20,
	}

	components, total, err := h.localDB.ListComponents(filter, offset, 20)
	if err == nil {
		data["Components"] = toLocalComponentViews(components)
		data["Total"] = total
	}

	c.Header("Content-Type", "text/html; charset=utf-8")
	if tmpl, ok := h.templates["components_list.html"]; ok {
		tmpl.ExecuteTemplate(c.Writer, "components_list.html", data)
	}
}

type localComponentView struct {
	LotName      string
	Description  string
	Category     string
	CategoryName string
	Model        string
	CurrentPrice *float64
}

func toLocalComponentViews(items []localdb.LocalComponent) []localComponentView {
	result := make([]localComponentView, 0, len(items))
	for _, item := range items {
		result = append(result, localComponentView{
			LotName:      item.LotName,
			Description:  item.LotDescription,
			Category:     item.Category,
			CategoryName: item.Category,
			Model:        item.Model,
		})
	}
	return result
}

func (h *WebHandler) localCategories() ([]models.Category, error) {
	codes, err := h.localDB.GetLocalComponentCategories()
	if err != nil || len(codes) == 0 {
		return []models.Category{}, err
	}

	categories := make([]models.Category, 0, len(codes))
	for _, code := range codes {
		trimmed := strings.TrimSpace(code)
		if trimmed == "" {
			continue
		}
		categories = append(categories, models.Category{
			Code: trimmed,
			Name: trimmed,
		})
	}
	return categories, nil
}
