package handlers

import (
	"encoding/json"
	"net/http"
	"strconv"

	"mgit.msbls.de/m/KanzlAI-mGMT/internal/auth"
	"mgit.msbls.de/m/KanzlAI-mGMT/internal/services"
)

type TemplateHandler struct {
	templates *services.TemplateService
	cases     *services.CaseService
	parties   *services.PartyService
	deadlines *services.DeadlineService
	tenants   *services.TenantService
}

func NewTemplateHandler(
	templates *services.TemplateService,
	cases *services.CaseService,
	parties *services.PartyService,
	deadlines *services.DeadlineService,
	tenants *services.TenantService,
) *TemplateHandler {
	return &TemplateHandler{
		templates: templates,
		cases:     cases,
		parties:   parties,
		deadlines: deadlines,
		tenants:   tenants,
	}
}

// List handles GET /api/templates
func (h *TemplateHandler) List(w http.ResponseWriter, r *http.Request) {
	tenantID, ok := auth.TenantFromContext(r.Context())
	if !ok {
		writeError(w, http.StatusForbidden, "missing tenant")
		return
	}

	q := r.URL.Query()
	limit, _ := strconv.Atoi(q.Get("limit"))
	offset, _ := strconv.Atoi(q.Get("offset"))
	limit, offset = clampPagination(limit, offset)

	filter := services.TemplateFilter{
		Category: q.Get("category"),
		Search:   q.Get("search"),
		Limit:    limit,
		Offset:   offset,
	}

	if filter.Search != "" {
		if msg := validateStringLength("search", filter.Search, maxSearchLen); msg != "" {
			writeError(w, http.StatusBadRequest, msg)
			return
		}
	}

	templates, total, err := h.templates.List(r.Context(), tenantID, filter)
	if err != nil {
		internalError(w, "failed to list templates", err)
		return
	}

	writeJSON(w, http.StatusOK, map[string]any{
		"data":  templates,
		"total": total,
	})
}

// Get handles GET /api/templates/{id}
func (h *TemplateHandler) Get(w http.ResponseWriter, r *http.Request) {
	tenantID, ok := auth.TenantFromContext(r.Context())
	if !ok {
		writeError(w, http.StatusForbidden, "missing tenant")
		return
	}

	templateID, err := parsePathUUID(r, "id")
	if err != nil {
		writeError(w, http.StatusBadRequest, "invalid template ID")
		return
	}

	t, err := h.templates.GetByID(r.Context(), tenantID, templateID)
	if err != nil {
		internalError(w, "failed to get template", err)
		return
	}
	if t == nil {
		writeError(w, http.StatusNotFound, "template not found")
		return
	}

	writeJSON(w, http.StatusOK, t)
}

// Create handles POST /api/templates
func (h *TemplateHandler) Create(w http.ResponseWriter, r *http.Request) {
	tenantID, ok := auth.TenantFromContext(r.Context())
	if !ok {
		writeError(w, http.StatusForbidden, "missing tenant")
		return
	}

	var raw struct {
		Name        string  `json:"name"`
		Description *string `json:"description,omitempty"`
		Category    string  `json:"category"`
		Content     string  `json:"content"`
		Variables   any     `json:"variables,omitempty"`
	}
	if err := json.NewDecoder(r.Body).Decode(&raw); err != nil {
		writeError(w, http.StatusBadRequest, "invalid request body")
		return
	}

	if raw.Name == "" {
		writeError(w, http.StatusBadRequest, "name is required")
		return
	}
	if msg := validateStringLength("name", raw.Name, maxTitleLen); msg != "" {
		writeError(w, http.StatusBadRequest, msg)
		return
	}
	if raw.Category == "" {
		writeError(w, http.StatusBadRequest, "category is required")
		return
	}

	var variables []byte
	if raw.Variables != nil {
		var err error
		variables, err = json.Marshal(raw.Variables)
		if err != nil {
			writeError(w, http.StatusBadRequest, "invalid variables")
			return
		}
	}

	input := services.CreateTemplateInput{
		Name:        raw.Name,
		Description: raw.Description,
		Category:    raw.Category,
		Content:     raw.Content,
		Variables:   variables,
	}

	t, err := h.templates.Create(r.Context(), tenantID, input)
	if err != nil {
		writeError(w, http.StatusBadRequest, err.Error())
		return
	}

	writeJSON(w, http.StatusCreated, t)
}

