package services

import (
	"fmt"
	"log/slog"
	"strings"

	"git.mchus.pro/mchus/quoteforge/internal/models"
	"git.mchus.pro/mchus/quoteforge/internal/repository"
)

type ComponentService struct {
	componentRepo *repository.ComponentRepository
	categoryRepo  *repository.CategoryRepository
}

func NewComponentService(
	componentRepo *repository.ComponentRepository,
	categoryRepo *repository.CategoryRepository,
) *ComponentService {
	return &ComponentService{
		componentRepo: componentRepo,
		categoryRepo:  categoryRepo,
	}
}

// ParsePartNumber extracts category and model from lot_name
// "CPU_AMD_9654" → category="CPU", model="AMD_9654"
// "MB_INTEL_4.Sapphire_2S_32xDDR5" → category="MB", model="INTEL_4.Sapphire_2S_32xDDR5"
func ParsePartNumber(lotName string) (category, model string) {
	parts := strings.SplitN(lotName, "_", 2)
	if len(parts) >= 1 {
		category = parts[0]
	}
	if len(parts) >= 2 {
		model = parts[1]
	}
	return
}

type ComponentListResult struct {
	Items      []ComponentView `json:"items"`
	TotalCount int64           `json:"total_count"`
	Page       int             `json:"page"`
	PerPage    int             `json:"per_page"`
	TotalPages int             `json:"total_pages"`
}

type ComponentView struct {
	LotName         string                `json:"lot_name"`
	Description     string                `json:"description"`
	Category        string                `json:"category"`
	CategoryName    string                `json:"category_name"`
	Model           string                `json:"model"`
	PriceFreshness  models.PriceFreshness `json:"price_freshness"`
	PopularityScore float64               `json:"popularity_score"`
	Specs           models.Specs          `json:"specs,omitempty"`
}

func (s *ComponentService) List(filter repository.ComponentFilter, page, perPage int) (*ComponentListResult, error) {
	// If no database connection (offline mode), return empty list
	// Components should be loaded via /api/sync/components first
	if s.componentRepo == nil {
		return &ComponentListResult{
			Items:      []ComponentView{},
			TotalCount: 0,
			Page:       page,
			PerPage:    perPage,
			TotalPages: 1,
		}, nil
	}

	if page < 1 {
		page = 1
	}
	if perPage < 1 {
		perPage = 20
	}
	if perPage > 5000 {
		perPage = 5000
	}
	offset := (page - 1) * perPage

	components, total, err := s.componentRepo.List(filter, offset, perPage)
	if err != nil {
		return nil, err
	}

	views := make([]ComponentView, len(components))
	for i, c := range components {
		view := ComponentView{
			LotName:         c.LotName,
			Model:           c.Model,
			PriceFreshness:  c.GetPriceFreshness(30, 60, 90, 3),
			PopularityScore: c.PopularityScore,
			Specs:           c.Specs,
		}

		if c.Lot != nil {
			view.Description = c.Lot.LotDescription
		}
		if c.Category != nil {
			view.Category = c.Category.Code
			view.CategoryName = c.Category.Name
		}

		views[i] = view
	}

	totalPages := int((total + int64(perPage) - 1) / int64(perPage))
	if totalPages < 1 {
		totalPages = 1
	}
	return &ComponentListResult{
		Items:      views,
		TotalCount: total,
		Page:       page,
		PerPage:    perPage,
		TotalPages: totalPages,
	}, nil
}

func (s *ComponentService) GetByLotName(lotName string) (*ComponentView, error) {
	// If no database connection (offline mode), return error
	if s.componentRepo == nil {
		return nil, fmt.Errorf("offline mode: component data not available")
	}

	c, err := s.componentRepo.GetByLotName(lotName)
	if err != nil {
		return nil, err
	}

	// Track usage (best-effort)
	if err := s.componentRepo.IncrementRequestCount(lotName); err != nil {
		slog.Warn("component: could not increment request count", "lot", lotName, "err", err)
	}

	view := &ComponentView{
		LotName:         c.LotName,
		Model:           c.Model,
		PriceFreshness:  c.GetPriceFreshness(30, 60, 90, 3),
		PopularityScore: c.PopularityScore,
		Specs:           c.Specs,
	}

	if c.Lot != nil {
		view.Description = c.Lot.LotDescription
	}
	if c.Category != nil {
		view.Category = c.Category.Code
		view.CategoryName = c.Category.Name
	}

	return view, nil
}

func (s *ComponentService) GetCategories() ([]models.Category, error) {
	// If no database connection (offline mode), return default categories
	if s.categoryRepo == nil {
		return models.DefaultCategories, nil
	}
	return s.categoryRepo.GetAll()
}

// ImportFromLot creates metadata entries for lots that don't have them
func (s *ComponentService) ImportFromLot() (int, error) {
	// If no database connection (offline mode), return error
	if s.componentRepo == nil || s.categoryRepo == nil {
		return 0, fmt.Errorf("offline mode: import not available")
	}

	lots, err := s.componentRepo.GetLotsWithoutMetadata()
	if err != nil {
		return 0, err
	}

	categories, err := s.categoryRepo.GetAll()
	if err != nil {
		return 0, err
	}

	categoryMap := make(map[string]uint)
	for _, cat := range categories {
		categoryMap[strings.ToUpper(cat.Code)] = cat.ID
	}

	imported := 0
	for _, lot := range lots {
		// Use lot_category from database if available, otherwise parse from lot_name
		var category string
		if lot.LotCategory != nil && *lot.LotCategory != "" {
			category = strings.ToUpper(*lot.LotCategory)
		} else {
			category, _ = ParsePartNumber(lot.LotName)
			category = strings.ToUpper(category)
		}

		_, model := ParsePartNumber(lot.LotName)

		metadata := &models.LotMetadata{
			LotName: lot.LotName,
			Model:   model,
			Specs:   make(models.Specs),
		}

		if catID, ok := categoryMap[category]; ok {
			metadata.CategoryID = &catID
		} else {
			// Create new category if it doesn't exist
			newCat, err := s.categoryRepo.CreateIfNotExists(category)
			if err == nil && newCat != nil {
				metadata.CategoryID = &newCat.ID
			}
		}

		if err := s.componentRepo.Create(metadata); err != nil {
			continue
		}
		imported++
	}

	return imported, nil
}
