<?php

namespace Tests\Unit\AccountingCore\Services;

use App\Services\Settings\ModuleSettingsService;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Modules\AccountingCore\App\Models\BasicTransaction;
use Modules\AccountingCore\App\Models\BasicTransactionCategory;
use Modules\AccountingCore\App\Services\AccountingSyncService;
use Modules\SystemCore\App\Models\CorePurchaseOrder;
use Modules\SystemCore\App\Models\CoreSalesOrder;
use Modules\SystemCore\App\Services\PostingLogService;
use Tests\TestCase;
use Tests\Traits\CreatesAccountingCoreEntities;
use Tests\Traits\CreatesSystemCoreEntities;
use Tests\Traits\CreatesUsers;

class AccountingSyncServiceTest extends TestCase
{
    use CreatesAccountingCoreEntities;
    use CreatesSystemCoreEntities;
    use CreatesUsers;
    use RefreshDatabase;

    protected AccountingSyncService $service;

    protected ModuleSettingsService $settingsService;

    protected PostingLogService $postingLogService;

    protected function setUp(): void
    {
        parent::setUp();

        $this->settingsService = app(ModuleSettingsService::class);
        $this->postingLogService = app(PostingLogService::class);
        $this->service = new AccountingSyncService($this->settingsService, $this->postingLogService);
    }

    // ==========================================
    // Sales Order Sync Tests
    // ==========================================

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

        // Enable auto-sync
        $this->settingsService->set('AccountingCore', 'auto_sync_sales_orders', true);

        $customer = $this->createCoreCustomer(['created_by_id' => $admin->id]);
        $salesOrder = CoreSalesOrder::create([
            'order_number' => 'SO-'.uniqid(),
            'customer_id' => $customer->id,
            'order_date' => now(),
            'status' => 'confirmed',
            'payment_status' => 'paid',
            'total_amount' => 5000.00,
            'user_id' => $admin->id,
            'created_by_id' => $admin->id,
        ]);

        $transaction = $this->service->syncSalesOrder($salesOrder);

