<?php
declare(strict_types=1);

namespace ROITheme\Shared\Application\UseCases\SaveComponent;

use ROITheme\Shared\Domain\Entities\Component;
use ROITheme\Shared\Domain\ValueObjects\ComponentName;
use ROITheme\Shared\Domain\ValueObjects\ComponentConfiguration;
use ROITheme\Shared\Domain\ValueObjects\ComponentVisibility;
use ROITheme\Shared\Domain\Contracts\ComponentRepositoryInterface;
use ROITheme\Shared\Domain\Contracts\ValidationServiceInterface;
use ROITheme\Shared\Domain\Contracts\CacheServiceInterface;
use ROITheme\Shared\Domain\Exceptions\InvalidComponentException;

/**
 * SaveComponentUseCase - Caso de Uso para guardar componente
 *
 * RESPONSABILIDAD: Orquestar la lógica de guardar un componente
 *
 * FLUJO:
 * 1. Validar datos contra schema
 * 2. Obtener datos sanitizados del resultado de validación
 * 3. Crear entidad de dominio (Component)
 * 4. Persistir en repositorio
 * 5. Invalidar cache
 * 6. Retornar respuesta
 *
 * PRINCIPIOS APLICADOS:
 * - Single Responsibility: Solo orquesta, no tiene lógica de negocio
 * - Dependency Inversion: Depende de interfaces, no implementaciones
 * - Open/Closed: Extensible via cambio de implementaciones
 *
 * USO:
 * ```php
 * $useCase = new SaveComponentUseCase($repository, $validator, $cache);
 * $request = new SaveComponentRequest('top_bar', $data);
 * $response = $useCase->execute($request);
 *
 * if ($response->isSuccess()) {
 *     // Éxito
 * }
 * ```
 *
 * @package ROITheme\Shared\Application\UseCases\SaveComponent
 */
final class SaveComponentUseCase
{
    /**
     * @param ComponentRepositoryInterface $repository Repositorio de componentes
     * @param ValidationServiceInterface $validator Servicio de validación
     * @param CacheServiceInterface $cache Servicio de cache
     */
    public function __construct(
        private ComponentRepositoryInterface $repository,
        private ValidationServiceInterface $validator,
        private CacheServiceInterface $cache
    ) {}

    /**
     * Ejecutar Use Case
     *
     * @param SaveComponentRequest $request Datos de entrada
     * @return SaveComponentResponse Respuesta (éxito o fallo)
     */
    public function execute(SaveComponentRequest $request): SaveComponentResponse
    {
        try {
            // 1. Validar datos contra schema
            $validationResult = $this->validator->validate(
                $request->getData(),
                $request->getComponentName()
            );

            if (!$validationResult->isValid()) {
                return SaveComponentResponse::failure($validationResult->getErrors());
            }

            // 2. Obtener datos sanitizados del resultado de validación
            $sanitized = $validationResult->getSanitizedData();

            // 3. Crear entidad de dominio
            $component = $this->createComponent($request->getComponentName(), $sanitized);

            // 4. Persistir (save() retorna Component guardado)
            $savedComponent = $this->repository->save($component);

            // 5. Invalidar cache
            $this->cache->delete("component_{$request->getComponentName()}");

            // 6. Retornar respuesta exitosa
            return SaveComponentResponse::success($savedComponent->toArray());

        } catch (InvalidComponentException $e) {
            // Errores de dominio (validación de reglas de negocio)
            return SaveComponentResponse::failure([$e->getMessage()]);
        } catch (\Exception $e) {
            // Errores inesperados
            return SaveComponentResponse::failure([
                'Unexpected error: ' . $e->getMessage()
            ]);
        }
    }

    /**
     * Crear entidad Component desde datos validados
     *
     * @param string $name Nombre del componente
     * @param array $data Datos validados y sanitizados
     * @return Component
     * @throws InvalidComponentException Si los datos no cumplen invariantes
     */
    private function createComponent(string $name, array $data): Component
    {
        // Extraer o usar valores por defecto
        $isEnabled = $data['is_enabled'] ?? true;
        $schemaVersion = $data['schema_version'] ?? '1.0.0';

        // Extraer grupos de configuración (visibility, content, styles, general)
        $configData = [
            'visibility' => $data['visibility'] ?? [],
            'content' => $data['content'] ?? [],
            'styles' => $data['styles'] ?? [],
            'general' => $data['general'] ?? []
        ];

        // Los datos de visibility se manejan aparte en ComponentVisibility
        $visibilityData = $data['visibility'] ?? [];

        // Crear Value Objects
        $componentName = new ComponentName($name);
        $configuration = ComponentConfiguration::fromArray($configData);
        $visibility = ComponentVisibility::fromArray($visibilityData);

        // Crear entidad Component
        return new Component(
            $componentName,
            $configuration,
            $visibility,
            $isEnabled,
            $schemaVersion
        );
    }
}
