<?php

namespace Tests\Unit\Models;

use Tests\TestCase;
use App\Models\ItemRequest;
use App\Models\Store;
use App\Models\StockOpname;
use App\Models\User;
use App\Models\NumberingFormat;
use Carbon\Carbon;
use Illuminate\Foundation\Testing\RefreshDatabase;

class ItemRequestTest extends TestCase
{
    use RefreshDatabase;

    protected $testStore;
    protected $testUser;
    protected $testStockOpname;

    protected function setUp(): void
    {
        parent::setUp();
        
        // Create test user
        $this->testUser = User::create([
            'name' => 'Test User',
            'email' => 'test@example.com',
            'password' => bcrypt('password'),
        ]);

        // Create test store
        $this->testStore = Store::create([
            'code' => 'TST001',
            'name' => 'Test Store',
            'address' => 'Test Address',
            'delete_flag' => false
        ]);

        // Create test stock opname
        $this->testStockOpname = StockOpname::create([
            'store_id' => $this->testStore->id,
            'so_number' => 'SO-TEST-001',
            'so_date' => Carbon::now()->format('Y-m-d'),
            'status' => 'Completed',
            'delete_flag' => false
        ]);

        // Create numbering format for SPB
        NumberingFormat::create([
            'store_id' => $this->testStore->id,
            'document_type' => 'SPB',
            'format_pattern' => '{DOCTYPE}{SEP}{STORE}{SEP}{YY}{MM}{SEP}{NUMBER}',
            'separator' => '-',
            'number_length' => 4,
            'reset_period' => 'Monthly',
            'current_number' => 0,
            'is_active' => true,
            'delete_flag' => false
        ]);
    }

    /** @test */
    public function it_can_create_item_request_with_auto_generated_spb_number()
    {
        $itemRequest = ItemRequest::create([
            'store_id' => $this->testStore->id,
            'stock_opname_id' => $this->testStockOpname->id,
            'request_type' => 'Regular',
            'request_date' => Carbon::now()->format('Y-m-d'),
            'delivery_date' => Carbon::now()->addDays(3)->format('Y-m-d'),
            'status' => ItemRequest::STATUS_DRAFT,
            'created_by' => $this->testUser->id,
            'delete_flag' => false
        ]);

        $this->assertNotNull($itemRequest->spb_number);
        $this->assertStringContainsString('SPB', $itemRequest->spb_number);
        $this->assertStringContainsString('TST001', $itemRequest->spb_number);
    }

    /** @test */
    public function it_can_check_status_methods()
    {
        $draftRequest = ItemRequest::create([
            'spb_number' => 'SPB-TEST-001',
            'store_id' => $this->testStore->id,
            'request_type' => 'Regular',
            'status' => ItemRequest::STATUS_DRAFT,
            'delete_flag' => false
        ]);

        $submittedRequest = ItemRequest::create([
            'spb_number' => 'SPB-TEST-002',
            'store_id' => $this->testStore->id,
            'request_type' => 'Regular',
            'status' => ItemRequest::STATUS_SUBMITTED,
            'delete_flag' => false
        ]);

        $approvedRequest = ItemRequest::create([
            'spb_number' => 'SPB-TEST-003',
            'store_id' => $this->testStore->id,
            'request_type' => 'Regular',
            'status' => ItemRequest::STATUS_APPROVED,
            'delete_flag' => false
        ]);

        $cancelledRequest = ItemRequest::create([
            'spb_number' => 'SPB-TEST-004',
            'store_id' => $this->testStore->id,
            'request_type' => 'Regular',
            'status' => ItemRequest::STATUS_CANCELLED,
            'delete_flag' => false
        ]);

        // Test status methods
        $this->assertTrue($draftRequest->isDraft());
        $this->assertFalse($draftRequest->isSubmitted());
        $this->assertFalse($draftRequest->isApproved());
        $this->assertFalse($draftRequest->isCancelled());

        $this->assertTrue($submittedRequest->isSubmitted());
        $this->assertFalse($submittedRequest->isDraft());

        $this->assertTrue($approvedRequest->isApproved());
        $this->assertTrue($cancelledRequest->isCancelled());
    }

