<?php

namespace Tests\Unit\AccountingCore\Models;

use Illuminate\Foundation\Testing\RefreshDatabase;
use Modules\AccountingCore\App\Models\BasicTransactionCategory;
use Tests\TestCase;
use Tests\Traits\CreatesAccountingCoreEntities;
use Tests\Traits\CreatesUsers;

class BasicTransactionCategoryTest extends TestCase
{
    use CreatesAccountingCoreEntities;
    use CreatesUsers;
    use RefreshDatabase;

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

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

        $category = BasicTransactionCategory::create([
            'name' => 'Test Category',
            'type' => 'income',
            'is_active' => true,
            'created_by_id' => $admin->id,
        ]);

        $this->assertNotNull($category->id);
        $this->assertEquals('Test Category', $category->name);
        $this->assertEquals('income', $category->type);
        $this->assertTrue($category->is_active);
    }

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

        $category = $this->createIncomeCategory(['created_by_id' => $admin->id]);

        $this->assertEquals('income', $category->type);
    }

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

        $category = $this->createExpenseCategory(['created_by_id' => $admin->id]);

        $this->assertEquals('expense', $category->type);
    }

    // ==========================================
    // Casting Tests
    // ==========================================

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

        $category = $this->createTransactionCategory([
            'is_active' => 1,
            'created_by_id' => $admin->id,
        ]);

        $this->assertIsBool($category->is_active);
        $this->assertTrue($category->is_active);
    }

    // ==========================================
    // Scope Tests
    // ==========================================

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

        $activeCategory = $this->createTransactionCategory([
            'is_active' => true,
            'created_by_id' => $admin->id,
        ]);
        $inactiveCategory = $this->createTransactionCategory([
            'is_active' => false,
            'created_by_id' => $admin->id,
        ]);

        $categories = BasicTransactionCategory::active()->get();

        $this->assertTrue($categories->contains($activeCategory));
        $this->assertFalse($categories->contains($inactiveCategory));
    }

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

        $incomeCategory = $this->createIncomeCategory(['created_by_id' => $admin->id]);
        $expenseCategory = $this->createExpenseCategory(['created_by_id' => $admin->id]);

        $categories = BasicTransactionCategory::byType('income')->get();

        $this->assertTrue($categories->contains($incomeCategory));
        $this->assertFalse($categories->contains($expenseCategory));
    }

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

        $incomeCategory = $this->createIncomeCategory(['created_by_id' => $admin->id]);
        $expenseCategory = $this->createExpenseCategory(['created_by_id' => $admin->id]);

        $categories = BasicTransactionCategory::byType('expense')->get();

        $this->assertTrue($categories->contains($expenseCategory));
        $this->assertFalse($categories->contains($incomeCategory));
    }

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

        $parentCategory = $this->createTransactionCategory([
            'parent_id' => null,
            'created_by_id' => $admin->id,
        ]);
        $childCategory = $this->createTransactionCategory([
            'parent_id' => $parentCategory->id,
            'created_by_id' => $admin->id,
        ]);

        $parents = BasicTransactionCategory::parents()->get();

        $this->assertTrue($parents->contains($parentCategory));
        $this->assertFalse($parents->contains($childCategory));
    }

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

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

        $parentCategory = $this->createTransactionCategory(['created_by_id' => $admin->id]);
        $childCategory = $this->createTransactionCategory([
            'parent_id' => $parentCategory->id,
            'created_by_id' => $admin->id,
        ]);

        $this->assertInstanceOf(BasicTransactionCategory::class, $childCategory->parent);
        $this->assertEquals($parentCategory->id, $childCategory->parent->id);
    }

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

        $parentCategory = $this->createTransactionCategory(['created_by_id' => $admin->id]);
        $child1 = $this->createTransactionCategory([
            'parent_id' => $parentCategory->id,
            'created_by_id' => $admin->id,
        ]);
        $child2 = $this->createTransactionCategory([
            'parent_id' => $parentCategory->id,
            'created_by_id' => $admin->id,
        ]);

        $children = $parentCategory->children;

        $this->assertCount(2, $children);
        $this->assertTrue($children->contains($child1));
        $this->assertTrue($children->contains($child2));
    }

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

        $category = $this->createIncomeCategory(['created_by_id' => $admin->id]);
        $transaction1 = $this->createIncomeTransaction([
            'category_id' => $category->id,
            'created_by_id' => $admin->id,
        ]);
        $transaction2 = $this->createIncomeTransaction([
            'category_id' => $category->id,
            'created_by_id' => $admin->id,
        ]);

        $transactions = $category->transactions;

        $this->assertCount(2, $transactions);
        $this->assertTrue($transactions->contains($transaction1));
        $this->assertTrue($transactions->contains($transaction2));
    }

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

        $parent = $this->createTransactionCategory(['name' => 'Parent', 'created_by_id' => $admin->id]);
        $child = $this->createTransactionCategory([
            'name' => 'Child',
            'parent_id' => $parent->id,
            'created_by_id' => $admin->id,
        ]);
        $grandchild = $this->createTransactionCategory([
            'name' => 'Grandchild',
            'parent_id' => $child->id,
            'created_by_id' => $admin->id,
        ]);

        $descendants = $parent->descendants;

        $this->assertCount(1, $descendants);
        $this->assertEquals('Child', $descendants->first()->name);
        $this->assertCount(1, $descendants->first()->descendants);
        $this->assertEquals('Grandchild', $descendants->first()->descendants->first()->name);
    }

    // ==========================================
    // Accessor Tests
    // ==========================================

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

        $category = $this->createTransactionCategory([
            'name' => 'Sales',
            'created_by_id' => $admin->id,
        ]);

        $this->assertEquals('Sales', $category->full_path);
    }

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

        $parent = $this->createTransactionCategory([
            'name' => 'Income',
            'created_by_id' => $admin->id,
        ]);
        $child = $this->createTransactionCategory([
            'name' => 'Sales',
            'parent_id' => $parent->id,
            'created_by_id' => $admin->id,
        ]);
        $grandchild = $this->createTransactionCategory([
            'name' => 'Online Sales',
            'parent_id' => $child->id,
            'created_by_id' => $admin->id,
        ]);

        $this->assertEquals('Income > Sales > Online Sales', $grandchild->full_path);
    }

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

        $category = $this->createTransactionCategory([
            'name' => 'Sales',
            'icon' => 'bx bx-dollar',
            'created_by_id' => $admin->id,
        ]);

        $display = $category->display_with_icon;

        $this->assertStringContainsString('bx bx-dollar', $display);
        $this->assertStringContainsString('Sales', $display);
    }

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

        $category = $this->createTransactionCategory([
            'name' => 'Sales',
            'icon' => null,
            'created_by_id' => $admin->id,
        ]);

        $this->assertEquals('Sales', $category->display_with_icon);
    }

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

        $category = $this->createIncomeCategory(['created_by_id' => $admin->id]);

        $badge = $category->badge_html;

        $this->assertStringContainsString('bg-label-success', $badge);
        $this->assertStringContainsString('Income', $badge);
    }

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

        $category = $this->createExpenseCategory(['created_by_id' => $admin->id]);

        $badge = $category->badge_html;

        $this->assertStringContainsString('bg-label-danger', $badge);
        $this->assertStringContainsString('Expense', $badge);
    }

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

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

        $parent = $this->createTransactionCategory(['created_by_id' => $admin->id]);
        $this->createTransactionCategory([
            'parent_id' => $parent->id,
            'created_by_id' => $admin->id,
        ]);

        $this->assertTrue($parent->hasChildren());
    }

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

        $category = $this->createTransactionCategory(['created_by_id' => $admin->id]);

        $this->assertFalse($category->hasChildren());
    }

    public function test_get_default_categories_returns_array(): void
    {
        $defaults = BasicTransactionCategory::getDefaultCategories();

        $this->assertIsArray($defaults);
        $this->assertNotEmpty($defaults);

        // Check for expected categories
        $names = array_column($defaults, 'name');
        $this->assertContains('Sales', $names);
        $this->assertContains('Salaries', $names);
    }

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

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

        $category = $this->createTransactionCategory(['created_by_id' => $admin->id]);
        $categoryId = $category->id;

        $category->delete();

        $this->assertSoftDeleted('basic_transaction_categories', ['id' => $categoryId]);
    }

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

        $category = $this->createTransactionCategory(['created_by_id' => $admin->id]);
        $category->delete();
        $category->restore();

        $this->assertDatabaseHas('basic_transaction_categories', [
            'id' => $category->id,
            'deleted_at' => null,
        ]);
    }
}
