package service

import (
	"context"
	"testing"

	"github.com/glebarez/sqlite"
	commonSvc "gitlab.com/mbugroup/lti-api.git/internal/common/service"
	"gorm.io/gorm"
)

func TestResolvePurchaseFlagGroupByProductWarehouseFallsBackToProductCategory(t *testing.T) {
	db := setupPurchaseFifoHelperTestDB(t)
	ctx := context.Background()

	flagGroupCode, err := resolvePurchaseFlagGroupByProductWarehouse(ctx, db, 1115)
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if flagGroupCode != "PAKAN" {
		t.Fatalf("expected PAKAN, got %s", flagGroupCode)
	}
}

func TestResolvePurchaseFlagGroupByProductWarehouseUsesProductFlagsWhenPresent(t *testing.T) {
	db := setupPurchaseFifoHelperTestDB(t)
	ctx := context.Background()

	flagGroupCode, err := resolvePurchaseFlagGroupByProductWarehouse(ctx, db, 2222)
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if flagGroupCode != "OVK" {
		t.Fatalf("expected OVK, got %s", flagGroupCode)
	}
}

func TestReflowPurchaseScopeRunsRecalculateToFixWarehouseDrift(t *testing.T) {
	db := setupPurchaseFifoHelperTestDB(t)
	ctx := context.Background()
	fifo := &purchaseFifoStub{}

	if err := reflowPurchaseScope(ctx, fifo, db, 1115, nil); err != nil {
		t.Fatalf("unexpected error: %v", err)
	}

	if len(fifo.reflowReqs) != 1 {
		t.Fatalf("expected 1 reflow request, got %d", len(fifo.reflowReqs))
	}
	reflowReq := fifo.reflowReqs[0]
	if reflowReq.ProductWarehouseID != 1115 {
		t.Fatalf("expected reflow product warehouse 1115, got %d", reflowReq.ProductWarehouseID)
	}
	if reflowReq.FlagGroupCode != "PAKAN" {
		t.Fatalf("expected reflow flag group PAKAN, got %s", reflowReq.FlagGroupCode)
	}

	if len(fifo.recalculateReqs) != 1 {
		t.Fatalf("expected 1 recalculate request, got %d", len(fifo.recalculateReqs))
	}
	recalculateReq := fifo.recalculateReqs[0]
	if len(recalculateReq.ProductWarehouseIDs) != 1 || recalculateReq.ProductWarehouseIDs[0] != 1115 {
		t.Fatalf("expected recalculate for warehouse 1115, got %+v", recalculateReq.ProductWarehouseIDs)
	}
	if len(recalculateReq.FlagGroupCodes) != 1 || recalculateReq.FlagGroupCodes[0] != "PAKAN" {
		t.Fatalf("expected recalculate for PAKAN, got %+v", recalculateReq.FlagGroupCodes)
	}
	if !recalculateReq.FixDrift {
		t.Fatalf("expected recalculate FixDrift=true")
	}
}

type purchaseFifoStub struct {
	reflowReqs      []commonSvc.FifoStockV2ReflowRequest
	recalculateReqs []commonSvc.FifoStockV2RecalculateRequest
}

func (s *purchaseFifoStub) Gather(context.Context, commonSvc.FifoStockV2GatherRequest) ([]commonSvc.FifoStockV2GatherRow, error) {
	return nil, nil
}

func (s *purchaseFifoStub) Allocate(context.Context, commonSvc.FifoStockV2AllocateRequest) (*commonSvc.FifoStockV2AllocateResult, error) {
	return nil, nil
}

func (s *purchaseFifoStub) Rollback(context.Context, commonSvc.FifoStockV2RollbackRequest) (*commonSvc.FifoStockV2RollbackResult, error) {
	return nil, nil
}

func (s *purchaseFifoStub) Reflow(_ context.Context, req commonSvc.FifoStockV2ReflowRequest) (*commonSvc.FifoStockV2ReflowResult, error) {
	s.reflowReqs = append(s.reflowReqs, req)
	return &commonSvc.FifoStockV2ReflowResult{}, nil
}

func (s *purchaseFifoStub) Recalculate(_ context.Context, req commonSvc.FifoStockV2RecalculateRequest) (*commonSvc.FifoStockV2RecalculateResult, error) {
	s.recalculateReqs = append(s.recalculateReqs, req)
	return &commonSvc.FifoStockV2RecalculateResult{}, nil
}

func setupPurchaseFifoHelperTestDB(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 fifo_stock_v2_flag_groups (code TEXT PRIMARY KEY, is_active BOOLEAN NOT NULL)`,
		`CREATE TABLE fifo_stock_v2_flag_members (flag_name TEXT NOT NULL, flag_group_code TEXT NOT NULL, is_active BOOLEAN NOT NULL)`,
		`CREATE TABLE fifo_stock_v2_route_rules (
			id INTEGER PRIMARY KEY,
			flag_group_code TEXT NOT NULL,
			lane TEXT NOT NULL,
			function_code TEXT NOT NULL,
			source_table TEXT NOT NULL,
			is_active BOOLEAN NOT NULL
		)`,
		`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)`,
		`INSERT INTO fifo_stock_v2_flag_groups (code, is_active) VALUES ('PAKAN', TRUE), ('OVK', TRUE)`,
		`INSERT INTO fifo_stock_v2_flag_members (flag_name, flag_group_code, is_active) VALUES ('PAKAN', 'PAKAN', TRUE), ('OVK', 'OVK', TRUE), ('OBAT', 'OVK', TRUE)`,
		`INSERT INTO fifo_stock_v2_route_rules (id, flag_group_code, lane, function_code, source_table, is_active) VALUES
			(1, 'PAKAN', 'STOCKABLE', 'PURCHASE_IN', 'purchase_items', TRUE),
			(2, 'OVK', 'STOCKABLE', 'PURCHASE_IN', 'purchase_items', TRUE)`,
		`INSERT INTO product_categories (id, code) VALUES (1, 'RAW'), (2, 'OBT')`,
		`INSERT INTO products (id, product_category_id) VALUES (37, 1), (112, 2)`,
		`INSERT INTO flags (id, flagable_id, flagable_type, name) VALUES
			(1, 112, 'products', 'OVK'),
			(2, 112, 'products', 'OBAT')`,
		`INSERT INTO product_warehouses (id, product_id) VALUES (1115, 37), (2222, 112)`,
	}

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

	return db
}
