<?php
declare(strict_types=1);

namespace ROITheme\Shared\Application\UseCases\SyncSchema;

use ROITheme\Shared\Domain\Contracts\ComponentRepositoryInterface;
use ROITheme\Shared\Domain\Contracts\DefaultRepositoryInterface;
use ROITheme\Shared\Domain\ValueObjects\ComponentName;
use ROITheme\Shared\Domain\ValueObjects\ComponentConfiguration;

/**
 * SyncSchemaUseCase - Sincronizar schemas JSON → BD
 *
 * RESPONSABILIDAD: Orquestar sincronización de componentes desde archivo JSON
 *
 * FLUJO:
 * 1. Leer archivo JSON
 * 2. Validar estructura
 * 3. Comparar con BD (agregar/actualizar/eliminar)
 * 4. Persistir cambios
 * 5. Retornar resumen
 *
 * @package ROITheme\Shared\Application\UseCases\SyncSchema
 */
final class SyncSchemaUseCase
{
    public function __construct(
        private ComponentRepositoryInterface $componentRepository,
        private DefaultRepositoryInterface $defaultsRepository
    ) {}

    /**
     * Ejecutar sincronización
     */
    public function execute(SyncSchemaRequest $request): SyncSchemaResponse
    {
        try {
            // 1. Leer y parsear JSON
            $schemas = $this->readSchemas($request->getSchemaFilePath());

            if (empty($schemas)) {
                return SyncSchemaResponse::failure(['No schemas found in file']);
            }

            // 2. Obtener componentes actuales de BD
            $currentComponents = $this->componentRepository->findAll();
            $currentNames = array_map(
                fn($c) => $c->name()->value(),
                $currentComponents
            );
            $schemaNames = array_keys($schemas);

            // 3. Determinar cambios
            $toAdd = array_diff($schemaNames, $currentNames);
            $toUpdate = array_intersect($schemaNames, $currentNames);
            $toDelete = array_diff($currentNames, $schemaNames);

            // 4. Aplicar cambios
            $added = $this->addComponents($toAdd, $schemas);
            $updated = $this->updateComponents($toUpdate, $schemas);
            $deleted = $this->deleteComponents($toDelete);

            // 5. Retornar resumen
            return SyncSchemaResponse::success($added, $updated, $deleted);

        } catch (\Exception $e) {
            return SyncSchemaResponse::failure([$e->getMessage()]);
        }
    }

    /**
     * Leer schemas desde archivo JSON
     */
    private function readSchemas(string $filePath): array
    {
        if (!file_exists($filePath)) {
            throw new \RuntimeException("Schema file not found: {$filePath}");
        }

        $content = file_get_contents($filePath);
        $schemas = json_decode($content, true);

        if (json_last_error() !== JSON_ERROR_NONE) {
            throw new \RuntimeException('Invalid JSON: ' . json_last_error_msg());
        }

        return $schemas;
    }

    /**
     * Agregar nuevos componentes
     */
    private function addComponents(array $names, array $schemas): array
    {
        $added = [];

        foreach ($names as $name) {
            $schema = $schemas[$name];
            $componentName = new ComponentName($name);
            $configuration = ComponentConfiguration::fromArray($schema);
            $this->defaultsRepository->save($componentName, $configuration);
            $added[] = $name;
        }

        return $added;
    }

    /**
     * Actualizar componentes existentes
     */
    private function updateComponents(array $names, array $schemas): array
    {
        $updated = [];

        foreach ($names as $name) {
            $schema = $schemas[$name];
            $componentName = new ComponentName($name);
            $configuration = ComponentConfiguration::fromArray($schema);
            $this->defaultsRepository->save($componentName, $configuration);
            $updated[] = $name;
        }

        return $updated;
    }

    /**
     * Eliminar componentes obsoletos
     */
    private function deleteComponents(array $names): array
    {
        $deleted = [];

        foreach ($names as $name) {
            $this->defaultsRepository->deleteDefaults($name);
            $deleted[] = $name;
        }

        return $deleted;
    }
}
