package profissionais

import (
	"net/http"

	"photum-backend/internal/db/generated"

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

type Handler struct {
	service *Service
}

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

// ProfissionalResponse struct for Swagger and JSON response
type ProfissionalResponse struct {
	ID                   string   `json:"id"`
	UsuarioID            string   `json:"usuario_id"`
	Nome                 string   `json:"nome"`
	FuncaoProfissional   string   `json:"funcao_profissional"` // Now returns name from join
	FuncaoProfissionalID string   `json:"funcao_profissional_id"`
	Endereco             *string  `json:"endereco"`
	Cidade               *string  `json:"cidade"`
	Uf                   *string  `json:"uf"`
	Whatsapp             *string  `json:"whatsapp"`
	CpfCnpjTitular       *string  `json:"cpf_cnpj_titular"`
	Banco                *string  `json:"banco"`
	Agencia              *string  `json:"agencia"`
	ContaPix             *string  `json:"conta_pix"`
	CarroDisponivel      *bool    `json:"carro_disponivel"`
	TemEstudio           *bool    `json:"tem_estudio"`
	QtdEstudio           *int     `json:"qtd_estudio"`
	TipoCartao           *string  `json:"tipo_cartao"`
	Observacao           *string  `json:"observacao"`
	QualTec              *int     `json:"qual_tec"`
	EducacaoSimpatia     *int     `json:"educacao_simpatia"`
	DesempenhoEvento     *int     `json:"desempenho_evento"`
	DispHorario          *int     `json:"disp_horario"`
	Media                *float64 `json:"media"`
	TabelaFree           *string  `json:"tabela_free"`
	ExtraPorEquipamento  *bool    `json:"extra_por_equipamento"`
	Equipamentos         *string  `json:"equipamentos"`
}

func toResponse(p interface{}) ProfissionalResponse {
	// Handle different types returned by queries (Create returns CadastroProfissionai, List/Get returns Row with join)
	// This is a bit hacky, ideally we'd have a unified model or separate response mappers.
	// For now, let's check type.

	switch v := p.(type) {
	case generated.CadastroProfissionai:
		return ProfissionalResponse{
			ID:                   uuid.UUID(v.ID.Bytes).String(),
			UsuarioID:            uuid.UUID(v.UsuarioID.Bytes).String(),
			Nome:                 v.Nome,
			FuncaoProfissionalID: uuid.UUID(v.FuncaoProfissionalID.Bytes).String(),
			// FuncaoProfissional name is not available in simple insert return without extra query or join
			FuncaoProfissional:  "",
			Endereco:            fromPgText(v.Endereco),
			Cidade:              fromPgText(v.Cidade),
			Uf:                  fromPgText(v.Uf),
			Whatsapp:            fromPgText(v.Whatsapp),
			CpfCnpjTitular:      fromPgText(v.CpfCnpjTitular),
			Banco:               fromPgText(v.Banco),
			Agencia:             fromPgText(v.Agencia),
			ContaPix:            fromPgText(v.ContaPix),
			CarroDisponivel:     fromPgBool(v.CarroDisponivel),
			TemEstudio:          fromPgBool(v.TemEstudio),
			QtdEstudio:          fromPgInt4(v.QtdEstudio),
			TipoCartao:          fromPgText(v.TipoCartao),
			Observacao:          fromPgText(v.Observacao),
			QualTec:             fromPgInt4(v.QualTec),
			EducacaoSimpatia:    fromPgInt4(v.EducacaoSimpatia),
			DesempenhoEvento:    fromPgInt4(v.DesempenhoEvento),
			DispHorario:         fromPgInt4(v.DispHorario),
			Media:               fromPgNumeric(v.Media),
			TabelaFree:          fromPgText(v.TabelaFree),
			ExtraPorEquipamento: fromPgBool(v.ExtraPorEquipamento),
			Equipamentos:        fromPgText(v.Equipamentos),
		}
	case generated.ListProfissionaisRow:
		return ProfissionalResponse{
			ID:                   uuid.UUID(v.ID.Bytes).String(),
			UsuarioID:            uuid.UUID(v.UsuarioID.Bytes).String(),
			Nome:                 v.Nome,
			FuncaoProfissionalID: uuid.UUID(v.FuncaoProfissionalID.Bytes).String(),
			FuncaoProfissional:   v.FuncaoNome.String, // From join
			Endereco:             fromPgText(v.Endereco),
			Cidade:               fromPgText(v.Cidade),
			Uf:                   fromPgText(v.Uf),
			Whatsapp:             fromPgText(v.Whatsapp),
			CpfCnpjTitular:       fromPgText(v.CpfCnpjTitular),
			Banco:                fromPgText(v.Banco),
			Agencia:              fromPgText(v.Agencia),
			ContaPix:             fromPgText(v.ContaPix),
			CarroDisponivel:      fromPgBool(v.CarroDisponivel),
			TemEstudio:           fromPgBool(v.TemEstudio),
			QtdEstudio:           fromPgInt4(v.QtdEstudio),
			TipoCartao:           fromPgText(v.TipoCartao),
			Observacao:           fromPgText(v.Observacao),
			QualTec:              fromPgInt4(v.QualTec),
			EducacaoSimpatia:     fromPgInt4(v.EducacaoSimpatia),
			DesempenhoEvento:     fromPgInt4(v.DesempenhoEvento),
			DispHorario:          fromPgInt4(v.DispHorario),
			Media:                fromPgNumeric(v.Media),
			TabelaFree:           fromPgText(v.TabelaFree),
			ExtraPorEquipamento:  fromPgBool(v.ExtraPorEquipamento),
			Equipamentos:         fromPgText(v.Equipamentos),
		}
	case generated.GetProfissionalByIDRow:
		return ProfissionalResponse{
			ID:                   uuid.UUID(v.ID.Bytes).String(),
			UsuarioID:            uuid.UUID(v.UsuarioID.Bytes).String(),
			Nome:                 v.Nome,
			FuncaoProfissionalID: uuid.UUID(v.FuncaoProfissionalID.Bytes).String(),
			FuncaoProfissional:   v.FuncaoNome.String, // From join
			Endereco:             fromPgText(v.Endereco),
			Cidade:               fromPgText(v.Cidade),
			Uf:                   fromPgText(v.Uf),
			Whatsapp:             fromPgText(v.Whatsapp),
			CpfCnpjTitular:       fromPgText(v.CpfCnpjTitular),
			Banco:                fromPgText(v.Banco),
			Agencia:              fromPgText(v.Agencia),
			ContaPix:             fromPgText(v.ContaPix),
			CarroDisponivel:      fromPgBool(v.CarroDisponivel),
			TemEstudio:           fromPgBool(v.TemEstudio),
			QtdEstudio:           fromPgInt4(v.QtdEstudio),
			TipoCartao:           fromPgText(v.TipoCartao),
			Observacao:           fromPgText(v.Observacao),
			QualTec:              fromPgInt4(v.QualTec),
			EducacaoSimpatia:     fromPgInt4(v.EducacaoSimpatia),
			DesempenhoEvento:     fromPgInt4(v.DesempenhoEvento),
			DispHorario:          fromPgInt4(v.DispHorario),
			Media:                fromPgNumeric(v.Media),
			TabelaFree:           fromPgText(v.TabelaFree),
			ExtraPorEquipamento:  fromPgBool(v.ExtraPorEquipamento),
			Equipamentos:         fromPgText(v.Equipamentos),
		}
	default:
		return ProfissionalResponse{}
	}
}

// Helpers for conversion
func fromPgText(t pgtype.Text) *string {
	if !t.Valid {
		return nil
	}
	return &t.String
}

func fromPgBool(b pgtype.Bool) *bool {
	if !b.Valid {
		return nil
	}
	return &b.Bool
}

func fromPgInt4(i pgtype.Int4) *int {
	if !i.Valid {
		return nil
	}
	val := int(i.Int32)
	return &val
}

func fromPgNumeric(n pgtype.Numeric) *float64 {
	if !n.Valid {
		return nil
	}
	f, _ := n.Float64Value()
	val := f.Float64
	return &val
}

// Create godoc
// @Summary      Create a new profissional
// @Description  Create a new profissional record
// @Tags         profissionais
// @Accept       json
// @Produce      json
// @Security     BearerAuth
// @Param        request body CreateProfissionalInput true "Create Profissional Request"
// @Success      201  {object}  ProfissionalResponse
// @Failure      400  {object}  map[string]string
// @Failure      500  {object}  map[string]string
// @Router       /api/profissionais [post]
func (h *Handler) Create(c *gin.Context) {
	var input CreateProfissionalInput
	if err := c.ShouldBindJSON(&input); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	userID, exists := c.Get("userID")
	if !exists {
		c.JSON(http.StatusUnauthorized, gin.H{"error": "user not authenticated"})
		return
	}

	userIDStr, ok := userID.(string)
	if !ok {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "invalid user id type in context"})
		return
	}

	prof, err := h.service.Create(c.Request.Context(), userIDStr, input)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	c.JSON(http.StatusCreated, toResponse(*prof))
}

