package finance

import (
	"fmt"
	"net/http"
	"photum-backend/internal/db/generated"
	"time"

	"github.com/gin-gonic/gin"
	"github.com/jackc/pgx/v5/pgtype"
)

type Handler struct {
	service *Service
}

func NewHandler(service *Service) *Handler {
	return &Handler{service: service}
}

// Request DTO
type TransactionRequest struct {
	FotID            *string `json:"fot_id"`
	DataCobranca     string  `json:"data_cobranca"` // YYYY-MM-DD
	TipoEvento       string  `json:"tipo_evento"`
	TipoServico      string  `json:"tipo_servico"`
	ProfessionalName string  `json:"professional_name"`
	Whatsapp         string  `json:"whatsapp"`
	Cpf              string  `json:"cpf"`
	TabelaFree       string  `json:"tabela_free"`
	ValorFree        float64 `json:"valor_free"`
	ValorExtra       float64 `json:"valor_extra"`
	DescricaoExtra   string  `json:"descricao_extra"`
	DataPagamento    *string `json:"data_pagamento"`
	PgtoOk           bool    `json:"pgto_ok"`
}

// Helper: Parse Date String to pgtype.Date
func parseDate(dateStr string) pgtype.Date {
	if dateStr == "" {
		return pgtype.Date{Valid: false}
	}
	t, err := time.Parse("2006-01-02", dateStr)
	if err != nil {
		return pgtype.Date{Valid: false}
	}
	return pgtype.Date{Time: t, Valid: true}
}

// Helper: Float to Numeric
func floatToNumeric(f float64) pgtype.Numeric {
	s := fmt.Sprintf("%.2f", f)
	var n pgtype.Numeric
	n.Scan(s)
	return n
}

// Helper: String UUID to pgtype.UUID
func parseUUID(uuidStr *string) pgtype.UUID {
	if uuidStr == nil || *uuidStr == "" {
		return pgtype.UUID{Valid: false}
	}
	var u pgtype.UUID
	err := u.Scan(*uuidStr)
	if err != nil {
		return pgtype.UUID{Valid: false}
	}
	return u
}

// Helper: String to pgtype.Text
func toText(s string) pgtype.Text {
	return pgtype.Text{String: s, Valid: s != ""}
}

func (h *Handler) Create(c *gin.Context) {
	var req TransactionRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	params := generated.CreateTransactionParams{
		FotID:            parseUUID(req.FotID),
		DataCobranca:     parseDate(req.DataCobranca),
		TipoEvento:       toText(req.TipoEvento),
		TipoServico:      toText(req.TipoServico),
		ProfessionalName: toText(req.ProfessionalName),
		Whatsapp:         toText(req.Whatsapp),
		Cpf:              toText(req.Cpf),
		TabelaFree:       toText(req.TabelaFree),
		ValorFree:        floatToNumeric(req.ValorFree),
		ValorExtra:       floatToNumeric(req.ValorExtra),
		DescricaoExtra:   toText(req.DescricaoExtra),
		TotalPagar:       floatToNumeric(req.ValorFree + req.ValorExtra),
		PgtoOk:           pgtype.Bool{Bool: req.PgtoOk, Valid: true},
	}

	if req.DataPagamento != nil {
		params.DataPagamento = parseDate(*req.DataPagamento)
	}

	regiao := c.GetString("regiao")
	txn, err := h.service.Create(c.Request.Context(), params, regiao)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	c.JSON(http.StatusCreated, txn)
}

func (h *Handler) Update(c *gin.Context) {
	idStr := c.Param("id")
	var idUUID pgtype.UUID
	if err := idUUID.Scan(idStr); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid ID"})
		return
	}

	var req TransactionRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	params := generated.UpdateTransactionParams{
		ID:               idUUID,
		FotID:            parseUUID(req.FotID),
		DataCobranca:     parseDate(req.DataCobranca),
		TipoEvento:       toText(req.TipoEvento),
		TipoServico:      toText(req.TipoServico),
		ProfessionalName: toText(req.ProfessionalName),
		Whatsapp:         toText(req.Whatsapp),
		Cpf:              toText(req.Cpf),
		TabelaFree:       toText(req.TabelaFree),
		ValorFree:        floatToNumeric(req.ValorFree),
		ValorExtra:       floatToNumeric(req.ValorExtra),
		DescricaoExtra:   toText(req.DescricaoExtra),
		TotalPagar:       floatToNumeric(req.ValorFree + req.ValorExtra),
		PgtoOk:           pgtype.Bool{Bool: req.PgtoOk, Valid: true},
	}

	if req.DataPagamento != nil {
		params.DataPagamento = parseDate(*req.DataPagamento)
	}

	regiao := c.GetString("regiao")
	txn, err := h.service.Update(c.Request.Context(), params, regiao)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	c.JSON(http.StatusOK, txn)
}

func (h *Handler) Delete(c *gin.Context) {
	idStr := c.Param("id")
	var idUUID pgtype.UUID
	if err := idUUID.Scan(idStr); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid ID"})
		return
	}

	regiao := c.GetString("regiao")
	if err := h.service.Delete(c.Request.Context(), idUUID, regiao); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	c.JSON(http.StatusOK, gin.H{"message": "deleted"})
}