    /** @test */
    public function it_can_check_business_rules()
    {
        $draftRequest = ItemRequest::create([
            'spb_number' => 'SPB-TEST-001',
            'store_id' => $this->testStore->id,
            'request_type' => 'Regular',
            'status' => ItemRequest::STATUS_DRAFT,
            'delete_flag' => false
        ]);

        $submittedRequest = ItemRequest::create([
            'spb_number' => 'SPB-TEST-002',
            'store_id' => $this->testStore->id,
            'request_type' => 'Regular',
            'status' => ItemRequest::STATUS_SUBMITTED,
            'delete_flag' => false
        ]);

        $approvedRequest = ItemRequest::create([
            'spb_number' => 'SPB-TEST-003',
            'store_id' => $this->testStore->id,
            'request_type' => 'Regular',
            'status' => ItemRequest::STATUS_APPROVED,
            'delete_flag' => false
        ]);

        // Test can be edited
        $this->assertTrue($draftRequest->canBeEdited());
        $this->assertFalse($submittedRequest->canBeEdited());
        $this->assertFalse($approvedRequest->canBeEdited());

        // Test can be cancelled
        $this->assertFalse($draftRequest->canBeCancelled());
        $this->assertTrue($submittedRequest->canBeCancelled());
        $this->assertTrue($approvedRequest->canBeCancelled());

        // Test can generate DO
        $this->assertFalse($draftRequest->canGenerateDO());
        $this->assertFalse($submittedRequest->canGenerateDO());
        $this->assertTrue($approvedRequest->canGenerateDO());
    }

    /** @test */
    public function it_can_check_regular_spb_exists_for_so()
    {
        // Create regular SPB for stock opname
        ItemRequest::create([
            'spb_number' => 'SPB-TEST-001',
            'store_id' => $this->testStore->id,
            'stock_opname_id' => $this->testStockOpname->id,
            'request_type' => 'Regular',
            'status' => ItemRequest::STATUS_SUBMITTED,
            'delete_flag' => false
        ]);

        $this->assertTrue(ItemRequest::hasRegularSPBForSO($this->testStockOpname->id));

        // Create another stock opname
        $anotherSO = StockOpname::create([
            'store_id' => $this->testStore->id,
            'so_number' => 'SO-TEST-002',
            'so_date' => Carbon::now()->format('Y-m-d'),
            'status' => 'Completed',
            'delete_flag' => false
        ]);

        $this->assertFalse(ItemRequest::hasRegularSPBForSO($anotherSO->id));
    }

    /** @test */
    public function it_can_check_daily_so_exists_for_store()
    {
        $today = Carbon::now()->format('Y-m-d');
        $yesterday = Carbon::yesterday()->format('Y-m-d');

        $this->assertTrue(ItemRequest::hasDailySOForStore($this->testStore->id, $today));
        $this->assertFalse(ItemRequest::hasDailySOForStore($this->testStore->id, $yesterday));
    }

    /** @test */
    public function it_has_correct_relationships()
    {
        $itemRequest = ItemRequest::create([
            'spb_number' => 'SPB-TEST-001',
            'store_id' => $this->testStore->id,
            'stock_opname_id' => $this->testStockOpname->id,
            'request_type' => 'Regular',
            'status' => ItemRequest::STATUS_DRAFT,
            'created_by' => $this->testUser->id,
            'updated_by' => $this->testUser->id,
            'delete_flag' => false
        ]);

        // Test relationships
        $this->assertInstanceOf(Store::class, $itemRequest->store);
        $this->assertEquals($this->testStore->id, $itemRequest->store->id);

        $this->assertInstanceOf(StockOpname::class, $itemRequest->stockOpname);
        $this->assertEquals($this->testStockOpname->id, $itemRequest->stockOpname->id);

        $this->assertInstanceOf(User::class, $itemRequest->creator);
        $this->assertEquals($this->testUser->id, $itemRequest->creator->id);

        $this->assertInstanceOf(User::class, $itemRequest->modificator);
        $this->assertEquals($this->testUser->id, $itemRequest->modificator->id);
    }

