package services

import (
	"git.mchus.pro/mchus/quoteforge/internal/localdb"
	"git.mchus.pro/mchus/quoteforge/internal/repository"
	"math"
)

// ResolvedBOMRow is the result of resolving a single vendor BOM row.
type ResolvedBOMRow struct {
	localdb.VendorSpecItem
	// ResolutionSource already on VendorSpecItem: "book", "manual_suggestion", "unresolved"
}

// AggregatedLOT represents a LOT with its aggregated quantity from the BOM.
type AggregatedLOT struct {
	LotName  string
	Quantity int
}

// VendorSpecResolver resolves vendor BOM rows to LOT names using the active partnumber book.
type VendorSpecResolver struct {
	bookRepo *repository.PartnumberBookRepository
}

func NewVendorSpecResolver(bookRepo *repository.PartnumberBookRepository) *VendorSpecResolver {
	return &VendorSpecResolver{bookRepo: bookRepo}
}

// Resolve resolves each vendor spec item's lot name using the 3-step algorithm.
// It returns the resolved items. Manual lot suggestions from the input are preserved as pre-fill.
func (r *VendorSpecResolver) Resolve(items []localdb.VendorSpecItem) ([]localdb.VendorSpecItem, error) {
	// Step 1: Get the active book
	book, err := r.bookRepo.GetActiveBook()
	if err != nil {
		// No book available — mark all as unresolved
		for i := range items {
			if items[i].ResolvedLotName == "" {
				items[i].ResolutionSource = "unresolved"
			}
		}
		return items, nil
	}

	for i, item := range items {
		pn := item.VendorPartnumber

		// Step 1: Look up in active book
		matches, err := r.bookRepo.FindLotByPartnumber(book.ID, pn)
		if err == nil && len(matches) > 0 {
			items[i].LotMappings = make([]localdb.VendorSpecLotMapping, 0, len(matches[0].LotsJSON))
			for _, lot := range matches[0].LotsJSON {
				if lot.LotName == "" {
					continue
				}
				items[i].LotMappings = append(items[i].LotMappings, localdb.VendorSpecLotMapping{
					LotName:       lot.LotName,
					QuantityPerPN: lotQtyToInt(lot.Qty),
				})
			}
			if len(items[i].LotMappings) > 0 {
				items[i].ResolvedLotName = items[i].LotMappings[0].LotName
			}
			items[i].ResolutionSource = "book"
			continue
		}

		// Step 2: Pre-fill from manual_lot_suggestion if provided
		if item.ManualLotSuggestion != "" {
			items[i].ResolvedLotName = item.ManualLotSuggestion
			items[i].ResolutionSource = "manual_suggestion"
			continue
		}

		// Step 3: Unresolved
		items[i].ResolvedLotName = ""
		items[i].ResolutionSource = "unresolved"
	}

	return items, nil
}

// AggregateLOTs applies qty from the resolved PN composition stored in lots_json.
func AggregateLOTs(items []localdb.VendorSpecItem, book *localdb.LocalPartnumberBook, bookRepo *repository.PartnumberBookRepository) ([]AggregatedLOT, error) {
	lotTotals := make(map[string]int)

	if book != nil {
		for _, item := range items {
			if item.ResolvedLotName == "" {
				continue
			}
			lot := item.ResolvedLotName
			pn := item.VendorPartnumber

			matches, err := bookRepo.FindLotByPartnumber(book.ID, pn)
			if err != nil || len(matches) == 0 {
				lotTotals[lot] += item.Quantity
				continue
			}
			for _, m := range matches {
				for _, mappedLot := range m.LotsJSON {
					if mappedLot.LotName != lot {
						continue
					}
					lotTotals[lot] += item.Quantity * lotQtyToInt(mappedLot.Qty)
				}
			}
		}
	} else {
		// No book: all resolved rows contribute qty=1 per lot
		for _, item := range items {
			if item.ResolvedLotName != "" {
				lotTotals[item.ResolvedLotName] += item.Quantity
			}
		}
	}

	// Build aggregated list
	seen := make(map[string]bool)
	var result []AggregatedLOT
	for _, item := range items {
		lot := item.ResolvedLotName
		if lot == "" || seen[lot] {
			continue
		}
		seen[lot] = true
		qty := lotTotals[lot]
		if qty < 1 {
			qty = 1
		}
		result = append(result, AggregatedLOT{LotName: lot, Quantity: qty})
	}
	return result, nil
}

func lotQtyToInt(qty float64) int {
	if qty < 1 {
		return 1
	}
	return int(math.Round(qty))
}
