package sync_test

import (
	"errors"
	"strings"
	"testing"
	"time"

	"git.mchus.pro/mchus/quoteforge/internal/localdb"
	"git.mchus.pro/mchus/quoteforge/internal/models"
	syncsvc "git.mchus.pro/mchus/quoteforge/internal/services/sync"
	"gorm.io/gorm"
)

func TestSyncPricelistsDeletesMissingUnusedLocalPricelists(t *testing.T) {
	local := newLocalDBForSyncTest(t)
	serverDB := newServerDBForSyncTest(t)
	if err := serverDB.AutoMigrate(&models.Pricelist{}, &models.PricelistItem{}); err != nil {
		t.Fatalf("migrate server pricelist tables: %v", err)
	}

	serverPL := models.Pricelist{
		Source:       "estimate",
		Version:      "2026-01-01-001",
		Notification: "server",
		CreatedBy:    "tester",
		IsActive:     true,
		CreatedAt:    time.Now().Add(-1 * time.Hour),
	}
	if err := serverDB.Create(&serverPL).Error; err != nil {
		t.Fatalf("create server pricelist: %v", err)
	}
	if err := serverDB.Create(&models.PricelistItem{PricelistID: serverPL.ID, LotName: "CPU_A", Price: 10}).Error; err != nil {
		t.Fatalf("create server pricelist item: %v", err)
	}

	if err := local.SaveLocalPricelist(&localdb.LocalPricelist{
		ServerID:  9991,
		Source:    "estimate",
		Version:   "old-unused",
		Name:      "old-unused",
		CreatedAt: time.Now().Add(-2 * time.Hour),
		SyncedAt:  time.Now().Add(-2 * time.Hour),
		IsUsed:    false,
	}); err != nil {
		t.Fatalf("seed local missing pricelist: %v", err)
	}
	missingUsed := &localdb.LocalPricelist{
		ServerID:  9992,
		Source:    "estimate",
		Version:   "old-used",
		Name:      "old-used",
		CreatedAt: time.Now().Add(-2 * time.Hour),
		SyncedAt:  time.Now().Add(-2 * time.Hour),
		IsUsed:    false,
	}
	if err := local.SaveLocalPricelist(missingUsed); err != nil {
		t.Fatalf("seed local referenced pricelist: %v", err)
	}
	if err := local.SaveConfiguration(&localdb.LocalConfiguration{
		UUID:             "cfg-1",
		OriginalUsername: "tester",
		Name:             "cfg",
		Items:            localdb.LocalConfigItems{{LotName: "CPU_A", Quantity: 1, UnitPrice: 1}},
		IsActive:         true,
		PricelistID:      &missingUsed.ServerID,
		SyncStatus:       "synced",
		CreatedAt:        time.Now().Add(-30 * time.Minute),
		UpdatedAt:        time.Now().Add(-30 * time.Minute),
	}); err != nil {
		t.Fatalf("seed local configuration with pricelist ref: %v", err)
	}

	svc := syncsvc.NewServiceWithDB(serverDB, local)
	if _, err := svc.SyncPricelists(); err != nil {
		t.Fatalf("sync pricelists: %v", err)
	}

	if _, err := local.GetLocalPricelistByServerID(9991); err == nil {
		t.Fatalf("expected unused missing local pricelist to be deleted")
	}
	if _, err := local.GetLocalPricelistByServerID(9992); err != nil {
		t.Fatalf("expected local pricelist referenced by active config to stay: %v", err)
	}
	if _, err := local.GetLocalPricelistByServerID(serverPL.ID); err != nil {
		t.Fatalf("expected server pricelist to be synced locally: %v", err)
	}
}

func TestSyncPricelistsDoesNotPersistHeaderWithoutItems(t *testing.T) {
	local := newLocalDBForSyncTest(t)
	serverDB := newServerDBForSyncTest(t)
	if err := serverDB.AutoMigrate(&models.Pricelist{}, &models.PricelistItem{}); err != nil {
		t.Fatalf("migrate server pricelist tables: %v", err)
	}

	serverPL := models.Pricelist{
		Source:       "estimate",
		Version:      "2026-03-17-001",
		Notification: "server",
		CreatedBy:    "tester",
		IsActive:     true,
		CreatedAt:    time.Now().Add(-1 * time.Hour),
	}
	if err := serverDB.Create(&serverPL).Error; err != nil {
		t.Fatalf("create server pricelist: %v", err)
	}
	if err := serverDB.Create(&models.PricelistItem{PricelistID: serverPL.ID, LotName: "CPU_A", Price: 10}).Error; err != nil {
		t.Fatalf("create server pricelist item: %v", err)
	}

	const callbackName = "test:fail_qt_pricelist_items_query"
	if err := serverDB.Callback().Query().Before("gorm:query").Register(callbackName, func(db *gorm.DB) {
		if db.Statement != nil && db.Statement.Table == "qt_pricelist_items" {
			_ = db.AddError(errors.New("forced pricelist item fetch failure"))
		}
	}); err != nil {
		t.Fatalf("register query callback: %v", err)
	}
	defer serverDB.Callback().Query().Remove(callbackName)

	svc := syncsvc.NewServiceWithDB(serverDB, local)
	synced, err := svc.SyncPricelists()
	if err == nil {
		t.Fatalf("expected sync error when item fetch fails")
	}
	if synced != 0 {
		t.Fatalf("expected synced=0 on incomplete sync, got %d", synced)
	}
	if !strings.Contains(err.Error(), "forced pricelist item fetch failure") {
		t.Fatalf("expected item fetch error, got %v", err)
	}

	if _, err := local.GetLocalPricelistByServerID(serverPL.ID); err == nil {
		t.Fatalf("expected pricelist header not to be persisted without items")
	}
	if got := local.CountLocalPricelists(); got != 0 {
		t.Fatalf("expected no local pricelists after failed sync, got %d", got)
	}
	if ts := local.GetLastSyncTime(); ts != nil {
		t.Fatalf("expected last_pricelist_sync to stay unset on incomplete sync, got %v", ts)
	}
}