    /** @test */
    public function it_casts_dates_correctly()
    {
        $itemRequest = ItemRequest::create([
            'spb_number' => 'SPB-TEST-001',
            'store_id' => $this->testStore->id,
            'request_type' => 'Regular',
            'request_date' => '2024-01-15',
            'delivery_date' => '2024-01-18',
            'status' => ItemRequest::STATUS_DRAFT,
            'delete_flag' => false
        ]);

        $this->assertInstanceOf(Carbon::class, $itemRequest->request_date);
        $this->assertInstanceOf(Carbon::class, $itemRequest->delivery_date);
        $this->assertEquals('2024-01-15', $itemRequest->request_date->format('Y-m-d'));
        $this->assertEquals('2024-01-18', $itemRequest->delivery_date->format('Y-m-d'));
    }

    /** @test */
    public function it_handles_cancellation_data()
    {
        $itemRequest = ItemRequest::create([
            'spb_number' => 'SPB-TEST-001',
            'store_id' => $this->testStore->id,
            'request_type' => 'Regular',
            'status' => ItemRequest::STATUS_CANCELLED,
            'cancelled_at' => Carbon::now(),
            'cancelled_by' => $this->testUser->id,
            'cancellation_reason' => 'Test cancellation',
            'cancellation_status' => 'Approved',
            'delete_flag' => false
        ]);

        $this->assertInstanceOf(Carbon::class, $itemRequest->cancelled_at);
        $this->assertInstanceOf(User::class, $itemRequest->cancelledBy);
        $this->assertEquals($this->testUser->id, $itemRequest->cancelledBy->id);
        $this->assertEquals('Test cancellation', $itemRequest->cancellation_reason);
    }

    /** @test */
    public function it_generates_fallback_spb_number_when_numbering_format_fails()
    {
        // Create store without numbering format
        $storeWithoutFormat = Store::create([
            'code' => 'TST002',
            'name' => 'Test Store 2',
            'address' => 'Test Address 2',
            'delete_flag' => false
        ]);

        $itemRequest = ItemRequest::create([
            'store_id' => $storeWithoutFormat->id,
            'request_type' => 'Regular',
            'status' => ItemRequest::STATUS_DRAFT,
            'delete_flag' => false
        ]);

        $this->assertNotNull($itemRequest->spb_number);
        $this->assertStringStartsWith('SPB', $itemRequest->spb_number);
        $this->assertStringContainsString(date('Ymd'), $itemRequest->spb_number);
    }

    /** @test */
    public function it_generates_different_prefix_for_temporary_spb()
    {
        $tempItemRequest = ItemRequest::create([
            'store_id' => $this->testStore->id,
            'request_type' => 'Temporary',
            'status' => ItemRequest::STATUS_DRAFT,
            'delete_flag' => false
        ]);

        // Since we have NumberingFormat, it should use that instead of fallback
        // But if fallback is used, it should have SPBT prefix for Temporary
        $this->assertNotNull($tempItemRequest->spb_number);
    }

    /** @test */
    public function it_can_check_cancellation_requested_status()
    {
        $itemRequest = ItemRequest::create([
            'spb_number' => 'SPB-TEST-001',
            'store_id' => $this->testStore->id,
            'request_type' => 'Regular',
            'status' => ItemRequest::STATUS_CANCELLATION_REQUESTED,
            'delete_flag' => false
        ]);

        $this->assertTrue($itemRequest->isCancellationRequested());
        $this->assertFalse($itemRequest->isDraft());
        $this->assertFalse($itemRequest->isCancelled());
    }
}