// List godoc
// @Summary      List profissionais
// @Description  List all profissionais
// @Tags         profissionais
// @Accept       json
// @Produce      json
// @Security     BearerAuth
// @Success      200  {array}   ProfissionalResponse
// @Failure      500  {object}  map[string]string
// @Router       /api/profissionais [get]
func (h *Handler) List(c *gin.Context) {
	profs, err := h.service.List(c.Request.Context())
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	var response []ProfissionalResponse
	for _, p := range profs {
		response = append(response, toResponse(p))
	}

	c.JSON(http.StatusOK, response)
}

// Get godoc
// @Summary      Get profissional by ID
// @Description  Get a profissional by ID
// @Tags         profissionais
// @Accept       json
// @Produce      json
// @Security     BearerAuth
// @Param        id   path      string  true  "Profissional ID"
// @Success      200  {object}  ProfissionalResponse
// @Failure      400  {object}  map[string]string
// @Failure      404  {object}  map[string]string
// @Failure      500  {object}  map[string]string
// @Router       /api/profissionais/{id} [get]
func (h *Handler) Get(c *gin.Context) {
	id := c.Param("id")
	if id == "" {
		c.JSON(http.StatusBadRequest, gin.H{"error": "id required"})
		return
	}

	prof, err := h.service.GetByID(c.Request.Context(), id)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	c.JSON(http.StatusOK, toResponse(*prof))
}

