package service

import (
	"context"
	"strings"
	"testing"

	"github.com/glebarez/sqlite"
	entity "gitlab.com/mbugroup/lti-api.git/internal/entities"
	rProductWarehouse "gitlab.com/mbugroup/lti-api.git/internal/modules/inventory/product-warehouses/repositories"
	repository "gitlab.com/mbugroup/lti-api.git/internal/modules/production/recordings/repositories"
	validation "gitlab.com/mbugroup/lti-api.git/internal/modules/production/recordings/validations"
	"gorm.io/gorm"
)

func TestResolveEggRequestsToFarmWarehousesChoosesFirstFarmWarehouse(t *testing.T) {
	db := setupRecordingServiceTestDB(t)
	repo := repository.NewRecordingRepository(db)
	productWarehouseRepo := rProductWarehouse.NewProductWarehouseRepository(db)
	svc := &recordingService{
		Log:                  nil,
		Repository:           repo,
		ProductWarehouseRepo: productWarehouseRepo,
	}

	pfk := &entity.ProjectFlockKandang{
		Id:        10,
		KandangId: 59,
		ProjectFlock: entity.ProjectFlock{
			LocationId: 16,
			Location:   entity.Location{Name: "Jamali"},
		},
		Kandang: entity.Kandang{
			Id:         59,
			LocationId: 16,
			Location:   entity.Location{Name: "Jamali"},
		},
	}

	got, err := svc.resolveEggRequestsToFarmWarehouses(context.Background(), pfk, 9, []validation.Egg{
		{ProductWarehouseId: 101, Qty: 120},
	})
	if err != nil {
		t.Fatalf("expected no error, got %v", err)
	}
	if len(got) != 1 {
		t.Fatalf("expected 1 egg row, got %d", len(got))
	}
	if got[0].ProductWarehouseId == 101 {
		t.Fatalf("expected egg warehouse to be remapped to farm warehouse")
	}

	var resolved entity.ProductWarehouse
	if err := db.WithContext(context.Background()).
		Preload("Warehouse").
		First(&resolved, got[0].ProductWarehouseId).Error; err != nil {
		t.Fatalf("failed to load resolved product warehouse: %v", err)
	}

	if resolved.ProductId != 8 {
		t.Fatalf("expected product_id 8, got %d", resolved.ProductId)
	}
	if resolved.WarehouseId != 21 {
		t.Fatalf("expected first farm warehouse id 21, got %d", resolved.WarehouseId)
	}
	if resolved.ProjectFlockKandangId != nil {
		t.Fatalf("expected farm-level product warehouse to remain shared, got pfk %+v", resolved.ProjectFlockKandangId)
	}
}

func TestResolveEggRequestsToFarmWarehousesFailsWhenFarmHasNoWarehouse(t *testing.T) {
	db := setupRecordingServiceTestDB(t)
	if err := db.Exec("DELETE FROM warehouses WHERE type = 'LOKASI'").Error; err != nil {
		t.Fatalf("failed to remove farm warehouses: %v", err)
	}

	repo := repository.NewRecordingRepository(db)
	productWarehouseRepo := rProductWarehouse.NewProductWarehouseRepository(db)
	svc := &recordingService{
		Log:                  nil,
		Repository:           repo,
		ProductWarehouseRepo: productWarehouseRepo,
	}

	pfk := &entity.ProjectFlockKandang{
		Id:        10,
		KandangId: 59,
		ProjectFlock: entity.ProjectFlock{
			LocationId: 16,
			Location:   entity.Location{Name: "Jamali"},
		},
	}

	_, err := svc.resolveEggRequestsToFarmWarehouses(context.Background(), pfk, 9, []validation.Egg{
		{ProductWarehouseId: 101, Qty: 120},
	})
	if err == nil {
		t.Fatal("expected validation error when farm warehouse is missing")
	}
	if !strings.Contains(err.Error(), "Farm Jamali belum memiliki gudang") {
		t.Fatalf("expected missing farm warehouse error, got %v", err)
	}
}

func TestShouldNormalizeEggRequestsOnUpdatePreservesHistoricalKandangEggs(t *testing.T) {
	db := setupRecordingServiceTestDB(t)
	repo := repository.NewRecordingRepository(db)
	productWarehouseRepo := rProductWarehouse.NewProductWarehouseRepository(db)
	svc := &recordingService{
		Log:                  nil,
		Repository:           repo,
		ProductWarehouseRepo: productWarehouseRepo,
	}

	shouldNormalize, err := svc.shouldNormalizeEggRequestsOnUpdate(context.Background(), []entity.RecordingEgg{
		{ProductWarehouseId: 101, Qty: 120},
	})
	if err != nil {
		t.Fatalf("expected no error, got %v", err)
	}
	if shouldNormalize {
		t.Fatal("expected historical kandang-level egg rows to remain kandang-level on update")
	}
}

