package repository

import (
	"context"
	"testing"

	"github.com/glebarez/sqlite"
	entity "gitlab.com/mbugroup/lti-api.git/internal/entities"
	"gorm.io/gorm"
)

func TestGetProductWarehouseByProductAndWarehouseIDPrefersSharedForFarmWarehouse(t *testing.T) {
	db := setupProductWarehouseRepoTestDB(t)
	repo := NewProductWarehouseRepository(db)
	ctx := context.Background()

	insertProductWarehouseTestFixtures(t, db)

	got, err := repo.GetProductWarehouseByProductAndWarehouseID(ctx, 1, 1)
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if got.Id != 2 {
		t.Fatalf("expected shared farm warehouse id 2, got %d", got.Id)
	}
}

func TestGetProductWarehouseByProductAndWarehouseIDPrefersOpenKandangOwnedForKandangWarehouse(t *testing.T) {
	db := setupProductWarehouseRepoTestDB(t)
	repo := NewProductWarehouseRepository(db)
	ctx := context.Background()

	insertProductWarehouseTestFixtures(t, db)

	got, err := repo.GetProductWarehouseByProductAndWarehouseID(ctx, 1, 2)
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if got.Id != 3 {
		t.Fatalf("expected kandang-owned warehouse id 3, got %d", got.Id)
	}
}

func TestEnsureProductWarehouseDoesNotBackfillSharedWarehouse(t *testing.T) {
	db := setupProductWarehouseRepoTestDB(t)
	repo := NewProductWarehouseRepository(db)
	ctx := context.Background()

	insertProductWarehouseTestFixtures(t, db)

	projectFlockKandangID := uint(101)
	createdID, err := repo.EnsureProductWarehouse(ctx, 1, 1, &projectFlockKandangID, 9)
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if createdID == 2 {
		t.Fatalf("expected new kandang-attributed row instead of reusing shared row")
	}

	var sharedPfkID *uint
	if err := db.WithContext(ctx).
		Table("product_warehouses").
		Select("project_flock_kandang_id").
		Where("id = ?", 2).
		Scan(&sharedPfkID).Error; err != nil {
		t.Fatalf("failed to load shared warehouse row: %v", err)
	}
	if sharedPfkID != nil {
		t.Fatalf("expected shared row attribution to stay nil, got %v", *sharedPfkID)
	}
}

func setupProductWarehouseRepoTestDB(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 warehouses (id INTEGER PRIMARY KEY, kandang_id INTEGER NULL)`,
		`CREATE TABLE project_flock_kandangs (id INTEGER PRIMARY KEY, closed_at TIMESTAMP NULL)`,
		`CREATE TABLE product_warehouses (
			id INTEGER PRIMARY KEY,
			product_id INTEGER NOT NULL,
			warehouse_id INTEGER NOT NULL,
			project_flock_kandang_id INTEGER NULL,
			qty NUMERIC(15,3) NOT NULL DEFAULT 0
		)`,
	}
	for _, stmt := range statements {
		if err := db.Exec(stmt).Error; err != nil {
			t.Fatalf("failed preparing schema: %v", err)
		}
	}

	return db
}

func insertProductWarehouseTestFixtures(t *testing.T, db *gorm.DB) {
	t.Helper()

	statements := []string{
		`INSERT INTO warehouses (id, kandang_id) VALUES (1, NULL), (2, 7)`,
		`INSERT INTO project_flock_kandangs (id, closed_at) VALUES (101, NULL)`,
		`INSERT INTO product_warehouses (id, product_id, warehouse_id, project_flock_kandang_id, qty) VALUES
			(1, 1, 1, 101, 10),
			(2, 1, 1, NULL, 20),
			(3, 1, 2, 101, 30),
			(4, 1, 2, NULL, 40)`,
	}
	for _, stmt := range statements {
		if err := db.Exec(stmt).Error; err != nil {
			t.Fatalf("failed seeding fixtures: %v", err)
		}
	}
}

func TestApplyFlagsFilterIncludesLegacyCategoryFallback(t *testing.T) {
	db := setupProductWarehouseFlagFilterTestDB(t)
	repo := NewProductWarehouseRepository(db)
	ctx := context.Background()

	var ids []uint
	err := repo.ApplyFlagsFilter(
		db.WithContext(ctx).Model(&entity.ProductWarehouse{}),
		[]string{"PAKAN"},
	).Order("product_warehouses.id").Pluck("product_warehouses.id", &ids).Error
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}

	if len(ids) != 2 || ids[0] != 1 || ids[1] != 2 {
		t.Fatalf("expected flagged and legacy RAW rows to match, got %v", ids)
	}
}

func TestApplyFlagsFilterDoesNotFallbackWhenProductAlreadyHasDifferentFlags(t *testing.T) {
	db := setupProductWarehouseFlagFilterTestDB(t)
	repo := NewProductWarehouseRepository(db)
	ctx := context.Background()

	var ids []uint
	err := repo.ApplyFlagsFilter(
		db.WithContext(ctx).Model(&entity.ProductWarehouse{}),
		[]string{"PAKAN"},
	).Where("product_warehouses.id = ?", 3).Pluck("product_warehouses.id", &ids).Error
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}

	if len(ids) != 0 {
		t.Fatalf("expected OVK-flagged product not to match PAKAN fallback, got %v", ids)
	}
}

func setupProductWarehouseFlagFilterTestDB(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 product_categories (id INTEGER PRIMARY KEY, code TEXT NOT NULL)`,
		`CREATE TABLE products (id INTEGER PRIMARY KEY, product_category_id INTEGER NOT NULL)`,
		`CREATE TABLE flags (id INTEGER PRIMARY KEY, flagable_id INTEGER NOT NULL, flagable_type TEXT NOT NULL, name TEXT NOT NULL)`,
		`CREATE TABLE product_warehouses (id INTEGER PRIMARY KEY, product_id INTEGER NOT NULL, warehouse_id INTEGER NOT NULL, project_flock_kandang_id INTEGER NULL, qty NUMERIC(15,3) NOT NULL DEFAULT 0)`,
		`INSERT INTO product_categories (id, code) VALUES (1, 'STR'), (2, 'RAW'), (3, 'OBT')`,
		`INSERT INTO products (id, product_category_id) VALUES (10, 1), (20, 2), (30, 2), (40, 3)`,
		`INSERT INTO flags (id, flagable_id, flagable_type, name) VALUES
			(1, 10, 'products', 'PAKAN'),
			(2, 10, 'products', 'STARTER'),
			(3, 40, 'products', 'OVK'),
			(4, 40, 'products', 'OBAT')`,
		`INSERT INTO product_warehouses (id, product_id, warehouse_id, project_flock_kandang_id, qty) VALUES
			(1, 10, 1, NULL, 10),
			(2, 20, 1, NULL, 20),
			(3, 40, 1, NULL, 30)`,
	}
	for _, stmt := range statements {
		if err := db.Exec(stmt).Error; err != nil {
			t.Fatalf("failed preparing schema: %v", err)
		}
	}

	return db
}
