<?php
declare(strict_types=1);

namespace ROITheme\Shared\Infrastructure\Persistence\WordPress;

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\Exceptions\ComponentNotFoundException;

/**
 * WordPressComponentRepository - Implementación con WordPress/MySQL
 *
 * RESPONSABILIDAD: Persistir y recuperar componentes desde wp_roi_theme_components
 *
 * DEPENDENCIAS EXTERNAS:
 * - wpdb (WordPress database abstraction)
 * - Tabla wp_roi_theme_components (creada en Fase 2)
 *
 * CONVERSIÓN:
 * - Entity → Array → MySQL (al guardar)
 * - MySQL → Array → Entity (al recuperar)
 *
 * @package ROITheme\Infrastructure\Persistence\WordPress
 */
final class WordPressComponentRepository implements ComponentRepositoryInterface
{
    private string $tableName;

    /**
     * @param \wpdb $wpdb WordPress database object
     */
    public function __construct(
        private \wpdb $wpdb
    ) {
        $this->tableName = $this->wpdb->prefix . 'roi_theme_components';
    }

    /**
     * Guardar componente
     *
     * INSERT si no existe, UPDATE si existe
     *
     * @param Component $component
     * @return Component Componente guardado (con timestamps actualizados)
     */
    public function save(Component $component): Component
    {
        $componentName = $component->name()->value();

        // Verificar si ya existe
        $existing = $this->findByName($componentName);

        $data = [
            'component_name' => $componentName,
            'configuration' => json_encode($component->configuration()->toArray()),
            'visibility' => json_encode($component->visibility()->toArray()),
            'is_enabled' => $component->isEnabled() ? 1 : 0,
            'schema_version' => $component->schemaVersion(),
            'updated_at' => current_time('mysql')
        ];

        if ($existing === null) {
            // INSERT
            $data['created_at'] = current_time('mysql');

            $this->wpdb->insert(
                $this->tableName,
                $data,
                ['%s', '%s', '%s', '%d', '%s', '%s', '%s']
            );
        } else {
            // UPDATE
            $this->wpdb->update(
                $this->tableName,
                $data,
                ['component_name' => $componentName],
                ['%s', '%s', '%s', '%d', '%s', '%s'],
                ['%s']
            );
        }

        // Return the saved component by fetching it from the database
        return $this->getByName($component->name());
    }

    /**
     * Buscar componente por nombre
     *
     * @param ComponentName $name Nombre del componente
     * @return Component|null Null si no existe
     */
    public function findByName(ComponentName $name): ?Component
    {
        $sql = $this->wpdb->prepare(
            "SELECT * FROM {$this->tableName} WHERE component_name = %s LIMIT 1",
            $name->value()
        );

        $row = $this->wpdb->get_row($sql, ARRAY_A);

        if ($row === null) {
            return null;
        }

        return $this->rowToEntity($row);
    }

    /**
     * Obtener componente por nombre (lanza excepción si no existe)
     *
     * @param ComponentName $name
     * @return Component
     * @throws ComponentNotFoundException
     */
    public function getByName(ComponentName $name): Component
    {
        $component = $this->findByName($name);

        if ($component === null) {
            throw ComponentNotFoundException::withName($name->value());
        }

        return $component;
    }

    /**
     * Obtener todos los componentes
     *
     * @return Component[]
     */
    public function findAll(): array
    {
        $sql = "SELECT * FROM {$this->tableName} ORDER BY component_name ASC";
        $rows = $this->wpdb->get_results($sql, ARRAY_A);

        if (empty($rows)) {
            return [];
        }

        return array_map(
            fn($row) => $this->rowToEntity($row),
            $rows
        );
    }

    /**
     * Eliminar componente
     *
     * @param ComponentName $name Nombre del componente
     * @return bool True si eliminó exitosamente
     */
    public function delete(ComponentName $name): bool
    {
        $result = $this->wpdb->delete(
            $this->tableName,
            ['component_name' => $name->value()],
            ['%s']
        );

        return $result !== false;
    }

    /**
     * Obtener componentes habilitados
     *
     * @return Component[]
     */
    public function findEnabled(): array
    {
        $sql = "SELECT * FROM {$this->tableName} WHERE is_enabled = 1 ORDER BY component_name ASC";
        $rows = $this->wpdb->get_results($sql, ARRAY_A);

        if (empty($rows)) {
            return [];
        }

        return array_map(
            fn($row) => $this->rowToEntity($row),
            $rows
        );
    }

    /**
     * Verificar si existe un componente con el nombre dado
     *
     * @param ComponentName $name
     * @return bool
     */
    public function exists(ComponentName $name): bool
    {
        return $this->findByName($name) !== null;
    }

    /**
     * Obtener cantidad total de componentes
     *
     * @return int
     */
    public function count(): int
    {
        $sql = "SELECT COUNT(*) FROM {$this->tableName}";
        return (int) $this->wpdb->get_var($sql);
    }

    /**
     * Obtener componentes por grupo de configuración
     *
     * @param string $group Grupo de configuración (visibility, content, styles, general)
     * @return Component[]
     */
    public function findByConfigGroup(string $group): array
    {
        // For now, return all components as we don't have a specific column for groups
        // This would require additional schema design
        return $this->findAll();
    }

    /**
     * Convertir fila de BD a Entity
     *
     * @param array $row Fila de la base de datos
     * @return Component
     */
    private function rowToEntity(array $row): Component
    {
        // Decodificar JSON
        $configuration = json_decode($row['configuration'], true) ?? [];
        $visibility = json_decode($row['visibility'], true) ?? [];

        // Crear Value Objects
        $name = new ComponentName($row['component_name']);
        $config = ComponentConfiguration::fromArray($configuration);
        $vis = ComponentVisibility::fromArray($visibility);

        // Crear Entity
        return new Component(
            $name,
            $config,
            $vis,
            (bool) $row['is_enabled'],
            $row['schema_version']
        );
    }
}