func TestShouldNormalizeEggRequestsOnUpdateNormalizesFarmLevelEggs(t *testing.T) {
	db := setupRecordingServiceTestDB(t)
	if err := db.Exec(`INSERT INTO product_warehouses (id, product_id, warehouse_id, project_flock_kandang_id, qty) VALUES (201, 8, 21, NULL, 300)`).Error; err != nil {
		t.Fatalf("failed to insert farm-level egg warehouse: %v", err)
	}

	repo := repository.NewRecordingRepository(db)
	productWarehouseRepo := rProductWarehouse.NewProductWarehouseRepository(db)
	svc := &recordingService{
		Log:                  nil,
		Repository:           repo,
		ProductWarehouseRepo: productWarehouseRepo,
	}

	shouldNormalize, err := svc.shouldNormalizeEggRequestsOnUpdate(context.Background(), []entity.RecordingEgg{
		{ProductWarehouseId: 201, Qty: 120},
	})
	if err != nil {
		t.Fatalf("expected no error, got %v", err)
	}
	if !shouldNormalize {
		t.Fatal("expected farm-level egg rows to keep using farm normalization on update")
	}
}

func TestResolveTransferSourceProjectFlockKandangIDPrefersTransferResourceSource(t *testing.T) {
	db := setupTransferSourceResolutionTestDB(t)
	repo := repository.NewRecordingRepository(db)
	svc := &recordingService{Repository: repo}

	transfer := &entity.LayingTransfer{
		Id:                          77,
		SourceProjectFlockKandangId: uintPtrForTest(999),
	}

	if err := db.Exec(`INSERT INTO laying_transfer_sources (id, laying_transfer_id, source_project_flock_kandang_id, deleted_at) VALUES (1, 77, 123, NULL)`).Error; err != nil {
		t.Fatalf("failed seeding laying_transfer_sources: %v", err)
	}

	got, err := svc.resolveTransferSourceProjectFlockKandangID(context.Background(), transfer)
	if err != nil {
		t.Fatalf("expected no error, got %v", err)
	}
	if got != 123 {
		t.Fatalf("expected resource source project_flock_kandang_id=123, got %d", got)
	}
}

func TestResolveTransferSourceProjectFlockKandangIDFallsBackToTransferHeader(t *testing.T) {
	db := setupTransferSourceResolutionTestDB(t)
	repo := repository.NewRecordingRepository(db)
	svc := &recordingService{Repository: repo}

	transfer := &entity.LayingTransfer{
		Id:                          78,
		SourceProjectFlockKandangId: uintPtrForTest(456),
	}

	got, err := svc.resolveTransferSourceProjectFlockKandangID(context.Background(), transfer)
	if err != nil {
		t.Fatalf("expected no error, got %v", err)
	}
	if got != 456 {
		t.Fatalf("expected fallback source project_flock_kandang_id=456, got %d", got)
	}
}

func uintPtrForTest(value uint) *uint {
	v := value
	return &v
}

func setupRecordingServiceTestDB(t *testing.T) *gorm.DB {
	t.Helper()

	db, err := gorm.Open(sqlite.Open("file:"+t.Name()+"?mode=memory&cache=private"), &gorm.Config{})
	if err != nil {
		t.Fatalf("failed opening sqlite db: %v", err)
	}

	statements := []string{
		`CREATE TABLE locations (
			id INTEGER PRIMARY KEY,
			name TEXT NOT NULL
		)`,
		`CREATE TABLE warehouses (
			id INTEGER PRIMARY KEY,
			name TEXT NOT NULL,
			type TEXT NOT NULL,
			location_id INTEGER NULL,
			kandang_id INTEGER NULL,
			deleted_at TIMESTAMP NULL
		)`,
		`CREATE TABLE product_warehouses (
			id INTEGER PRIMARY KEY AUTOINCREMENT,
			product_id INTEGER NOT NULL,
			warehouse_id INTEGER NOT NULL,
			project_flock_kandang_id INTEGER NULL,
			qty NUMERIC NULL
		)`,
		`INSERT INTO locations (id, name) VALUES (16, 'Jamali')`,
		`INSERT INTO warehouses (id, name, type, location_id, kandang_id, deleted_at) VALUES
			(21, 'Gudang Farm Jamali A', 'LOKASI', 16, NULL, NULL),
			(25, 'Gudang Farm Jamali B', 'LOKASI', 16, NULL, NULL),
			(46, 'Gudang Jamali 1', 'KANDANG', 16, 59, NULL)`,
		`INSERT INTO product_warehouses (id, product_id, warehouse_id, project_flock_kandang_id, qty) VALUES
			(101, 8, 46, 10, 500)`,
	}

	for _, stmt := range statements {
		if err := db.Exec(stmt).Error; err != nil {
			t.Fatalf("failed preparing schema: %v", err)
		}
	}

	return db
}

func setupTransferSourceResolutionTestDB(t *testing.T) *gorm.DB {
	t.Helper()

	db, err := gorm.Open(sqlite.Open("file:"+t.Name()+"?mode=memory&cache=private"), &gorm.Config{})
	if err != nil {
		t.Fatalf("failed opening sqlite db: %v", err)
	}

	if err := db.Exec(`CREATE TABLE laying_transfer_sources (
		id INTEGER PRIMARY KEY,
		laying_transfer_id INTEGER NOT NULL,
		source_project_flock_kandang_id INTEGER NOT NULL,
		deleted_at TIMESTAMP NULL
	)`).Error; err != nil {
		t.Fatalf("failed creating laying_transfer_sources schema: %v", err)
	}

	return db
}
