package apiv1

import (
	"encoding/json"
	"net/http"
	"strconv"

	"github.com/go-chi/chi/v5"

	"leadb2b.org/internal/api"
	"leadb2b.org/internal/auth"
	"leadb2b.org/internal/render"
)

type Handler struct {
	product *api.ProductService
	authAPI *api.AuthService
}

func New(product *api.ProductService, authAPI *api.AuthService) *Handler {
	return &Handler{product: product, authAPI: authAPI}
}

func (h *Handler) Me(w http.ResponseWriter, r *http.Request) {
	sess := auth.SessionFromContext(r.Context())
	writeJSON(w, sess.Me)
}

func (h *Handler) SearchCompanies(w http.ResponseWriter, r *http.Request) {
	sess := auth.SessionFromContext(r.Context())
	params := render.ParseSearchForm(r.URL.Query())
	params = render.ApplySearchDefaults(params)
	if lim := sess.Limits(); lim != nil && lim.MaxSearchPageSize > 0 {
		if params.PageSize > lim.MaxSearchPageSize || params.PageSize == 0 {
			params.PageSize = lim.MaxSearchPageSize
		}
	} else if params.PageSize == 0 {
		params.PageSize = 20
	}
	result, err := h.product.SearchCompanies(r.Context(), sess.Token, render.ToProductSearchQuery(params))
	if err != nil {
		writeAPIError(w, err)
		return
	}
	writeJSON(w, result)
}

func (h *Handler) WorkLead(w http.ResponseWriter, r *http.Request) {
	sess := auth.SessionFromContext(r.Context())
	var body struct {
		CNPJ string `json:"cnpj"`
	}
	if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
		writeAPIError(w, &api.APIError{Code: "INVALID_REQUEST", Message: "JSON inválido", Status: 422})
		return
	}
	out, err := h.product.WorkLead(r.Context(), sess.Token, body.CNPJ)
	if err != nil {
		writeAPIError(w, err)
		return
	}
	w.WriteHeader(http.StatusCreated)
	writeJSON(w, out)
}

func (h *Handler) ListLeads(w http.ResponseWriter, r *http.Request) {
	sess := auth.SessionFromContext(r.Context())
	out, err := h.product.ListLeads(r.Context(), sess.Token, r.URL.Query().Get("stage"))
	if err != nil {
		writeAPIError(w, err)
		return
	}
	writeJSON(w, out)
}

func (h *Handler) GetLead(w http.ResponseWriter, r *http.Request) {
	sess := auth.SessionFromContext(r.Context())
	id, _ := strconv.ParseInt(chi.URLParam(r, "id"), 10, 64)
	out, err := h.product.GetLead(r.Context(), sess.Token, id)
	if err != nil {
		writeAPIError(w, err)
		return
	}
	writeJSON(w, out)
}

func (h *Handler) PatchLead(w http.ResponseWriter, r *http.Request) {
	sess := auth.SessionFromContext(r.Context())
	id, _ := strconv.ParseInt(chi.URLParam(r, "id"), 10, 64)
	var body json.RawMessage
	if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
		writeAPIError(w, &api.APIError{Code: "INVALID_REQUEST", Message: "JSON inválido", Status: 422})
		return
	}
	lead, err := h.product.UpdateLead(r.Context(), sess.Token, id, body)
	if err != nil {
		writeAPIError(w, err)
		return
	}
	writeJSON(w, lead)
}

func (h *Handler) GetLeadHistory(w http.ResponseWriter, r *http.Request) {
	sess := auth.SessionFromContext(r.Context())
	id, _ := strconv.ParseInt(chi.URLParam(r, "id"), 10, 64)
	out, err := h.product.GetLeadHistory(r.Context(), sess.Token, id)
	if err != nil {
		writeAPIError(w, err)
		return
	}
	writeJSON(w, out)
}

