package handlers

import (
	"net/http"
	"sort"
	"strconv"
	"strings"

	"git.mchus.pro/mchus/quoteforge/internal/localdb"
	"git.mchus.pro/mchus/quoteforge/internal/models"
	"github.com/gin-gonic/gin"
)

type PricelistHandler struct {
	localDB *localdb.LocalDB
}

func NewPricelistHandler(localDB *localdb.LocalDB) *PricelistHandler {
	return &PricelistHandler{localDB: localDB}
}

// List returns all pricelists with pagination.
func (h *PricelistHandler) List(c *gin.Context) {
	page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
	perPage, _ := strconv.Atoi(c.DefaultQuery("per_page", "20"))
	if page < 1 {
		page = 1
	}
	if perPage < 1 {
		perPage = 20
	}
	source := c.Query("source")
	activeOnly := c.DefaultQuery("active_only", "false") == "true"

	localPLs, err := h.localDB.GetLocalPricelists()
	if err != nil {
		RespondError(c, http.StatusInternalServerError, "internal server error", err)
		return
	}
	if source != "" {
		filtered := localPLs[:0]
		for _, lpl := range localPLs {
			if strings.EqualFold(lpl.Source, source) {
				filtered = append(filtered, lpl)
			}
		}
		localPLs = filtered
	}
	type pricelistWithCount struct {
		pricelist  localdb.LocalPricelist
		itemCount  int64
		usageCount int
	}
	withCounts := make([]pricelistWithCount, 0, len(localPLs))
	for _, lpl := range localPLs {
		itemCount := h.localDB.CountLocalPricelistItems(lpl.ID)
		if activeOnly && itemCount == 0 {
			continue
		}
		usageCount := 0
		if lpl.IsUsed {
			usageCount = 1
		}
		withCounts = append(withCounts, pricelistWithCount{
			pricelist:  lpl,
			itemCount:  itemCount,
			usageCount: usageCount,
		})
	}
	localPLs = localPLs[:0]
	for _, row := range withCounts {
		localPLs = append(localPLs, row.pricelist)
	}
	sort.SliceStable(localPLs, func(i, j int) bool { return localPLs[i].CreatedAt.After(localPLs[j].CreatedAt) })
	total := len(localPLs)
	start := (page - 1) * perPage
	if start > total {
		start = total
	}
	end := start + perPage
	if end > total {
		end = total
	}
	pageSlice := localPLs[start:end]
	summaries := make([]map[string]interface{}, 0, len(pageSlice))
	for _, lpl := range pageSlice {
		itemCount := int64(0)
		usageCount := 0
		for _, row := range withCounts {
			if row.pricelist.ID == lpl.ID {
				itemCount = row.itemCount
				usageCount = row.usageCount
				break
			}
		}
		summaries = append(summaries, map[string]interface{}{
			"id":          lpl.ServerID,
			"source":      lpl.Source,
			"version":     lpl.Version,
			"created_by":  "sync",
			"item_count":  itemCount,
			"usage_count": usageCount,
			"is_active":   true,
			"created_at":  lpl.CreatedAt,
			"synced_from": "local",
		})
	}

	totalPages := (total + perPage - 1) / perPage
	if totalPages < 1 {
		totalPages = 1
	}
	c.JSON(http.StatusOK, gin.H{
		"items":       summaries,
		"total_count": total,
		"page":        page,
		"per_page":    perPage,
		"total_pages": totalPages,
	})
}

// Get returns a single pricelist by ID.
func (h *PricelistHandler) Get(c *gin.Context) {
	idStr := c.Param("id")
	id, err := strconv.ParseUint(idStr, 10, 32)
	if err != nil {
		c.JSON(http.StatusUnprocessableEntity, gin.H{"error": "invalid pricelist ID"})
		return
	}

	localPL, err := h.localDB.GetLocalPricelistByServerID(uint(id))
	if err != nil {
		c.JSON(http.StatusNotFound, gin.H{"error": "pricelist not found"})
		return
	}

	c.JSON(http.StatusOK, gin.H{
		"id":          localPL.ServerID,
		"source":      localPL.Source,
		"version":     localPL.Version,
		"created_by":  "sync",
		"item_count":  h.localDB.CountLocalPricelistItems(localPL.ID),
		"is_active":   true,
		"created_at":  localPL.CreatedAt,
		"synced_from": "local",
	})
}

