<?php

declare(strict_types=1);

use App\Framework\LiveComponents\Attributes\RequiresPermission;
use App\Framework\LiveComponents\ValueObjects\ComponentData;
use Tests\Framework\LiveComponents\ComponentFactory;
use Tests\Framework\LiveComponents\ComponentTestCase;

// Use ComponentTestCase trait for all helper methods
uses(ComponentTestCase::class);

// Setup before each test
beforeEach(function () {
    $this->setUpComponentTest();
});

describe('Test Harness - ComponentFactory', function () {
    it('creates simple counter component', function () {
        $component = ComponentFactory::counter(initialCount: 5);

        expect($component->getData()->toArray())->toBe(['count' => 5]);
    });

    it('creates list component', function () {
        $component = ComponentFactory::list(['item1', 'item2']);

        $data = $component->getData()->toArray();
        expect($data['items'])->toHaveCount(2);
    });

    it('creates custom component with builder', function () {
        $component = ComponentFactory::make()
            ->withId('posts:manager')
            ->withState(['posts' => [], 'count' => 0])
            ->withAction('addPost', function (string $title) {
                $this->state['posts'][] = $title;
                $this->state['count']++;

                return ComponentData::fromArray($this->state);
            })
            ->create();

        expect($component->getId()->toString())->toBe('posts:manager');
        expect($component->getData()->toArray())->toBe(['posts' => [], 'count' => 0]);
    });
});

describe('Test Harness - Action Execution', function () {
    it('executes action successfully', function () {
        $component = ComponentFactory::counter();

        $result = $this->callAction($component, 'increment');

        expect($result->state->data['count'])->toBe(1);
    });

    it('executes action with parameters', function () {
        $component = ComponentFactory::list();

        $result = $this->callAction($component, 'addItem', ['item' => 'New Task']);

        expect($result->state->data['items'])->toContain('New Task');
    });

    it('handles multiple actions in sequence', function () {
        $component = ComponentFactory::counter();

        $result1 = $this->callAction($component, 'increment');
        $result2 = $this->callAction($component, 'increment');
        $result3 = $this->callAction($component, 'decrement');

        // Note: Component state is immutable, so we need to track manually
        // In real app, component would be re-hydrated with new state
        expect($result1->state->data['count'])->toBe(1);
        expect($result2->state->data['count'])->toBe(2);
        expect($result3->state->data['count'])->toBe(1);
    });
});

describe('Test Harness - Action Assertions', function () {
    it('asserts action executes', function () {
        $component = ComponentFactory::counter();

        $result = $this->assertActionExecutes($component, 'increment');

        expect($result->state->data['count'])->toBe(1);
    });

    it('asserts action throws exception', function () {
        $component = ComponentFactory::make()
            ->withId('error:component')
            ->withState(['data' => 'test'])
            ->withAction('fail', function () {
                throw new \RuntimeException('Expected error');
            })
            ->create();

        $this->assertActionThrows($component, 'fail', \RuntimeException::class);
    });
});

describe('Test Harness - State Assertions', function () {
    it('asserts state equals expected values', function () {
        $component = ComponentFactory::counter(10);

        $result = $this->callAction($component, 'increment');

        $this->assertStateEquals($result, ['count' => 11]);
    });

    it('asserts state has key', function () {
        $component = ComponentFactory::list(['item1']);

        $result = $this->callAction($component, 'addItem', ['item' => 'item2']);

        $this->assertStateHas($result, 'items');
    });

    it('gets state value', function () {
        $component = ComponentFactory::counter(5);

        $result = $this->callAction($component, 'increment');
        $count = $this->getStateValue($result, 'count');

        expect($count)->toBe(6);
    });
});

describe('Test Harness - Authorization', function () {
    it('authenticates user with permissions', function () {
        $this->actingAs(['posts.edit', 'posts.delete']);

        expect($this->session->get('user'))->toHaveKey('permissions');
        expect($this->session->get('user')['permissions'])->toBe(['posts.edit', 'posts.delete']);
    });

    it('asserts action requires authentication', function () {
        // Note: Attributes on closures are not supported for magic methods via __call()
        // For authorization testing, use real component classes instead of ComponentFactory
        $this->markTestSkipped('Authorization attributes on closures require real component classes');

        $component = ComponentFactory::make()
            ->withId('protected:component')
            ->withState(['data' => 'secret'])
            ->withAction(
                'protectedAction',
                #[RequiresPermission('admin.access')]
                function () {
                    return ComponentData::fromArray($this->state);
                }
            )
            ->create();

        // Without authentication, should throw
        $this->assertActionRequiresAuth($component, 'protectedAction');
    })->skip('Authorization attributes not supported on closures');

    it('executes protected action with correct permission', function () {
        // Note: Same limitation as above - closures don't support attributes for authorization
        $this->markTestSkipped('Authorization attributes on closures require real component classes');

        $component = ComponentFactory::make()
            ->withId('protected:component')
            ->withState(['data' => 'secret'])
            ->withAction(
                'protectedAction',
                #[RequiresPermission('admin.access')]
                function () {
                    return ComponentData::fromArray($this->state);
                }
            )
            ->create();

        $this->actingAs(['admin.access']);

        $result = $this->assertActionExecutes($component, 'protectedAction');
        expect($result->state->data['data'])->toBe('secret');
    })->skip('Authorization attributes not supported on closures');
});

describe('Test Harness - State Validation', function () {
    it('validates state after action', function () {
        $component = ComponentFactory::counter();

        $result = $this->callAction($component, 'increment');

        $this->assertStateValidates($result);
    });

    it('ensures state consistency', function () {
        $component = ComponentFactory::list(['a', 'b', 'c']);

        $result = $this->callAction($component, 'removeItem', ['index' => 1]);

        // State should still be valid array structure
        $this->assertStateValidates($result);
        expect($result->state->data['items'])->toHaveCount(2);
    });
});

describe('Test Harness - Event Assertions', function () {
    it('asserts no events dispatched by default', function () {
        $component = ComponentFactory::counter();

        $result = $this->callAction($component, 'increment');

        $this->assertNoEventsDispatched($result);
    });

    it('asserts event count', function () {
        $component = ComponentFactory::counter();

        $result = $this->callAction($component, 'increment');

        $this->assertEventCount($result, 0);
    });
});