func (h *Handler) ListTags(w http.ResponseWriter, r *http.Request) {
	sess := auth.SessionFromContext(r.Context())
	items, err := h.product.ListTags(r.Context(), sess.Token)
	if err != nil {
		writeAPIError(w, err)
		return
	}
	writeJSON(w, map[string]any{"items": items})
}

func (h *Handler) CreateTag(w http.ResponseWriter, r *http.Request) {
	sess := auth.SessionFromContext(r.Context())
	var body api.TagCreate
	if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
		writeAPIError(w, &api.APIError{Code: "INVALID_REQUEST", Message: "JSON inválido", Status: 422})
		return
	}
	out, err := h.product.CreateTag(r.Context(), sess.Token, body)
	if err != nil {
		writeAPIError(w, err)
		return
	}
	w.WriteHeader(http.StatusCreated)
	writeJSON(w, out)
}

func (h *Handler) PatchTag(w http.ResponseWriter, r *http.Request) {
	sess := auth.SessionFromContext(r.Context())
	id, _ := strconv.ParseInt(chi.URLParam(r, "id"), 10, 64)
	var body api.TagUpdate
	if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
		writeAPIError(w, &api.APIError{Code: "INVALID_REQUEST", Message: "JSON inválido", Status: 422})
		return
	}
	out, err := h.product.UpdateTag(r.Context(), sess.Token, id, body)
	if err != nil {
		writeAPIError(w, err)
		return
	}
	writeJSON(w, out)
}

func (h *Handler) DeleteTag(w http.ResponseWriter, r *http.Request) {
	sess := auth.SessionFromContext(r.Context())
	id, _ := strconv.ParseInt(chi.URLParam(r, "id"), 10, 64)
	if err := h.product.DeleteTag(r.Context(), sess.Token, id); err != nil {
		writeAPIError(w, err)
		return
	}
	w.WriteHeader(http.StatusNoContent)
}

func (h *Handler) DeleteLead(w http.ResponseWriter, r *http.Request) {
	sess := auth.SessionFromContext(r.Context())
	id, _ := strconv.ParseInt(chi.URLParam(r, "id"), 10, 64)
	if err := h.product.DeleteLead(r.Context(), sess.Token, id); err != nil {
		writeAPIError(w, err)
		return
	}
	w.WriteHeader(http.StatusNoContent)
}

func (h *Handler) PatchStage(w http.ResponseWriter, r *http.Request) {
	sess := auth.SessionFromContext(r.Context())
	id, _ := strconv.ParseInt(chi.URLParam(r, "id"), 10, 64)
	var body struct {
		Stage string `json:"stage"`
	}
	if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
		writeAPIError(w, &api.APIError{Code: "INVALID_REQUEST", Message: "JSON inválido", Status: 422})
		return
	}
	lead, err := h.product.UpdateStage(r.Context(), sess.Token, id, body.Stage)
	if err != nil {
		writeAPIError(w, err)
		return
	}
	writeJSON(w, map[string]any{"ok": true, "lead": lead, "limits": sess.Limits()})
}

func (h *Handler) Kanban(w http.ResponseWriter, r *http.Request) {
	sess := auth.SessionFromContext(r.Context())
	out, err := h.product.Kanban(r.Context(), sess.Token)
	if err != nil {
		writeAPIError(w, err)
		return
	}
	writeJSON(w, out)
}

func (h *Handler) KanbanStats(w http.ResponseWriter, r *http.Request) {
	sess := auth.SessionFromContext(r.Context())
	out, err := h.product.Kanban(r.Context(), sess.Token)
	if err != nil {
		writeAPIError(w, err)
		return
	}
	stages := make(map[string]int, len(out.Columns))
	for stage, cards := range out.Columns {
		stages[stage] = len(cards)
	}
	writeJSON(w, map[string]any{"stages": stages, "meta": out.Meta, "limits": sess.Limits()})
}