// Update handles PUT /api/templates/{id}
func (h *TemplateHandler) Update(w http.ResponseWriter, r *http.Request) {
	tenantID, ok := auth.TenantFromContext(r.Context())
	if !ok {
		writeError(w, http.StatusForbidden, "missing tenant")
		return
	}

	templateID, err := parsePathUUID(r, "id")
	if err != nil {
		writeError(w, http.StatusBadRequest, "invalid template ID")
		return
	}

	var raw struct {
		Name        *string `json:"name,omitempty"`
		Description *string `json:"description,omitempty"`
		Category    *string `json:"category,omitempty"`
		Content     *string `json:"content,omitempty"`
		Variables   any     `json:"variables,omitempty"`
	}
	if err := json.NewDecoder(r.Body).Decode(&raw); err != nil {
		writeError(w, http.StatusBadRequest, "invalid request body")
		return
	}

	if raw.Name != nil {
		if msg := validateStringLength("name", *raw.Name, maxTitleLen); msg != "" {
			writeError(w, http.StatusBadRequest, msg)
			return
		}
	}

	var variables []byte
	if raw.Variables != nil {
		variables, err = json.Marshal(raw.Variables)
		if err != nil {
			writeError(w, http.StatusBadRequest, "invalid variables")
			return
		}
	}

	input := services.UpdateTemplateInput{
		Name:        raw.Name,
		Description: raw.Description,
		Category:    raw.Category,
		Content:     raw.Content,
		Variables:   variables,
	}

	t, err := h.templates.Update(r.Context(), tenantID, templateID, input)
	if err != nil {
		writeError(w, http.StatusBadRequest, err.Error())
		return
	}
	if t == nil {
		writeError(w, http.StatusNotFound, "template not found")
		return
	}

	writeJSON(w, http.StatusOK, t)
}

// Delete handles DELETE /api/templates/{id}
func (h *TemplateHandler) Delete(w http.ResponseWriter, r *http.Request) {
	tenantID, ok := auth.TenantFromContext(r.Context())
	if !ok {
		writeError(w, http.StatusForbidden, "missing tenant")
		return
	}

	templateID, err := parsePathUUID(r, "id")
	if err != nil {
		writeError(w, http.StatusBadRequest, "invalid template ID")
		return
	}

	if err := h.templates.Delete(r.Context(), tenantID, templateID); err != nil {
		writeError(w, http.StatusBadRequest, err.Error())
		return
	}

	writeJSON(w, http.StatusOK, map[string]string{"status": "deleted"})
}

// Render handles POST /api/templates/{id}/render?case_id=X
func (h *TemplateHandler) Render(w http.ResponseWriter, r *http.Request) {
	tenantID, ok := auth.TenantFromContext(r.Context())
	if !ok {
		writeError(w, http.StatusForbidden, "missing tenant")
		return
	}

	userID, _ := auth.UserFromContext(r.Context())

	templateID, err := parsePathUUID(r, "id")
	if err != nil {
		writeError(w, http.StatusBadRequest, "invalid template ID")
		return
	}

	// Get template
	tmpl, err := h.templates.GetByID(r.Context(), tenantID, templateID)
	if err != nil {
		internalError(w, "failed to get template", err)
		return
	}
	if tmpl == nil {
		writeError(w, http.StatusNotFound, "template not found")
		return
	}

	// Build render data
	data := services.RenderData{}

	// Case data (optional)
	caseIDStr := r.URL.Query().Get("case_id")
	if caseIDStr != "" {
		caseID, err := parseUUID(caseIDStr)
		if err != nil {
			writeError(w, http.StatusBadRequest, "invalid case_id")
			return
		}

		caseDetail, err := h.cases.GetByID(r.Context(), tenantID, caseID)
		if err != nil {
			internalError(w, "failed to get case", err)
			return
		}
		if caseDetail == nil {
			writeError(w, http.StatusNotFound, "case not found")
			return
		}
		data.Case = &caseDetail.Case
		data.Parties = caseDetail.Parties

		// Get next upcoming deadline for this case
		deadlines, err := h.deadlines.ListForCase(tenantID, caseID)
		if err == nil && len(deadlines) > 0 {
			// Find next non-completed deadline
			for i := range deadlines {
				if deadlines[i].Status != "completed" {
					data.Deadline = &deadlines[i]
					break
				}
			}
		}
	}

	// Tenant data
	tenant, err := h.tenants.GetByID(r.Context(), tenantID)
	if err == nil && tenant != nil {
		data.Tenant = tenant
	}

	// User data (userID from context — detailed name/email would need a user table lookup)
	data.UserName = userID.String()
	data.UserEmail = ""

	rendered := h.templates.Render(tmpl, data)

	writeJSON(w, http.StatusOK, map[string]any{
		"content":     rendered,
		"template_id": tmpl.ID,
		"name":        tmpl.Name,
	})
}