// Update godoc
// @Summary      Update profissional
// @Description  Update a profissional by ID
// @Tags         profissionais
// @Accept       json
// @Produce      json
// @Security     BearerAuth
// @Param        id   path      string  true  "Profissional ID"
// @Param        request body UpdateProfissionalInput true "Update Profissional Request"
// @Success      200  {object}  ProfissionalResponse
// @Failure      400  {object}  map[string]string
// @Failure      500  {object}  map[string]string
// @Router       /api/profissionais/{id} [put]
func (h *Handler) Update(c *gin.Context) {
	id := c.Param("id")
	if id == "" {
		c.JSON(http.StatusBadRequest, gin.H{"error": "id required"})
		return
	}

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

	prof, err := h.service.Update(c.Request.Context(), id, input)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	c.JSON(http.StatusOK, toResponse(*prof))
}

// Delete godoc
// @Summary      Delete profissional
// @Description  Delete a profissional by ID
// @Tags         profissionais
// @Accept       json
// @Produce      json
// @Security     BearerAuth
// @Param        id   path      string  true  "Profissional ID"
// @Success      200  {object}  map[string]string
// @Failure      400  {object}  map[string]string
// @Failure      500  {object}  map[string]string
// @Router       /api/profissionais/{id} [delete]
func (h *Handler) Delete(c *gin.Context) {
	id := c.Param("id")
	if id == "" {
		c.JSON(http.StatusBadRequest, gin.H{"error": "id required"})
		return
	}

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

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