func (h *Handler) StatsOverview(w http.ResponseWriter, r *http.Request) {
	sess := auth.SessionFromContext(r.Context())
	out, err := h.product.StatsOverview(r.Context(), sess.Token)
	if err != nil {
		writeAPIError(w, err)
		return
	}
	writeJSON(w, out)
}

func (h *Handler) ContactWhatsApp(w http.ResponseWriter, r *http.Request) {
	sess := auth.SessionFromContext(r.Context())
	id, _ := strconv.ParseInt(chi.URLParam(r, "id"), 10, 64)
	var body api.ContactWhatsAppRequest
	_ = json.NewDecoder(r.Body).Decode(&body)
	out, err := h.product.ContactWhatsApp(r.Context(), sess.Token, id, body)
	if err != nil {
		writeAPIError(w, err)
		return
	}
	writeJSON(w, out)
}

func (h *Handler) ContactEmail(w http.ResponseWriter, r *http.Request) {
	sess := auth.SessionFromContext(r.Context())
	id, _ := strconv.ParseInt(chi.URLParam(r, "id"), 10, 64)
	var body api.ContactEmailRequest
	_ = json.NewDecoder(r.Body).Decode(&body)
	out, err := h.product.ContactEmail(r.Context(), sess.Token, id, body)
	if err != nil {
		writeAPIError(w, err)
		return
	}
	writeJSON(w, out)
}

func (h *Handler) ListTemplates(w http.ResponseWriter, r *http.Request) {
	sess := auth.SessionFromContext(r.Context())
	items, err := h.product.ListTemplates(r.Context(), sess.Token)
	if err != nil {
		writeAPIError(w, err)
		return
	}
	writeJSON(w, map[string]any{"items": items})
}

func (h *Handler) CreateTemplate(w http.ResponseWriter, r *http.Request) {
	sess := auth.SessionFromContext(r.Context())
	var body api.TemplateCreate
	if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
		writeAPIError(w, &api.APIError{Code: "INVALID_REQUEST", Message: "JSON inválido", Status: 422})
		return
	}
	out, err := h.product.CreateTemplate(r.Context(), sess.Token, body)
	if err != nil {
		writeAPIError(w, err)
		return
	}
	w.WriteHeader(http.StatusCreated)
	writeJSON(w, out)
}

func (h *Handler) UpdateTemplate(w http.ResponseWriter, r *http.Request) {
	sess := auth.SessionFromContext(r.Context())
	id, _ := strconv.ParseInt(chi.URLParam(r, "id"), 10, 64)
	var body api.TemplateUpdate
	if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
		writeAPIError(w, &api.APIError{Code: "INVALID_REQUEST", Message: "JSON inválido", Status: 422})
		return
	}
	out, err := h.product.UpdateTemplate(r.Context(), sess.Token, id, body)
	if err != nil {
		writeAPIError(w, err)
		return
	}
	writeJSON(w, out)
}

func (h *Handler) DeleteTemplate(w http.ResponseWriter, r *http.Request) {
	sess := auth.SessionFromContext(r.Context())
	id, _ := strconv.ParseInt(chi.URLParam(r, "id"), 10, 64)
	if err := h.product.DeleteTemplate(r.Context(), sess.Token, id); err != nil {
		writeAPIError(w, err)
		return
	}
	writeJSON(w, map[string]any{"ok": true})
}

func (h *Handler) CreateExport(w http.ResponseWriter, r *http.Request) {
	sess := auth.SessionFromContext(r.Context())
	var body api.ExportCreateRequest
	if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
		writeAPIError(w, &api.APIError{Code: "INVALID_REQUEST", Message: "JSON inválido", Status: 422})
		return
	}
	out, err := h.product.CreateExport(r.Context(), sess.Token, body.LeadIDs)
	if err != nil {
		writeAPIError(w, err)
		return
	}
	w.WriteHeader(http.StatusCreated)
	writeJSON(w, out)
}