        $this->assertNotNull($transaction);
        $this->assertInstanceOf(BasicTransaction::class, $transaction);
        $this->assertEquals('income', $transaction->type);
        $this->assertEquals(5000.00, $transaction->amount);
        $this->assertEquals($salesOrder->order_number, $transaction->reference_number);
    }

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

        $this->settingsService->set('AccountingCore', 'auto_sync_sales_orders', false);

        $customer = $this->createCoreCustomer(['created_by_id' => $admin->id]);
        $salesOrder = CoreSalesOrder::create([
            'order_number' => 'SO-'.uniqid(),
            'customer_id' => $customer->id,
            'order_date' => now(),
            'status' => 'confirmed',
            'payment_status' => 'paid',
            'total_amount' => 5000.00,
            'user_id' => $admin->id,
            'created_by_id' => $admin->id,
        ]);

        $transaction = $this->service->syncSalesOrder($salesOrder);

        $this->assertNull($transaction);
    }

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

        // Disable auto-sync
        $this->settingsService->set('AccountingCore', 'auto_sync_sales_orders', false);

        $customer = $this->createCoreCustomer(['created_by_id' => $admin->id]);
        $salesOrder = CoreSalesOrder::create([
            'order_number' => 'SO-'.uniqid(),
            'customer_id' => $customer->id,
            'order_date' => now(),
            'status' => 'confirmed',
            'payment_status' => 'paid',
            'total_amount' => 5000.00,
            'user_id' => $admin->id,
            'created_by_id' => $admin->id,
        ]);

        // Force sync even when auto-sync is disabled
        $transaction = $this->service->syncSalesOrder($salesOrder, true);

        $this->assertNotNull($transaction);
        $this->assertEquals('manual', $transaction->sync_status);
    }

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

        $this->settingsService->set('AccountingCore', 'auto_sync_sales_orders', true);

        $customer = $this->createCoreCustomer(['created_by_id' => $admin->id]);
        $salesOrder = CoreSalesOrder::create([
            'order_number' => 'SO-'.uniqid(),
            'customer_id' => $customer->id,
            'order_date' => now(),
            'status' => 'confirmed',
            'payment_status' => 'pending', // Not paid
            'total_amount' => 5000.00,
            'user_id' => $admin->id,
            'created_by_id' => $admin->id,
        ]);

        $transaction = $this->service->syncSalesOrder($salesOrder);

        $this->assertNull($transaction);
    }

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

        $this->settingsService->set('AccountingCore', 'auto_sync_sales_orders', true);

        $customer = $this->createCoreCustomer(['created_by_id' => $admin->id]);
        $salesOrder = CoreSalesOrder::create([
            'order_number' => 'SO-'.uniqid(),
            'customer_id' => $customer->id,
            'order_date' => now(),
            'status' => 'confirmed',
            'payment_status' => 'paid',
            'total_amount' => 5000.00,
            'user_id' => $admin->id,
            'created_by_id' => $admin->id,
        ]);

        // First sync
        $transaction1 = $this->service->syncSalesOrder($salesOrder);

        // Update order amount
        $salesOrder->update(['total_amount' => 7500.00]);

        // Second sync should update existing
        $transaction2 = $this->service->syncSalesOrder($salesOrder);

        $this->assertEquals($transaction1->id, $transaction2->id);
        $this->assertEquals(7500.00, $transaction2->amount);
    }

    // ==========================================
    // Purchase Order Sync Tests
    // ==========================================

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

        $this->settingsService->set('AccountingCore', 'auto_sync_purchase_orders', true);

        $supplier = $this->createCoreSupplier(['created_by_id' => $admin->id]);
        $purchaseOrder = CorePurchaseOrder::create([
            'order_number' => 'PO-'.uniqid(),
            'supplier_id' => $supplier->id,
            'order_date' => now(),
            'status' => 'confirmed',
            'payment_status' => 'paid',
            'total_amount' => 3000.00,
            'user_id' => $admin->id,
            'created_by_id' => $admin->id,
        ]);

        $transaction = $this->service->syncPurchaseOrder($purchaseOrder);

        $this->assertNotNull($transaction);
        $this->assertInstanceOf(BasicTransaction::class, $transaction);
        $this->assertEquals('expense', $transaction->type);
        $this->assertEquals(3000.00, $transaction->amount);
    }

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

        $this->settingsService->set('AccountingCore', 'auto_sync_purchase_orders', false);

        $supplier = $this->createCoreSupplier(['created_by_id' => $admin->id]);
        $purchaseOrder = CorePurchaseOrder::create([
            'order_number' => 'PO-'.uniqid(),
            'supplier_id' => $supplier->id,
            'order_date' => now(),
            'status' => 'confirmed',
            'payment_status' => 'paid',
            'total_amount' => 3000.00,
            'user_id' => $admin->id,
            'created_by_id' => $admin->id,
        ]);

        $transaction = $this->service->syncPurchaseOrder($purchaseOrder);

        $this->assertNull($transaction);
    }

    // ==========================================
    // Unsync Tests
    // ==========================================

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

        $this->settingsService->set('AccountingCore', 'auto_sync_sales_orders', true);

        $customer = $this->createCoreCustomer(['created_by_id' => $admin->id]);
        $salesOrder = CoreSalesOrder::create([
            'order_number' => 'SO-'.uniqid(),
            'customer_id' => $customer->id,
            'order_date' => now(),
            'status' => 'confirmed',
            'payment_status' => 'paid',
            'total_amount' => 5000.00,
            'user_id' => $admin->id,
            'created_by_id' => $admin->id,
        ]);

        $transaction = $this->service->syncSalesOrder($salesOrder);
        $transactionId = $transaction->id;

        $result = $this->service->unsyncOrder($salesOrder);

        $this->assertTrue($result);
        $this->assertSoftDeleted('basic_transactions', ['id' => $transactionId]);
    }

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

        $customer = $this->createCoreCustomer(['created_by_id' => $admin->id]);
        $salesOrder = CoreSalesOrder::create([
            'order_number' => 'SO-'.uniqid(),
            'customer_id' => $customer->id,
            'order_date' => now(),
            'status' => 'confirmed',
            'payment_status' => 'paid',
            'total_amount' => 5000.00,
            'user_id' => $admin->id,
            'created_by_id' => $admin->id,
        ]);

        $result = $this->service->unsyncOrder($salesOrder);

        $this->assertFalse($result);
    }

    // ==========================================
    // Helper Method Tests
    // ==========================================

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

        $this->settingsService->set('AccountingCore', 'auto_sync_sales_orders', true);

        $customer = $this->createCoreCustomer(['created_by_id' => $admin->id]);
        $salesOrder = CoreSalesOrder::create([
            'order_number' => 'SO-'.uniqid(),
            'customer_id' => $customer->id,
            'order_date' => now(),
            'status' => 'confirmed',
            'payment_status' => 'paid',
            'total_amount' => 5000.00,
            'user_id' => $admin->id,
            'created_by_id' => $admin->id,
        ]);

        $syncedTransaction = $this->service->syncSalesOrder($salesOrder);
        $retrievedTransaction = $this->service->getTransactionForOrder($salesOrder);

        $this->assertNotNull($retrievedTransaction);
        $this->assertEquals($syncedTransaction->id, $retrievedTransaction->id);
    }

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

        $customer = $this->createCoreCustomer(['created_by_id' => $admin->id]);
        $salesOrder = CoreSalesOrder::create([
            'order_number' => 'SO-'.uniqid(),
            'customer_id' => $customer->id,
            'order_date' => now(),
            'status' => 'confirmed',
            'payment_status' => 'paid',
            'total_amount' => 5000.00,
            'user_id' => $admin->id,
            'created_by_id' => $admin->id,
        ]);

        $transaction = $this->service->getTransactionForOrder($salesOrder);

        $this->assertNull($transaction);
    }

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

        $this->settingsService->set('AccountingCore', 'auto_sync_sales_orders', true);

        $customer = $this->createCoreCustomer(['created_by_id' => $admin->id]);
        $salesOrder = CoreSalesOrder::create([
            'order_number' => 'SO-'.uniqid(),
            'customer_id' => $customer->id,
            'order_date' => now(),
            'status' => 'confirmed',
            'payment_status' => 'paid',
            'total_amount' => 5000.00,
            'user_id' => $admin->id,
            'created_by_id' => $admin->id,
        ]);

        $this->service->syncSalesOrder($salesOrder);

        $this->assertTrue($this->service->isOrderSynced($salesOrder));
    }

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

        $customer = $this->createCoreCustomer(['created_by_id' => $admin->id]);
        $salesOrder = CoreSalesOrder::create([
            'order_number' => 'SO-'.uniqid(),
            'customer_id' => $customer->id,
            'order_date' => now(),
            'status' => 'confirmed',
            'payment_status' => 'paid',
            'total_amount' => 5000.00,
            'user_id' => $admin->id,
            'created_by_id' => $admin->id,
        ]);

        $this->assertFalse($this->service->isOrderSynced($salesOrder));
    }

    // ==========================================
    // Category Creation Tests
    // ==========================================

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

        $this->settingsService->set('AccountingCore', 'auto_sync_sales_orders', true);

        // Ensure no Sales category exists
        BasicTransactionCategory::where('name', 'Sales')->delete();

        $customer = $this->createCoreCustomer(['created_by_id' => $admin->id]);
        $salesOrder = CoreSalesOrder::create([
            'order_number' => 'SO-'.uniqid(),
            'customer_id' => $customer->id,
            'order_date' => now(),
            'status' => 'confirmed',
            'payment_status' => 'paid',
            'total_amount' => 5000.00,
            'user_id' => $admin->id,
            'created_by_id' => $admin->id,
        ]);

        $this->service->syncSalesOrder($salesOrder);

        $this->assertDatabaseHas('basic_transaction_categories', [
            'name' => 'Sales',
            'type' => 'income',
        ]);
    }

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

        $this->settingsService->set('AccountingCore', 'auto_sync_purchase_orders', true);

        // Ensure no Purchases category exists
        BasicTransactionCategory::where('name', 'Purchases')->delete();

        $supplier = $this->createCoreSupplier(['created_by_id' => $admin->id]);
        $purchaseOrder = CorePurchaseOrder::create([
            'order_number' => 'PO-'.uniqid(),
            'supplier_id' => $supplier->id,
            'order_date' => now(),
            'status' => 'confirmed',
            'payment_status' => 'paid',
            'total_amount' => 3000.00,
            'user_id' => $admin->id,
            'created_by_id' => $admin->id,
        ]);

        $this->service->syncPurchaseOrder($purchaseOrder);

        $this->assertDatabaseHas('basic_transaction_categories', [
            'name' => 'Purchases',
            'type' => 'expense',
        ]);
    }

    // ==========================================
    // Posting Log Integration Tests
    // ==========================================

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

        $this->settingsService->set('AccountingCore', 'auto_sync_sales_orders', true);
        $this->settingsService->set('SystemCore', 'enable_posting_log', true);

        $customer = $this->createCoreCustomer(['created_by_id' => $admin->id]);
        $salesOrder = CoreSalesOrder::create([
            'order_number' => 'SO-'.uniqid(),
            'customer_id' => $customer->id,
            'order_date' => now(),
            'status' => 'confirmed',
            'payment_status' => 'paid',
            'total_amount' => 5000.00,
            'user_id' => $admin->id,
            'created_by_id' => $admin->id,
        ]);

        $this->service->syncSalesOrder($salesOrder);

        $this->assertDatabaseHas('posting_logs', [
            'source_module' => 'SystemCore',
            'source_entity_type' => 'sales_order',
            'source_entity_id' => $salesOrder->id,
            'target_module' => 'AccountingCore',
            'target_entity_type' => 'transaction',
            'action' => 'create',
            'status' => 'success',
        ]);
    }
}
