<?php

namespace Tests\Feature\SystemCore\Models;

use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Event;
use Modules\SystemCore\App\Enums\OrderStatus;
use Modules\SystemCore\App\Events\SalesOrderPaymentStatusChanged;
use Modules\SystemCore\App\Models\CoreSalesOrderLine;
use Tests\TestCase;
use Tests\Traits\CreatesSystemCoreEntities;
use Tests\Traits\CreatesUsers;

class CoreSalesOrderTest extends TestCase
{
    use CreatesSystemCoreEntities;
    use CreatesUsers;
    use RefreshDatabase;

    // ==========================================
    // Order Number Tests
    // ==========================================

    public function test_order_number_auto_generated(): void
    {
        $admin = $this->createAdmin();
        $this->actingAs($admin);

        $order = $this->createCoreSalesOrder(['created_by_id' => $admin->id]);

        $this->assertNotNull($order->order_number);
        $this->assertStringStartsWith('SO-', $order->order_number);
    }

    public function test_order_number_includes_date(): void
    {
        $admin = $this->createAdmin();
        $this->actingAs($admin);

        $order = $this->createCoreSalesOrder(['created_by_id' => $admin->id]);
        $today = date('Ymd');

        $this->assertStringContainsString($today, $order->order_number);
    }

    public function test_custom_order_number_not_overwritten(): void
    {
        $admin = $this->createAdmin();
        $this->actingAs($admin);

        $customNumber = 'CUSTOM-SO-123';
        $order = $this->createCoreSalesOrder([
            'order_number' => $customNumber,
            'created_by_id' => $admin->id,
        ]);

        $this->assertEquals($customNumber, $order->order_number);
    }

    // ==========================================
    // Payment Tracking Tests
    // ==========================================

    public function test_remaining_balance_calculation(): void
    {
        $admin = $this->createAdmin();
        $this->actingAs($admin);

        $order = $this->createCoreSalesOrder([
            'total_amount' => 1000.00,
            'paid_amount' => 300.00,
            'created_by_id' => $admin->id,
        ]);

        $this->assertEquals(700.00, $order->remaining_balance);
    }

    public function test_remaining_balance_with_full_payment(): void
    {
        $admin = $this->createAdmin();
        $this->actingAs($admin);

        $order = $this->createPaidSalesOrder([
            'total_amount' => 1000.00,
            'created_by_id' => $admin->id,
        ]);

        $this->assertEquals(0, $order->remaining_balance);
    }

    public function test_is_fully_paid_returns_true_when_balance_zero(): void
    {
        $admin = $this->createAdmin();
        $this->actingAs($admin);

        $order = $this->createCoreSalesOrder([
            'total_amount' => 500.00,
            'paid_amount' => 500.00,
            'payment_status' => 'partial', // Even if status is partial
            'created_by_id' => $admin->id,
        ]);

        $this->assertTrue($order->isFullyPaid());
    }

    public function test_is_fully_paid_returns_true_when_status_paid(): void
    {
        $admin = $this->createAdmin();
        $this->actingAs($admin);

        $order = $this->createPaidSalesOrder(['created_by_id' => $admin->id]);

        $this->assertTrue($order->isFullyPaid());
    }

    public function test_is_fully_paid_returns_false_when_unpaid(): void
    {
        $admin = $this->createAdmin();
        $this->actingAs($admin);

        $order = $this->createCoreSalesOrder([
            'total_amount' => 1000.00,
            'paid_amount' => 0,
            'payment_status' => 'pending',
            'created_by_id' => $admin->id,
        ]);

        $this->assertFalse($order->isFullyPaid());
    }

    // ==========================================
    // Fulfillment Tests
    // ==========================================

    public function test_is_fully_fulfilled_returns_true_when_status_fulfilled(): void
    {
        $admin = $this->createAdmin();
        $this->actingAs($admin);

        $order = $this->createCompletedSalesOrder(['created_by_id' => $admin->id]);

        $this->assertTrue($order->isFullyFulfilled());
    }

    public function test_is_fully_fulfilled_returns_false_when_pending(): void
    {
        $admin = $this->createAdmin();
        $this->actingAs($admin);

        $order = $this->createCoreSalesOrder([
            'fulfillment_status' => 'pending',
            'created_by_id' => $admin->id,
        ]);

        $this->assertFalse($order->isFullyFulfilled());
    }

