package handler

import (
	"context"
	"database/sql"
	"encoding/json"
	"errors"
	"net/http"
	"strconv"
	"time"

	"github.com/gofrs/uuid/v5"

	"github.com/saveinmed/backend-go/internal/domain"
	"github.com/saveinmed/backend-go/internal/http/middleware"
	"github.com/saveinmed/backend-go/internal/usecase"
)

var _ = json.Marshal // dummy to keep import if needed elsewhere

type Handler struct {
	svc          *usecase.Service
	buyerFeeRate float64 // Rate to inflate prices for buyers (e.g., 0.12 = 12%)
}

func New(svc *usecase.Service, buyerFeeRate float64) *Handler {
	return &Handler{svc: svc, buyerFeeRate: buyerFeeRate}
}

// Register godoc
// @Summary Cadastro de usuário
// @Description Cria um usuário e opcionalmente uma empresa, retornando token JWT.
// @Tags Autenticação
// @Accept json
// @Produce json
// @Param payload body registerAuthRequest true "Dados do usuário e empresa"
// @Success 201 {object} authResponse
// @Failure 400 {object} map[string]string
// @Failure 500 {object} map[string]string
// @Router /api/v1/auth/register [post]
func (h *Handler) Register(w http.ResponseWriter, r *http.Request) {
	var req registerAuthRequest
	if err := decodeJSON(r.Context(), r, &req); err != nil {
		writeError(w, http.StatusBadRequest, err)
		return
	}

	var company *domain.Company
	if req.Company != nil {
		company = &domain.Company{
			ID:            req.Company.ID,
			Category:      req.Company.Category,
			CNPJ:          req.Company.CNPJ,
			CorporateName: req.Company.CorporateName,
			LicenseNumber: req.Company.LicenseNumber,
			Latitude:      req.Company.Latitude,
			Longitude:     req.Company.Longitude,
			City:          req.Company.City,
			State:         req.Company.State,
		}
	}

	// If company_name and cnpj are sent directly (from frontend), create company object
	if company == nil && req.CompanyName != "" && req.CNPJ != "" {
		company = &domain.Company{
			Category:      "farmacia",
			CNPJ:          req.CNPJ,
			CorporateName: req.CompanyName,
			LicenseNumber: "PENDING",
			IsVerified:    false,
			CreatedAt:     time.Now().UTC(),
			UpdatedAt:     time.Now().UTC(),
		}
	}

	var companyID uuid.UUID
	if req.CompanyID != nil {
		companyID = *req.CompanyID
	}

	user := &domain.User{
		CompanyID: companyID,
		Role:      req.Role,
		Name:      req.Name,
		Username:  req.Username,
		Email:     req.Email,
	}

	// Default role to Seller if not provided
	if user.Role == "" {
		user.Role = "Seller"
	}

	// If no company provided at all, create a placeholder one to satisfy DB constraints
	if user.CompanyID == uuid.Nil && company == nil {
		timestamp := time.Now().UnixNano()
		company = &domain.Company{
			// ID left as Nil so usecase creates it
			Category:      "farmacia",
			CNPJ:          "TMP-" + strconv.FormatInt(timestamp, 10), // Temporary CNPJ
			CorporateName: "Empresa de " + req.Name,
			LicenseNumber: "PENDING",
			IsVerified:    false,
			CreatedAt:     time.Now().UTC(),
			UpdatedAt:     time.Now().UTC(),
		}
	}

	if err := h.svc.RegisterAccount(r.Context(), company, user, req.Password); err != nil {
		writeError(w, http.StatusInternalServerError, err)
		return
	}

	token, exp, err := h.svc.Authenticate(r.Context(), user.Username, req.Password)
	if err != nil {
		writeError(w, http.StatusInternalServerError, err)
		return
	}

	writeJSON(w, http.StatusCreated, authResponse{Token: token, ExpiresAt: exp})
}

