<?php

declare(strict_types=1);

namespace Tests\Framework\LiveComponents;

use App\Framework\LiveComponents\Contracts\LiveComponentContract;
use App\Framework\LiveComponents\ValueObjects\ComponentData;
use App\Framework\LiveComponents\ValueObjects\ComponentId;
use App\Framework\LiveComponents\ValueObjects\ComponentRenderData;

/**
 * Factory for creating test components
 *
 * Simplifies component creation in tests with builder pattern.
 *
 * Usage:
 * ```php
 * // Simple component with initial state
 * $component = ComponentFactory::make()
 *     ->withId('counter:test')
 *     ->withState(['count' => 0])
 *     ->create();
 *
 * // Component with custom actions
 * $component = ComponentFactory::make()
 *     ->withId('posts:manager')
 *     ->withState(['posts' => []])
 *     ->withAction('addPost', function(string $title) {
 *         $this->state['posts'][] = $title;
 *         return ComponentData::fromArray($this->state);
 *     })
 *     ->create();
 * ```
 */
final class ComponentFactory
{
    private string $id = 'test:component';

    private array $initialState = [];

    private array $actions = [];

    private ?string $template = null;

    private function __construct()
    {
    }

    /**
     * Create new factory instance
     */
    public static function make(): self
    {
        return new self();
    }

    /**
     * Set component ID
     */
    public function withId(string $id): self
    {
        $this->id = $id;

        return $this;
    }

    /**
     * Set initial state
     *
     * @param array<string, mixed> $state Initial state data
     */
    public function withState(array $state): self
    {
        $this->initialState = $state;

        return $this;
    }

    /**
     * Add custom action
     *
     * @param string $name Action method name
     * @param callable $handler Action handler
     */
    public function withAction(string $name, callable $handler): self
    {
        $this->actions[$name] = $handler;

        return $this;
    }

    /**
     * Set template name
     */
    public function withTemplate(string $template): self
    {
        $this->template = $template;

        return $this;
    }

    /**
     * Create component instance
     */
    public function create(): LiveComponentContract
    {
        $componentId = ComponentId::fromString($this->id);
        $initialState = $this->initialState;
        $actions = $this->actions;
        $template = $this->template ?? 'test-component';

        return new class ($componentId, $initialState, $actions, $template) implements LiveComponentContract {
            private array $state;

            public function __construct(
                private ComponentId $id,
                array $initialState,
                private array $actions,
                private string $template
            ) {
                $this->state = $initialState;
            }

            public function getId(): ComponentId
            {
                return $this->id;
            }

            public function getData(): ComponentData
            {
                return ComponentData::fromArray($this->state);
            }

            public function getRenderData(): ComponentRenderData
            {
                return new ComponentRenderData($this->template, $this->state);
            }

            public function __call(string $method, array $arguments): mixed
            {
                if (isset($this->actions[$method])) {
                    // Bind action to this component instance
                    $action = $this->actions[$method]->bindTo($this, self::class);

                    return $action(...$arguments);
                }

                throw new \BadMethodCallException("Method {$method} not found on test component");
            }
        };
    }

    /**
     * Create simple counter component
     *
     * Pre-configured counter with increment/decrement/reset actions.
     */
    public static function counter(int $initialCount = 0): LiveComponentContract
    {
        return self::make()
            ->withId('counter:test')
            ->withState(['count' => $initialCount])
            ->withAction('increment', function () {
                $this->state['count']++;

                return ComponentData::fromArray($this->state);
            })
            ->withAction('decrement', function () {
                $this->state['count']--;

                return ComponentData::fromArray($this->state);
            })
            ->withAction('reset', function () {
                $this->state['count'] = 0;

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

    /**
     * Create simple list component
     *
     * Pre-configured list with add/remove actions.
     *
     * @param array<string> $initialItems Initial list items
     */
    public static function list(array $initialItems = []): LiveComponentContract
    {
        return self::make()
            ->withId('list:test')
            ->withState(['items' => $initialItems])
            ->withAction('addItem', function (string $item) {
                $this->state['items'][] = $item;

                return ComponentData::fromArray($this->state);
            })
            ->withAction('removeItem', function (int $index) {
                array_splice($this->state['items'], $index, 1);

                return ComponentData::fromArray($this->state);
            })
            ->withAction('clear', function () {
                $this->state['items'] = [];

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