    public function test_fulfillment_progress_calculation_with_no_lines(): void
    {
        $admin = $this->createAdmin();
        $this->actingAs($admin);

        $order = $this->createCoreSalesOrder(['created_by_id' => $admin->id]);

        $this->assertEquals(0.0, $order->fulfillment_progress);
    }

    // ==========================================
    // Event Tests
    // ==========================================

    public function test_payment_status_change_dispatches_event(): void
    {
        Event::fake([SalesOrderPaymentStatusChanged::class]);

        $admin = $this->createAdmin();
        $this->actingAs($admin);

        $order = $this->createCoreSalesOrder([
            'payment_status' => 'pending',
            'created_by_id' => $admin->id,
        ]);

        // Change payment status
        $order->update(['payment_status' => 'paid']);

        Event::assertDispatched(SalesOrderPaymentStatusChanged::class, function ($event) use ($order) {
            return $event->salesOrder->id === $order->id
                && $event->oldStatus === 'pending'
                && $event->newStatus === 'paid';
        });
    }

    public function test_no_event_when_payment_status_unchanged(): void
    {
        Event::fake([SalesOrderPaymentStatusChanged::class]);

        $admin = $this->createAdmin();
        $this->actingAs($admin);

        $order = $this->createCoreSalesOrder([
            'payment_status' => 'pending',
            'created_by_id' => $admin->id,
        ]);

        // Update a different field
        $order->update(['notes' => 'Updated notes']);

        Event::assertNotDispatched(SalesOrderPaymentStatusChanged::class);
    }

    public function test_event_dispatched_for_partial_payment(): void
    {
        Event::fake([SalesOrderPaymentStatusChanged::class]);

        $admin = $this->createAdmin();
        $this->actingAs($admin);

        $order = $this->createCoreSalesOrder([
            'payment_status' => 'pending',
            'created_by_id' => $admin->id,
        ]);

        $order->update(['payment_status' => 'partial']);

        Event::assertDispatched(SalesOrderPaymentStatusChanged::class, function ($event) {
            return $event->oldStatus === 'pending'
                && $event->newStatus === 'partial';
        });
    }

    // ==========================================
    // Relationship Tests
    // ==========================================

    public function test_order_belongs_to_customer(): void
    {
        $admin = $this->createAdmin();
        $this->actingAs($admin);

        $customer = $this->createCoreCustomer(['created_by_id' => $admin->id]);
        $order = $this->createCoreSalesOrder([
            'customer_id' => $customer->id,
            'created_by_id' => $admin->id,
        ]);

        $this->assertNotNull($order->customer);
        $this->assertEquals($customer->id, $order->customer->id);
    }

    public function test_order_belongs_to_user(): void
    {
        $admin = $this->createAdmin();
        $this->actingAs($admin);

        $order = $this->createCoreSalesOrder([
            'user_id' => $admin->id,
            'created_by_id' => $admin->id,
        ]);

        $this->assertNotNull($order->user);
        $this->assertEquals($admin->id, $order->user->id);
    }

    public function test_order_belongs_to_payment_term(): void
    {
        $admin = $this->createAdmin();
        $this->actingAs($admin);

        $paymentTerm = $this->createCorePaymentTerm(['created_by_id' => $admin->id]);
        $order = $this->createCoreSalesOrder([
            'payment_term_id' => $paymentTerm->id,
            'created_by_id' => $admin->id,
        ]);

        $this->assertNotNull($order->paymentTerm);
        $this->assertEquals($paymentTerm->id, $order->paymentTerm->id);
    }

    public function test_order_has_many_order_lines(): void
    {
        $admin = $this->createAdmin();
        $this->actingAs($admin);

        $order = $this->createCoreSalesOrder(['created_by_id' => $admin->id]);
        $product = $this->createCoreProduct(['created_by_id' => $admin->id]);

        // Create order lines
        CoreSalesOrderLine::create([
            'sales_order_id' => $order->id,
            'product_id' => $product->id,
            'product_name' => $product->name,
            'sku' => $product->sku,
            'quantity' => 2,
            'unit_price' => 100.00,
            'total_price' => 200.00,
            'created_by_id' => $admin->id,
        ]);

        CoreSalesOrderLine::create([
            'sales_order_id' => $order->id,
            'product_id' => $product->id,
            'product_name' => $product->name,
            'sku' => $product->sku,
            'quantity' => 1,
            'unit_price' => 50.00,
            'total_price' => 50.00,
            'created_by_id' => $admin->id,
        ]);

        $this->assertCount(2, $order->orderLines);
    }