// GetItems returns items for a pricelist with pagination.
func (h *PricelistHandler) GetItems(c *gin.Context) {
	idStr := c.Param("id")
	id, err := strconv.ParseUint(idStr, 10, 32)
	if err != nil {
		c.JSON(http.StatusUnprocessableEntity, gin.H{"error": "invalid pricelist ID"})
		return
	}

	page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
	perPage, _ := strconv.Atoi(c.DefaultQuery("per_page", "50"))
	search := c.Query("search")

	localPL, err := h.localDB.GetLocalPricelistByServerID(uint(id))
	if err != nil {
		c.JSON(http.StatusNotFound, gin.H{"error": "pricelist not found"})
		return
	}
	if page < 1 {
		page = 1
	}
	if perPage < 1 {
		perPage = 50
	}

	items, total, err := h.localDB.GetLocalPricelistItemsPage(localPL.ID, strings.TrimSpace(search), page, perPage)
	if err != nil {
		RespondError(c, http.StatusInternalServerError, "internal server error", err)
		return
	}

	lotNames := make([]string, len(items))
	for i, item := range items {
		lotNames[i] = item.LotName
	}
	descMap, err := h.localDB.GetLocalComponentDescriptionsByLotNames(lotNames)
	if err != nil {
		RespondError(c, http.StatusInternalServerError, "internal server error", err)
		return
	}

	resultItems := make([]gin.H, 0, len(items))
	for _, item := range items {
		resultItems = append(resultItems, gin.H{
			"id":               item.ID,
			"lot_name":         item.LotName,
			"lot_description":  descMap[item.LotName],
			"price":            item.Price,
			"category":         item.LotCategory,
			"available_qty":    item.AvailableQty,
			"partnumbers":      []string(item.Partnumbers),
			"partnumber_qtys":  map[string]interface{}{},
			"competitor_names": []string{},
			"price_spread_pct": nil,
		})
	}

	totalPages := int((total + int64(perPage) - 1) / int64(perPage))
	c.JSON(http.StatusOK, gin.H{
		"source":      localPL.Source,
		"items":       resultItems,
		"total_count": total,
		"page":        page,
		"per_page":    perPage,
		"total_pages": totalPages,
	})
}

func (h *PricelistHandler) GetLotNames(c *gin.Context) {
	idStr := c.Param("id")
	id, err := strconv.ParseUint(idStr, 10, 32)
	if err != nil {
		c.JSON(http.StatusUnprocessableEntity, gin.H{"error": "invalid pricelist ID"})
		return
	}

	localPL, err := h.localDB.GetLocalPricelistByServerID(uint(id))
	if err != nil {
		c.JSON(http.StatusNotFound, gin.H{"error": "pricelist not found"})
		return
	}
	items, err := h.localDB.GetLocalPricelistItems(localPL.ID)
	if err != nil {
		RespondError(c, http.StatusInternalServerError, "internal server error", err)
		return
	}
	lotNames := make([]string, 0, len(items))
	for _, item := range items {
		lotNames = append(lotNames, item.LotName)
	}
	sort.Strings(lotNames)

	c.JSON(http.StatusOK, gin.H{
		"lot_names": lotNames,
		"total":     len(lotNames),
	})
}

// GetLatest returns the most recent active pricelist.
func (h *PricelistHandler) GetLatest(c *gin.Context) {
	source := c.DefaultQuery("source", string(models.PricelistSourceEstimate))
	source = string(models.NormalizePricelistSource(source))

	localPL, err := h.localDB.GetLatestLocalPricelistBySource(source)
	if err != nil {
		c.JSON(http.StatusNotFound, gin.H{"error": "no pricelists available"})
		return
	}
	c.JSON(http.StatusOK, gin.H{
		"id":          localPL.ServerID,
		"source":      localPL.Source,
		"version":     localPL.Version,
		"created_by":  "sync",
		"item_count":  h.localDB.CountLocalPricelistItems(localPL.ID),
		"is_active":   true,
		"created_at":  localPL.CreatedAt,
		"synced_from": "local",
	})
}
