package services

import (
	"testing"
	"time"

	"git.mchus.pro/mchus/quoteforge/internal/models"
	"git.mchus.pro/mchus/quoteforge/internal/repository"
	"github.com/glebarez/sqlite"
	"gorm.io/gorm"
)

func TestCalculatePriceLevels_WithMissingLevel(t *testing.T) {
	db := newPriceLevelsTestDB(t)
	repo := repository.NewPricelistRepository(db)
	service := NewQuoteService(nil, repo, nil, nil)

	estimate := seedPricelistWithItem(t, repo, "estimate", "CPU_X", 100)
	_ = estimate
	seedPricelistWithItem(t, repo, "warehouse", "CPU_X", 120)

	result, err := service.CalculatePriceLevels(&PriceLevelsRequest{
		Items: []struct {
			LotName  string `json:"lot_name"`
			Quantity int    `json:"quantity"`
		}{
			{LotName: "CPU_X", Quantity: 2},
		},
	})
	if err != nil {
		t.Fatalf("CalculatePriceLevels returned error: %v", err)
	}
	if len(result.Items) != 1 {
		t.Fatalf("expected 1 item, got %d", len(result.Items))
	}
	item := result.Items[0]
	if item.EstimatePrice == nil || *item.EstimatePrice != 100 {
		t.Fatalf("expected estimate 100, got %#v", item.EstimatePrice)
	}
	if item.WarehousePrice == nil || *item.WarehousePrice != 120 {
		t.Fatalf("expected warehouse 120, got %#v", item.WarehousePrice)
	}
	if item.CompetitorPrice != nil {
		t.Fatalf("expected competitor nil, got %#v", item.CompetitorPrice)
	}
	if len(item.PriceMissing) != 1 || item.PriceMissing[0] != "competitor" {
		t.Fatalf("expected price_missing [competitor], got %#v", item.PriceMissing)
	}
	if item.DeltaWhEstimateAbs == nil || *item.DeltaWhEstimateAbs != 20 {
		t.Fatalf("expected delta abs 20, got %#v", item.DeltaWhEstimateAbs)
	}
	if item.DeltaWhEstimatePct == nil || *item.DeltaWhEstimatePct != 20 {
		t.Fatalf("expected delta pct 20, got %#v", item.DeltaWhEstimatePct)
	}
}

func TestCalculatePriceLevels_UsesExplicitPricelistIDs(t *testing.T) {
	db := newPriceLevelsTestDB(t)
	repo := repository.NewPricelistRepository(db)
	service := NewQuoteService(nil, repo, nil, nil)

	olderEstimate := seedPricelistWithItem(t, repo, "estimate", "CPU_Y", 80)
	seedPricelistWithItem(t, repo, "estimate", "CPU_Y", 90)

	result, err := service.CalculatePriceLevels(&PriceLevelsRequest{
		Items: []struct {
			LotName  string `json:"lot_name"`
			Quantity int    `json:"quantity"`
		}{
			{LotName: "CPU_Y", Quantity: 1},
		},
		PricelistIDs: map[string]uint{
			"estimate": olderEstimate.ID,
		},
	})
	if err != nil {
		t.Fatalf("CalculatePriceLevels returned error: %v", err)
	}
	item := result.Items[0]
	if item.EstimatePrice == nil || *item.EstimatePrice != 80 {
		t.Fatalf("expected explicit estimate 80, got %#v", item.EstimatePrice)
	}
}

func newPriceLevelsTestDB(t *testing.T) *gorm.DB {
	t.Helper()
	db, err := gorm.Open(sqlite.Open("file::memory:?cache=shared"), &gorm.Config{})
	if err != nil {
		t.Fatalf("open sqlite: %v", err)
	}
	if err := db.AutoMigrate(&models.Pricelist{}, &models.PricelistItem{}); err != nil {
		t.Fatalf("migrate: %v", err)
	}
	return db
}

func seedPricelistWithItem(t *testing.T, repo *repository.PricelistRepository, source, lot string, price float64) *models.Pricelist {
	t.Helper()
	version, err := repo.GenerateVersionBySource(source)
	if err != nil {
		t.Fatalf("GenerateVersionBySource: %v", err)
	}
	expiresAt := time.Now().Add(24 * time.Hour)
	pl := &models.Pricelist{
		Source:    source,
		Version:   version,
		CreatedBy: "test",
		IsActive:  true,
		ExpiresAt: &expiresAt,
	}
	if err := repo.Create(pl); err != nil {
		t.Fatalf("create pricelist: %v", err)
	}
	if err := repo.CreateItems([]models.PricelistItem{
		{
			PricelistID: pl.ID,
			LotName:     lot,
			Price:       price,
		},
	}); err != nil {
		t.Fatalf("create items: %v", err)
	}
	return pl
}
