package repositories

import (
	"context"
	"errors"
	"testing"
	"time"

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

func TestUpdateReceivingDetailsAllowsNoOpUpdatesOnExistingItem(t *testing.T) {
	db := setupPurchaseRepositoryTestDB(t)
	repo := NewPurchaseRepository(db)
	ctx := context.Background()

	receivedAt := time.Date(2026, 4, 1, 0, 0, 0, 0, time.UTC)
	travelNumber := "SJ-001"
	vehicleNumber := "B 1234 CD"

	if err := db.WithContext(ctx).Create(&entity.PurchaseItem{
		Id:            10,
		PurchaseId:    1,
		ProductId:     2,
		WarehouseId:   3,
		SubQty:        10,
		TotalQty:      10,
		Price:         15000,
		TotalPrice:    150000,
		ReceivedDate:  &receivedAt,
		TravelNumber:  &travelNumber,
		VehicleNumber: &vehicleNumber,
	}).Error; err != nil {
		t.Fatalf("failed seeding purchase item: %v", err)
	}

	pwID := uint(99)
	if err := repo.UpdateReceivingDetails(ctx, 1, []PurchaseReceivingUpdate{
		{
			ItemID:             10,
			ReceivedDate:       &receivedAt,
			TravelNumber:       &travelNumber,
			VehicleNumber:      &vehicleNumber,
			ProductWarehouseID: &pwID,
		},
	}); err != nil {
		t.Fatalf("expected no-op receive update to succeed, got %v", err)
	}
}

func TestUpdateReceivingDetailsReturnsNotFoundForMissingItem(t *testing.T) {
	db := setupPurchaseRepositoryTestDB(t)
	repo := NewPurchaseRepository(db)
	ctx := context.Background()

	receivedAt := time.Date(2026, 4, 1, 0, 0, 0, 0, time.UTC)

	err := repo.UpdateReceivingDetails(ctx, 1, []PurchaseReceivingUpdate{
		{
			ItemID:       999,
			ReceivedDate: &receivedAt,
		},
	})
	if !errors.Is(err, gorm.ErrRecordNotFound) {
		t.Fatalf("expected gorm.ErrRecordNotFound, got %v", err)
	}
}

func TestUpdatePricingAllowsNoOpUpdatesOnExistingItem(t *testing.T) {
	db := setupPurchaseRepositoryTestDB(t)
	repo := NewPurchaseRepository(db)
	ctx := context.Background()

	if err := db.WithContext(ctx).Create(&entity.PurchaseItem{
		Id:          20,
		PurchaseId:  2,
		ProductId:   5,
		WarehouseId: 6,
		SubQty:      5,
		TotalQty:    5,
		Price:       10000,
		TotalPrice:  50000,
	}).Error; err != nil {
		t.Fatalf("failed seeding purchase item: %v", err)
	}

	if err := repo.UpdatePricing(ctx, 2, []PurchasePricingUpdate{
		{
			ItemID:     20,
			Price:      10000,
			TotalPrice: 50000,
		},
	}); err != nil {
		t.Fatalf("expected no-op pricing update to succeed, got %v", err)
	}
}

func TestUpdatePricingReturnsNotFoundForMissingItem(t *testing.T) {
	db := setupPurchaseRepositoryTestDB(t)
	repo := NewPurchaseRepository(db)
	ctx := context.Background()

	err := repo.UpdatePricing(ctx, 2, []PurchasePricingUpdate{
		{
			ItemID:     777,
			Price:      10000,
			TotalPrice: 50000,
		},
	})
	if !errors.Is(err, gorm.ErrRecordNotFound) {
		t.Fatalf("expected gorm.ErrRecordNotFound, got %v", err)
	}
}

func setupPurchaseRepositoryTestDB(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 purchase_items (
			id INTEGER PRIMARY KEY,
			purchase_id INTEGER NOT NULL,
			product_id INTEGER NOT NULL,
			warehouse_id INTEGER NOT NULL,
			product_warehouse_id INTEGER NULL,
			project_flock_kandang_id INTEGER NULL,
			received_date TIMESTAMP NULL,
			travel_number TEXT NULL,
			travel_number_docs TEXT NULL,
			vehicle_number TEXT NULL,
			sub_qty NUMERIC(15,3) NOT NULL DEFAULT 0,
			total_qty NUMERIC(15,3) NOT NULL DEFAULT 0,
			total_used NUMERIC(15,3) NOT NULL DEFAULT 0,
			price NUMERIC(15,3) NOT NULL DEFAULT 0,
			total_price NUMERIC(15,3) NOT NULL DEFAULT 0,
			expense_nonstock_id INTEGER NULL
		)`,
	}
	for _, stmt := range statements {
		if err := db.Exec(stmt).Error; err != nil {
			t.Fatalf("failed preparing schema: %v", err)
		}
	}

	return db
}