    // ==========================================
    // Status Enum Tests
    // ==========================================

    public function test_order_status_casts_correctly(): void
    {
        $admin = $this->createAdmin();
        $this->actingAs($admin);

        $order = $this->createCoreSalesOrder([
            'status' => OrderStatus::Confirmed,
            'created_by_id' => $admin->id,
        ]);

        $this->assertInstanceOf(OrderStatus::class, $order->status);
        $this->assertEquals(OrderStatus::Confirmed, $order->status);
    }

    public function test_order_can_have_various_statuses(): void
    {
        $admin = $this->createAdmin();
        $this->actingAs($admin);

        $statuses = [
            OrderStatus::Draft,
            OrderStatus::Pending,
            OrderStatus::Confirmed,
            OrderStatus::Processing,
            OrderStatus::Shipped,
            OrderStatus::Delivered,
            OrderStatus::Completed,
            OrderStatus::Cancelled,
        ];

        foreach ($statuses as $status) {
            $order = $this->createCoreSalesOrder([
                'status' => $status,
                'created_by_id' => $admin->id,
            ]);

            $this->assertEquals($status, $order->status);
        }
    }

    // ==========================================
    // Date Cast Tests
    // ==========================================

    public function test_order_date_casts_correctly(): void
    {
        $admin = $this->createAdmin();
        $this->actingAs($admin);

        $order = $this->createCoreSalesOrder([
            'order_date' => now(),
            'expected_delivery_date' => now()->addWeek(),
            'payment_due_date' => now()->addMonth(),
            'created_by_id' => $admin->id,
        ]);

        $this->assertInstanceOf(\Carbon\Carbon::class, $order->order_date);
    }

    // ==========================================
    // Decimal Cast Tests
    // ==========================================

    public function test_amount_fields_cast_to_decimal(): void
    {
        $admin = $this->createAdmin();
        $this->actingAs($admin);

        $order = $this->createCoreSalesOrder([
            'subtotal' => 999.99,
            'tax_amount' => 49.99,
            'discount_amount' => 10.00,
            'shipping_cost' => 15.00,
            'total_amount' => 1054.98,
            'paid_amount' => 500.00,
            'created_by_id' => $admin->id,
        ]);

        // Values should be precise decimals
        $this->assertEquals('999.99', number_format($order->subtotal, 2));
        $this->assertEquals('49.99', number_format($order->tax_amount, 2));
        $this->assertEquals('10.00', number_format($order->discount_amount, 2));
    }

    // ==========================================
    // Soft Delete Tests
    // ==========================================

    public function test_order_can_be_soft_deleted(): void
    {
        $admin = $this->createAdmin();
        $this->actingAs($admin);

        $order = $this->createCoreSalesOrder(['created_by_id' => $admin->id]);
        $orderId = $order->id;

        $order->delete();

        $this->assertSoftDeleted('core_sales_orders', ['id' => $orderId]);
    }

    // ==========================================
    // Warehouse Relationship Tests
    // ==========================================

    public function test_warehouse_relationship_returns_null_when_no_warehouse(): void
    {
        $admin = $this->createAdmin();
        $this->actingAs($admin);

        $order = $this->createCoreSalesOrder([
            'warehouse_id' => null,
            'created_by_id' => $admin->id,
        ]);

        // Should not throw even with null warehouse_id
        $this->assertNull($order->warehouse);
    }

    // ==========================================
    // Accounting Transaction Relationship Tests
    // ==========================================

    public function test_accounting_transaction_relationship_returns_null_when_not_synced(): void
    {
        $admin = $this->createAdmin();
        $this->actingAs($admin);

        $order = $this->createCoreSalesOrder(['created_by_id' => $admin->id]);

        // Should return null when no transaction is linked
        if (moduleExists('AccountingCore')) {
            $this->assertNull($order->accountingTransaction);
        } else {
            $this->assertNull($order->accountingTransaction);
        }
    }
}
