<?php

use App\Models\Category;
use App\Models\Customer;
use App\Models\Supplier;
use App\Services\Concerns\CachesQuery;
use App\Services\Master\CategoryService;
use App\Services\Master\CustomerService;
use App\Services\Master\SupplierService;
use Database\Seeders\RolePermissionSeeder;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Cache;

uses(RefreshDatabase::class);

beforeEach(function () {
    $this->seed(RolePermissionSeeder::class);
});

// ─── CachesQuery Trait ─────────────────────────────────────

describe('CachesQuery Trait', function () {
    test('cacheRemember stores and retrieves value', function () {
        $service = new class
        {
            use CachesQuery;

            public function callRemember(): array
            {
                return $this->cacheRemember('test:key', 60, function () {
                    return ['foo' => 'bar'];
                });
            }
        };

        $result = $service->callRemember();
        expect($result)->toBe(['foo' => 'bar']);
        expect(Cache::has('test:key'))->toBeTrue();
    });

    test('cacheRemember returns cached value on second call', function () {
        $service = new class
        {
            use CachesQuery;

            public int $callCount = 0;

            public function callRemember(): array
            {
                return $this->cacheRemember('test:counter', 60, function () {
                    $this->callCount++;

                    return ['count' => $this->callCount];
                });
            }
        };

        $first = $service->callRemember();
        $second = $service->callRemember();

        expect($first)->toBe(['count' => 1]);
        expect($second)->toBe(['count' => 1]);
        expect($service->callCount)->toBe(1);
    });

    test('cacheForget removes cached value', function () {
        $service = new class
        {
            use CachesQuery;

            public function callRemember(): array
            {
                return $this->cacheRemember('test:forget', 60, function () {
                    return ['data' => true];
                });
            }

            public function callForget(): void
            {
                $this->cacheForget('test:forget');
            }
        };

        $service->callRemember();
        expect(Cache::has('test:forget'))->toBeTrue();

        $service->callForget();
        expect(Cache::has('test:forget'))->toBeFalse();
    });

    test('cacheForever stores value without expiration', function () {
        $service = new class
        {
            use CachesQuery;

            public function callForever(): string
            {
                return $this->cacheForever('test:forever', function () {
                    return 'permanent';
                });
            }
        };

        $result = $service->callForever();
        expect($result)->toBe('permanent');
        expect(Cache::has('test:forever'))->toBeTrue();
    });
});

// ─── Category Cache ────────────────────────────────────────

describe('Category Caching', function () {
    test('getSelectOptions caches results', function () {
        Category::factory()->count(3)->create();

        $service = app(CategoryService::class);

        $first = $service->getSelectOptions();
        expect($first)->toHaveCount(3);
        expect(Cache::has('master:categories:options'))->toBeTrue();

        Category::create(['name' => 'New Category']);

        $second = $service->getSelectOptions();
        expect($second)->toHaveCount(3);

        Cache::forget('master:categories:options');
        $third = $service->getSelectOptions();
        expect($third)->toHaveCount(4);
    });

    test('create invalidates category cache', function () {
        $service = app(CategoryService::class);

        $service->getSelectOptions();
        expect(Cache::has('master:categories:options'))->toBeTrue();

        $service->create(['name' => 'Test']);
        expect(Cache::has('master:categories:options'))->toBeFalse();
    });

    test('update invalidates category cache', function () {
        $category = Category::factory()->create();
        $service = app(CategoryService::class);

        $service->getSelectOptions();
        expect(Cache::has('master:categories:options'))->toBeTrue();

        $service->update($category, ['name' => 'Updated']);
        expect(Cache::has('master:categories:options'))->toBeFalse();
    });

    test('delete invalidates category cache', function () {
        $category = Category::factory()->create();
        $service = app(CategoryService::class);

        $service->getSelectOptions();
        expect(Cache::has('master:categories:options'))->toBeTrue();

        $service->delete($category);
        expect(Cache::has('master:categories:options'))->toBeFalse();
    });
});

// ─── Supplier Cache ────────────────────────────────────────