// GetMe godoc
// @Summary Obter dados do usuário logado
// @Tags Autenticação
// @Security BearerAuth
// @Produce json
// @Success 200 {object} domain.User
// @Failure 401 {object} map[string]string
// @Router /api/v1/auth/me [get]
func (h *Handler) GetMe(w http.ResponseWriter, r *http.Request) {
	requester, err := getRequester(r)
	if err != nil {
		writeError(w, http.StatusUnauthorized, err)
		return
	}

	user, err := h.svc.GetUser(r.Context(), requester.ID)
	if err != nil {
		writeError(w, http.StatusNotFound, err)
		return
	}

	var companyName string
	var isSuperAdmin bool

	if user.CompanyID != uuid.Nil {
		if c, err := h.svc.GetCompany(r.Context(), user.CompanyID); err == nil && c != nil {
			companyName = c.CorporateName
			if c.Category == "admin" {
				isSuperAdmin = true
			}
		}
	}

	response := struct {
		*domain.User
		CompanyName   string   `json:"company_name"`
		SuperAdmin    bool     `json:"superadmin"`
		EmpresasDados []string `json:"empresasDados"` // Frontend expects this array
	}{
		User:          user,
		CompanyName:   companyName,
		SuperAdmin:    isSuperAdmin,
		EmpresasDados: []string{user.CompanyID.String()},
	}

	writeJSON(w, http.StatusOK, response)
}

// RegisterCustomer godoc
// @Summary Cadastro de cliente
// @Description Cria um usuário do tipo cliente e opcionalmente uma empresa, retornando token JWT.
// @Tags Autenticação
// @Accept json
// @Produce json
// @Param payload body registerAuthRequest true "Dados do usuário e empresa"
// @Success 201 {object} authResponse
// @Failure 400 {object} map[string]string
// @Failure 500 {object} map[string]string
// @Router /api/v1/auth/register/customer [post]
func (h *Handler) RegisterCustomer(w http.ResponseWriter, r *http.Request) {
	var req registerAuthRequest
	if err := decodeJSON(r.Context(), r, &req); err != nil {
		writeError(w, http.StatusBadRequest, err)
		return
	}

	req.Role = "Customer"
	h.registerWithPayload(w, r, req)
}

// RegisterTenant godoc
// @Summary Cadastro de tenant
// @Description Cria um usuário do tipo tenant e opcionalmente uma empresa, retornando token JWT.
// @Tags Autenticação
// @Accept json
// @Produce json
// @Param payload body registerAuthRequest true "Dados do usuário e empresa"
// @Success 201 {object} authResponse
// @Failure 400 {object} map[string]string
// @Failure 500 {object} map[string]string
// @Router /api/v1/auth/register/tenant [post]
func (h *Handler) RegisterTenant(w http.ResponseWriter, r *http.Request) {
	var req registerAuthRequest
	if err := decodeJSON(r.Context(), r, &req); err != nil {
		writeError(w, http.StatusBadRequest, err)
		return
	}

	req.Role = "Seller"
	h.registerWithPayload(w, r, req)
}

// Logout godoc
// @Summary Logout
// @Description Endpoint para logout (invalidação client-side).
// @Tags Autenticação
// @Success 204 {string} string "No Content"
// @Router /api/v1/auth/logout [post]
func (h *Handler) Logout(w http.ResponseWriter, _ *http.Request) {
	w.WriteHeader(http.StatusNoContent)
}

// ForgotPassword godoc
// @Summary Solicitar redefinição de senha
// @Description Gera um token de redefinição de senha.
// @Tags Autenticação
// @Accept json
// @Produce json
// @Param payload body forgotPasswordRequest true "Email do usuário"
// @Success 202 {object} resetTokenResponse
// @Failure 400 {object} map[string]string
// @Router /api/v1/auth/password/forgot [post]
func (h *Handler) ForgotPassword(w http.ResponseWriter, r *http.Request) {
	var req forgotPasswordRequest
	if err := decodeJSON(r.Context(), r, &req); err != nil {
		writeError(w, http.StatusBadRequest, err)
		return
	}
	if req.Email == "" {
		writeError(w, http.StatusBadRequest, errors.New("email is required"))
		return
	}

	token, exp, err := h.svc.CreatePasswordResetToken(r.Context(), req.Email)
	if err != nil {
		if errors.Is(err, sql.ErrNoRows) {
			writeJSON(w, http.StatusAccepted, resetTokenResponse{
				Message: "Se existir uma conta, enviaremos instruções de redefinição.",
			})
			return
		}
		writeError(w, http.StatusInternalServerError, err)
		return
	}

	writeJSON(w, http.StatusAccepted, resetTokenResponse{
		Message:    "Token de redefinição gerado.",
		ResetToken: token,
		ExpiresAt:  &exp,
	})
}