func (h *Handler) List(c *gin.Context) {
	fotIDStr := c.Query("fot_id")
	if fotIDStr != "" {
		var fotUUID pgtype.UUID
		if err := fotUUID.Scan(fotIDStr); err != nil {
			c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid Fot ID"})
			return
		}
		regiao := c.GetString("regiao")
		list, err := h.service.ListByFot(c.Request.Context(), fotUUID, regiao)
		if err != nil {
			c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
			return
		}
		c.JSON(http.StatusOK, list)
		return
	}

	// Pagination
	page := 1
	limit := 50
	if p := c.Query("page"); p != "" {
		fmt.Sscanf(p, "%d", &page)
	}
	if l := c.Query("limit"); l != "" {
		fmt.Sscanf(l, "%d", &limit)
	}

	// Filters
	fot := c.Query("fot")
	data := c.Query("data")
	evento := c.Query("evento")
	servico := c.Query("servico")
	nome := c.Query("nome")
	curso := c.Query("curso")
	instituicao := c.Query("instituicao")
	ano := c.Query("ano")
	empresa := c.Query("empresa")
	startDate := c.Query("startDate")
	endDate := c.Query("endDate")
	includeWeekendsStr := c.Query("includeWeekends")
	includeWeekends := true
	if includeWeekendsStr == "false" {
		includeWeekends = false
	}

	regiao := c.GetString("regiao")
	list, count, err := h.service.ListPaginated(c.Request.Context(), int32(page), int32(limit), FilterParams{
		Fot:             fot,
		Data:            data,
		Evento:          evento,
		Servico:         servico,
		Nome:            nome,
		Curso:           curso,
		Instituicao:     instituicao,
		Ano:             ano,
		Empresa:         empresa,
		StartDate:       startDate,
		EndDate:         endDate,
		IncludeWeekends: includeWeekends,
	}, regiao)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}
	c.JSON(http.StatusOK, gin.H{
		"data":  list,
		"total": count,
		"page":  page,
		"limit": limit,
	})
}

func (h *Handler) AutoFill(c *gin.Context) {
	fotNumStr := c.Query("fot")
	if fotNumStr == "" {
		c.JSON(http.StatusBadRequest, gin.H{"error": "FOT Number required"})
		return
	}

	regiao := c.GetString("regiao")
	fotData, err := h.service.AutoFillSearch(c.Request.Context(), fotNumStr, regiao)
	if err != nil {
		c.JSON(http.StatusNotFound, gin.H{"error": "FOT not found"})
		return
	}

	c.JSON(http.StatusOK, fotData)
}

func (h *Handler) GetFotEvents(c *gin.Context) {
	fotNumStr := c.Query("fot_id") // Accepts UUID string currently, or we can look up by number if needed.
	// User has UUID from AutoFill
	var fotUUID pgtype.UUID
	if err := fotUUID.Scan(fotNumStr); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid FOT ID"})
		return
	}

	regiao := c.GetString("regiao")
	events, err := h.service.ListFotEvents(c.Request.Context(), fotUUID, regiao)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}
	c.JSON(http.StatusOK, events)
}

func (h *Handler) SearchProfessionals(c *gin.Context) {
	query := c.Query("q") // can be empty if function is provided potentially? No, search usually needs text.
	functionName := c.Query("function")

	// If function is provided, we might want to list all if query is empty?
	// For now let's enforce query length if function is missing, or allow empty query if function is present.

	if query == "" && functionName == "" {
		c.JSON(http.StatusOK, []interface{}{})
		return
	}

	regiao := c.GetString("regiao")
	if functionName != "" {
		pros, err := h.service.SearchProfessionalsByFunction(c.Request.Context(), query, functionName, regiao)
		if err != nil {
			c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
			return
		}
		c.JSON(http.StatusOK, pros)
		return
	}

	pros, err := h.service.SearchProfessionals(c.Request.Context(), query, regiao)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}
	c.JSON(http.StatusOK, pros)
}

func (h *Handler) GetPrice(c *gin.Context) {
	eventName := c.Query("event")
	serviceName := c.Query("service")

	if eventName == "" || serviceName == "" {
		c.JSON(http.StatusBadRequest, gin.H{"error": "Missing event or service parameters"})
		return
	}

	regiao := c.GetString("regiao")
	price, err := h.service.GetStandardPrice(c.Request.Context(), eventName, serviceName, regiao)
	if err != nil {
		// likely not found, return 0 or 404
		c.JSON(http.StatusOK, gin.H{"valor": 0})
		return
	}

	val, _ := price.Float64Value()
	c.JSON(http.StatusOK, gin.H{"valor": val.Float64})
}

func (h *Handler) SearchFot(c *gin.Context) {
	query := c.Query("q")
	if query == "" {
		c.JSON(http.StatusOK, []interface{}{})
		return
	}

	regiao := c.GetString("regiao")
	results, err := h.service.SearchFot(c.Request.Context(), query, regiao)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}
	c.JSON(http.StatusOK, results)
}

func (h *Handler) Import(c *gin.Context) {
	var items []ImportFinanceItem
	if err := c.ShouldBindJSON(&items); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid JSON: " + err.Error()})
		return
	}

	regiao := c.GetString("regiao")
	result, err := h.service.Import(c.Request.Context(), items, regiao)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	c.JSON(http.StatusOK, result)
}