describe('Supplier Caching', function () {
    test('getSelectOptions caches results', function () {
        Supplier::factory()->count(3)->create();

        $service = app(SupplierService::class);

        $first = $service->getSelectOptions();
        expect($first)->toHaveCount(3);
        expect(Cache::has('master:suppliers:options'))->toBeTrue();

        Cache::forget('master:suppliers:options');
        $second = $service->getSelectOptions();
        expect($second)->toHaveCount(3);
    });

    test('create invalidates supplier cache', function () {
        $service = app(SupplierService::class);

        $service->getSelectOptions();
        expect(Cache::has('master:suppliers:options'))->toBeTrue();

        $service->create(['name' => 'Test Supplier', 'phone_number' => '08123', 'address' => 'Jl. Test']);
        expect(Cache::has('master:suppliers:options'))->toBeFalse();
    });

    test('update invalidates supplier cache', function () {
        $supplier = Supplier::factory()->create();
        $service = app(SupplierService::class);

        $service->getSelectOptions();
        expect(Cache::has('master:suppliers:options'))->toBeTrue();

        $service->update($supplier, ['name' => 'Updated']);
        expect(Cache::has('master:suppliers:options'))->toBeFalse();
    });

    test('delete invalidates supplier cache', function () {
        $supplier = Supplier::factory()->create();
        $service = app(SupplierService::class);

        $service->getSelectOptions();
        expect(Cache::has('master:suppliers:options'))->toBeTrue();

        $service->delete($supplier);
        expect(Cache::has('master:suppliers:options'))->toBeFalse();
    });
});

// ─── Customer Cache ────────────────────────────────────────

describe('Customer Caching', function () {
    test('getSelectOptions caches results', function () {
        Customer::factory()->count(3)->create();

        $service = app(CustomerService::class);

        $first = $service->getSelectOptions();
        expect($first)->toHaveCount(3);
        expect(Cache::has('master:customers:options'))->toBeTrue();

        Cache::forget('master:customers:options');
        $second = $service->getSelectOptions();
        expect($second)->toHaveCount(3);
    });

    test('create invalidates customer cache', function () {
        $service = app(CustomerService::class);

        $service->getSelectOptions();
        expect(Cache::has('master:customers:options'))->toBeTrue();

        $service->create(['name' => 'Test Customer', 'phone_number' => '08123', 'address' => 'Jl. Test']);
        expect(Cache::has('master:customers:options'))->toBeFalse();
    });

    test('createAndReturn invalidates customer cache', function () {
        $service = app(CustomerService::class);

        $service->getSelectOptions();
        expect(Cache::has('master:customers:options'))->toBeTrue();

        $customer = $service->createAndReturn(['name' => 'Test', 'phone_number' => '08123', 'address' => 'Jl. Test']);
        expect($customer)->toBeInstanceOf(Customer::class);
        expect(Cache::has('master:customers:options'))->toBeFalse();
    });

    test('update invalidates customer cache', function () {
        $customer = Customer::factory()->create();
        $service = app(CustomerService::class);

        $service->getSelectOptions();
        expect(Cache::has('master:customers:options'))->toBeTrue();

        $service->update($customer, ['name' => 'Updated']);
        expect(Cache::has('master:customers:options'))->toBeFalse();
    });

    test('delete invalidates customer cache', function () {
        $customer = Customer::factory()->create();
        $service = app(CustomerService::class);

        $service->getSelectOptions();
        expect(Cache::has('master:customers:options'))->toBeTrue();

        $service->delete($customer);
        expect(Cache::has('master:customers:options'))->toBeFalse();
    });
});

// ─── Cache Key Isolation ───────────────────────────────────

describe('Cache Key Isolation', function () {
    test('different services use different cache keys', function () {
        Category::factory()->count(2)->create();
        Supplier::factory()->count(3)->create();

        app(CategoryService::class)->getSelectOptions();
        app(SupplierService::class)->getSelectOptions();

        expect(Cache::has('master:categories:options'))->toBeTrue();
        expect(Cache::has('master:suppliers:options'))->toBeTrue();

        Cache::forget('master:categories:options');

        expect(Cache::has('master:categories:options'))->toBeFalse();
        expect(Cache::has('master:suppliers:options'))->toBeTrue();
    });
});