// ResetPassword godoc
// @Summary Redefinir senha
// @Description Atualiza a senha usando o token de redefinição.
// @Tags Autenticação
// @Accept json
// @Produce json
// @Param payload body resetPasswordRequest true "Token e nova senha"
// @Success 200 {object} messageResponse
// @Failure 400 {object} map[string]string
// @Failure 401 {object} map[string]string
// @Router /api/v1/auth/password/reset [post]
func (h *Handler) ResetPassword(w http.ResponseWriter, r *http.Request) {
	var req resetPasswordRequest
	if err := decodeJSON(r.Context(), r, &req); err != nil {
		writeError(w, http.StatusBadRequest, err)
		return
	}
	if req.Token == "" || req.Password == "" {
		writeError(w, http.StatusBadRequest, errors.New("token and password are required"))
		return
	}

	if err := h.svc.ResetPassword(r.Context(), req.Token, req.Password); err != nil {
		writeError(w, http.StatusUnauthorized, err)
		return
	}

	writeJSON(w, http.StatusOK, messageResponse{Message: "Senha atualizada com sucesso."})
}

// VerifyEmail godoc
// @Summary Verificar email
// @Description Marca o email como verificado usando um token JWT.
// @Tags Autenticação
// @Accept json
// @Produce json
// @Param payload body verifyEmailRequest true "Token de verificação"
// @Success 200 {object} messageResponse
// @Failure 400 {object} map[string]string
// @Failure 401 {object} map[string]string
// @Router /api/v1/auth/verify-email [post]
func (h *Handler) VerifyEmail(w http.ResponseWriter, r *http.Request) {
	var req verifyEmailRequest
	if err := decodeJSON(r.Context(), r, &req); err != nil {
		writeError(w, http.StatusBadRequest, err)
		return
	}
	if req.Token == "" {
		writeError(w, http.StatusBadRequest, errors.New("token is required"))
		return
	}

	if _, err := h.svc.VerifyEmail(r.Context(), req.Token); err != nil {
		writeError(w, http.StatusUnauthorized, err)
		return
	}

	writeJSON(w, http.StatusOK, messageResponse{Message: "E-mail verificado com sucesso."})
}

func (h *Handler) registerWithPayload(w http.ResponseWriter, r *http.Request, req registerAuthRequest) {
	var company *domain.Company
	if req.Company != nil {
		company = &domain.Company{
			ID:            req.Company.ID,
			Category:      req.Company.Category,
			CNPJ:          req.Company.CNPJ,
			CorporateName: req.Company.CorporateName,
			LicenseNumber: req.Company.LicenseNumber,
			Latitude:      req.Company.Latitude,
			Longitude:     req.Company.Longitude,
			City:          req.Company.City,
			State:         req.Company.State,
		}
	}

	var companyID uuid.UUID
	if req.CompanyID != nil {
		companyID = *req.CompanyID
	}

	user := &domain.User{
		CompanyID: companyID,
		Role:      req.Role,
		Name:      req.Name,
		Email:     req.Email,
	}

	if user.CompanyID == uuid.Nil && company == nil {
		writeError(w, http.StatusBadRequest, errors.New("company_id or company payload is required"))
		return
	}

	if err := h.svc.RegisterAccount(r.Context(), company, user, req.Password); err != nil {
		writeError(w, http.StatusInternalServerError, err)
		return
	}

	token, exp, err := h.svc.Authenticate(r.Context(), user.Email, req.Password)
	if err != nil {
		writeError(w, http.StatusInternalServerError, err)
		return
	}

	writeJSON(w, http.StatusCreated, authResponse{Token: token, ExpiresAt: exp})
}

func (h *Handler) getUserFromContext(ctx context.Context) (*domain.User, error) {
	claims, ok := middleware.GetClaims(ctx)
	if !ok {
		return nil, errors.New("unauthorized")
	}
	var cid uuid.UUID
	if claims.CompanyID != nil {
		cid = *claims.CompanyID
	}
	return &domain.User{
		ID:        claims.UserID,
		Role:      claims.Role,
		CompanyID: cid,
	}, nil
}
