package handler

import (
	"errors"
	"net/http"
	"strconv"
	"time"

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

// CreateProduct godoc
// @Summary Cadastro de produto com rastreabilidade de lote
// @Tags Produtos
// @Accept json
// @Produce json
// @Param product body registerProductRequest true "Produto"
// @Success 201 {object} domain.Product
// @Router /api/v1/products [post]
func (h *Handler) CreateProduct(w http.ResponseWriter, r *http.Request) {
	var req registerProductRequest
	if err := decodeJSON(r.Context(), r, &req); err != nil {
		writeError(w, http.StatusBadRequest, err)
		return
	}

	product := &domain.Product{
		SellerID:    req.SellerID,
		Name:        req.Name,
		Description: req.Description,
		Batch:       req.Batch,
		ExpiresAt:   req.ExpiresAt,
		PriceCents:  req.PriceCents,
		Stock:       req.Stock,
	}

	if err := h.svc.RegisterProduct(r.Context(), product); err != nil {
		writeError(w, http.StatusInternalServerError, err)
		return
	}

	writeJSON(w, http.StatusCreated, product)
}

// ListProducts godoc
// @Summary Lista catálogo com lote e validade
// @Tags Produtos
// @Produce json
// @Success 200 {array} domain.Product
// @Router /api/v1/products [get]
func (h *Handler) ListProducts(w http.ResponseWriter, r *http.Request) {
	page, pageSize := parsePagination(r)
	filter := domain.ProductFilter{
		Search: r.URL.Query().Get("search"),
	}

	result, err := h.svc.ListProducts(r.Context(), filter, page, pageSize)
	if err != nil {
		writeError(w, http.StatusInternalServerError, err)
		return
	}
	writeJSON(w, http.StatusOK, result)
}

// SearchProducts godoc
// @Summary Busca avançada de produtos com filtros e distância
// @Description Retorna produtos ordenados por validade, com distância aproximada. Vendedor anônimo até checkout.
// @Tags Produtos
// @Produce json
// @Param search query string false "Termo de busca"
// @Param min_price query integer false "Preço mínimo em centavos"
// @Param max_price query integer false "Preço máximo em centavos"
// @Param max_distance query number false "Distância máxima em km"
// @Param lat query number true "Latitude do comprador"
// @Param lng query number true "Longitude do comprador"
// @Param page query integer false "Página"
// @Param page_size query integer false "Itens por página"
// @Success 200 {object} domain.ProductSearchPage
// @Router /api/v1/products/search [get]
func (h *Handler) SearchProducts(w http.ResponseWriter, r *http.Request) {
	page, pageSize := parsePagination(r)

	filter := domain.ProductSearchFilter{
		Search: r.URL.Query().Get("search"),
	}

	// Parse buyer location (required)
	latStr := r.URL.Query().Get("lat")
	lngStr := r.URL.Query().Get("lng")
	if latStr == "" || lngStr == "" {
		writeError(w, http.StatusBadRequest, errors.New("lat and lng query params are required"))
		return
	}
	lat, err := strconv.ParseFloat(latStr, 64)
	if err != nil {
		writeError(w, http.StatusBadRequest, errors.New("invalid lat value"))
		return
	}
	lng, err := strconv.ParseFloat(lngStr, 64)
	if err != nil {
		writeError(w, http.StatusBadRequest, errors.New("invalid lng value"))
		return
	}
	filter.BuyerLat = lat
	filter.BuyerLng = lng

	// Parse optional price filters
	if v := r.URL.Query().Get("min_price"); v != "" {
		if price, err := strconv.ParseInt(v, 10, 64); err == nil {
			filter.MinPriceCents = &price
		}
	}
	if v := r.URL.Query().Get("max_price"); v != "" {
		if price, err := strconv.ParseInt(v, 10, 64); err == nil {
			filter.MaxPriceCents = &price
		}
	}

	// Parse optional max distance
	if v := r.URL.Query().Get("max_distance"); v != "" {
		if dist, err := strconv.ParseFloat(v, 64); err == nil {
			filter.MaxDistanceKm = &dist
		}
	}

	// Parse optional expiration filter
	if v := r.URL.Query().Get("expires_before"); v != "" {
		if days, err := strconv.Atoi(v); err == nil && days > 0 {
			expires := time.Now().AddDate(0, 0, days)
			filter.ExpiresBefore = &expires
		}
	}

	// Exclude products from the buyer's own company
	if claims, ok := middleware.GetClaims(r.Context()); ok && claims.CompanyID != nil {
		filter.ExcludeSellerID = claims.CompanyID
	}

	result, err := h.svc.SearchProducts(r.Context(), filter, page, pageSize)
	if err != nil {
		writeError(w, http.StatusInternalServerError, err)
		return
	}
	writeJSON(w, http.StatusOK, result)
}

// GetProduct godoc
// @Summary Obter produto
// @Tags Produtos
// @Produce json
// @Param id path string true "Product ID"
// @Success 200 {object} domain.Product
// @Failure 404 {object} map[string]string
// @Router /api/v1/products/{id} [get]
func (h *Handler) GetProduct(w http.ResponseWriter, r *http.Request) {
	id, err := parseUUIDFromPath(r.URL.Path)
	if err != nil {
		writeError(w, http.StatusBadRequest, err)
		return
	}

	product, err := h.svc.GetProduct(r.Context(), id)
	if err != nil {
		writeError(w, http.StatusNotFound, err)
		return
	}

	writeJSON(w, http.StatusOK, product)
}

// UpdateProduct godoc
// @Summary Atualizar produto
// @Tags Produtos
// @Accept json
// @Produce json
// @Param id path string true "Product ID"
// @Param payload body updateProductRequest true "Campos para atualização"
// @Success 200 {object} domain.Product
// @Failure 400 {object} map[string]string
// @Failure 404 {object} map[string]string
// @Router /api/v1/products/{id} [patch]
func (h *Handler) UpdateProduct(w http.ResponseWriter, r *http.Request) {
	id, err := parseUUIDFromPath(r.URL.Path)
	if err != nil {
		writeError(w, http.StatusBadRequest, err)
		return
	}

	var req updateProductRequest
	if err := decodeJSON(r.Context(), r, &req); err != nil {
		writeError(w, http.StatusBadRequest, err)
		return
	}

	product, err := h.svc.GetProduct(r.Context(), id)
	if err != nil {
		writeError(w, http.StatusNotFound, err)
		return
	}

	if req.SellerID != nil {
		product.SellerID = *req.SellerID
	}
	if req.Name != nil {
		product.Name = *req.Name
	}
	if req.Description != nil {
		product.Description = *req.Description
	}
	if req.Batch != nil {
		product.Batch = *req.Batch
	}
	if req.ExpiresAt != nil {
		product.ExpiresAt = *req.ExpiresAt
	}
	if req.PriceCents != nil {
		product.PriceCents = *req.PriceCents
	}
	if req.Stock != nil {
		product.Stock = *req.Stock
	}

	if err := h.svc.UpdateProduct(r.Context(), product); err != nil {
		writeError(w, http.StatusInternalServerError, err)
		return
	}

	writeJSON(w, http.StatusOK, product)
}

// DeleteProduct godoc
// @Summary Remover produto
// @Tags Produtos
// @Param id path string true "Product ID"
// @Success 204 ""
// @Failure 400 {object} map[string]string
// @Failure 404 {object} map[string]string
// @Router /api/v1/products/{id} [delete]
func (h *Handler) DeleteProduct(w http.ResponseWriter, r *http.Request) {
	id, err := parseUUIDFromPath(r.URL.Path)
	if err != nil {
		writeError(w, http.StatusBadRequest, err)
		return
	}

	if err := h.svc.DeleteProduct(r.Context(), id); err != nil {
		writeError(w, http.StatusBadRequest, err)
		return
	}

	w.WriteHeader(http.StatusNoContent)
}

// ListInventory godoc
// @Summary Listar estoque
// @Tags Estoque
// @Security BearerAuth
// @Produce json
// @Param expires_in_days query int false "Dias para expiração"
// @Success 200 {array} domain.InventoryItem
// @Router /api/v1/inventory [get]
// ListInventory exposes stock with expiring batch filters.
func (h *Handler) ListInventory(w http.ResponseWriter, r *http.Request) {
	page, pageSize := parsePagination(r)
	var filter domain.InventoryFilter
	if days := r.URL.Query().Get("expires_in_days"); days != "" {
		n, err := strconv.Atoi(days)
		if err != nil || n < 0 {
			writeError(w, http.StatusBadRequest, errors.New("invalid expires_in_days"))
			return
		}
		expires := time.Now().Add(time.Duration(n) * 24 * time.Hour)
		filter.ExpiringBefore = &expires
	}

	result, err := h.svc.ListInventory(r.Context(), filter, page, pageSize)
	if err != nil {
		writeError(w, http.StatusInternalServerError, err)
		return
	}

	writeJSON(w, http.StatusOK, result)
}

// AdjustInventory godoc
// @Summary Ajustar estoque
// @Tags Estoque
// @Security BearerAuth
// @Accept json
// @Produce json
// @Param payload body inventoryAdjustRequest true "Ajuste de estoque"
// @Success 200 {object} domain.InventoryItem
// @Failure 400 {object} map[string]string
// @Router /api/v1/inventory/adjust [post]
// AdjustInventory handles manual stock corrections.
func (h *Handler) AdjustInventory(w http.ResponseWriter, r *http.Request) {
	var req inventoryAdjustRequest
	if err := decodeJSON(r.Context(), r, &req); err != nil {
		writeError(w, http.StatusBadRequest, err)
		return
	}

	if req.Delta == 0 {
		writeError(w, http.StatusBadRequest, errors.New("delta must be non-zero"))
		return
	}

	item, err := h.svc.AdjustInventory(r.Context(), req.ProductID, req.Delta, req.Reason)
	if err != nil {
		writeError(w, http.StatusBadRequest, err)
		return
	}

	writeJSON(w, http.StatusOK, item)
}