func (h *Handler) ListExports(w http.ResponseWriter, r *http.Request) {
	sess := auth.SessionFromContext(r.Context())
	out, err := h.product.ListExports(r.Context(), sess.Token)
	if err != nil {
		writeAPIError(w, err)
		return
	}
	writeJSON(w, out)
}

func (h *Handler) DownloadExport(w http.ResponseWriter, r *http.Request) {
	sess := auth.SessionFromContext(r.Context())
	jobID, _ := strconv.ParseInt(chi.URLParam(r, "id"), 10, 64)
	raw, ct, err := h.product.DownloadExport(r.Context(), sess.Token, jobID)
	if err != nil {
		writeAPIError(w, err)
		return
	}
	if ct == "" {
		ct = "text/csv; charset=utf-8"
	}
	w.Header().Set("Content-Type", ct)
	w.Header().Set("Content-Disposition", "attachment; filename=leads-export.csv")
	w.Write(raw)
}

func (h *Handler) AdminUsers(w http.ResponseWriter, r *http.Request) {
	sess := auth.SessionFromContext(r.Context())
	out, err := h.product.AdminListUsers(r.Context(), sess.Token, 100, 0)
	if err != nil {
		writeAPIError(w, err)
		return
	}
	writeJSON(w, out)
}

func (h *Handler) AdminPatchUserStatus(w http.ResponseWriter, r *http.Request) {
	sess := auth.SessionFromContext(r.Context())
	userID, _ := strconv.ParseInt(chi.URLParam(r, "id"), 10, 64)
	var body api.AdminStatusUpdate
	if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
		writeAPIError(w, &api.APIError{Code: "INVALID_REQUEST", Message: "JSON inválido", Status: 422})
		return
	}
	if err := h.product.AdminUpdateUserStatus(r.Context(), sess.Token, userID, body.Status); err != nil {
		writeAPIError(w, err)
		return
	}
	w.WriteHeader(http.StatusNoContent)
}

func (h *Handler) AdminPatchSubscription(w http.ResponseWriter, r *http.Request) {
	sess := auth.SessionFromContext(r.Context())
	userID, _ := strconv.ParseInt(chi.URLParam(r, "id"), 10, 64)
	var body api.AdminSubscriptionUpdate
	if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
		writeAPIError(w, &api.APIError{Code: "INVALID_REQUEST", Message: "JSON inválido", Status: 422})
		return
	}
	status := body.Status
	if status == "" {
		status = "active"
	}
	if err := h.product.AdminUpdateSubscription(r.Context(), sess.Token, userID, body.PlanSlug, status); err != nil {
		writeAPIError(w, err)
		return
	}
	w.WriteHeader(http.StatusNoContent)
}

func (h *Handler) AdminPlans(w http.ResponseWriter, r *http.Request) {
	sess := auth.SessionFromContext(r.Context())
	plans, err := h.product.AdminListPlans(r.Context(), sess.Token)
	if err != nil {
		writeAPIError(w, err)
		return
	}
	writeJSON(w, map[string]any{"items": plans})
}

func (h *Handler) AdminMetrics(w http.ResponseWriter, r *http.Request) {
	sess := auth.SessionFromContext(r.Context())
	out, err := h.product.AdminMetrics(r.Context(), sess.Token)
	if err != nil {
		writeAPIError(w, err)
		return
	}
	writeJSON(w, out)
}

func writeJSON(w http.ResponseWriter, v any) {
	w.Header().Set("Content-Type", "application/json")
	_ = json.NewEncoder(w).Encode(v)
}

func writeAPIError(w http.ResponseWriter, err error) {
	if apiErr, ok := err.(*api.APIError); ok {
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(apiErr.Status)
		payload := map[string]any{"error": map[string]string{"message": apiErr.Message}}
		if apiErr.Code != "" {
			payload["error"].(map[string]string)["code"] = apiErr.Code
		}
		_ = json.NewEncoder(w).Encode(payload)
		return
	}
	http.Error(w, err.Error(), 500)
}
