<?php
declare(strict_types=1);

namespace ROITheme\Shared\Infrastructure\Api\WordPress;

use ROITheme\Shared\Infrastructure\Di\DIContainer;

/**
 * WP-CLI Command para Sincronización de Schemas
 *
 * Responsabilidad: Sincronizar schemas JSON a base de datos
 *
 * COMANDOS DISPONIBLES:
 * - wp roi-theme sync-component [nombre]  : Sincronizar un componente específico
 * - wp roi-theme sync-all-components      : Sincronizar todos los componentes
 *
 * USO:
 * ```bash
 * # Sincronizar un componente específico
 * wp roi-theme sync-component top-notification-bar
 *
 * # Sincronizar todos los componentes
 * wp roi-theme sync-all-components
 * ```
 *
 * IMPORTANTE:
 * - Si un campo NO existe en BD → INSERT con valor default del JSON
 * - Si un campo YA existe → PRESERVA el valor del usuario, solo actualiza is_editable
 */
final class MigrationCommand
{
    /**
     * Sincronizar un componente específico desde su schema JSON a la BD
     *
     * ## OPCIONES
     *
     * <component_name>
     * : Nombre del componente a sincronizar (sin extensión .json)
     *
     * ## EJEMPLOS
     *
     *     # Sincronizar top-notification-bar
     *     wp roi-theme sync-component top-notification-bar
     *
     *     # Sincronizar navbar
     *     wp roi-theme sync-component navbar
     *
     * @param array $args Argumentos posicionales
     * @param array $assoc_args Argumentos asociativos (--flags)
     * @return void
     */
    public function sync_component(array $args, array $assoc_args): void
    {
        if (empty($args[0])) {
            \WP_CLI::error('Debes especificar el nombre del componente. Ejemplo: wp roi-theme sync-component top-notification-bar');
            return;
        }

        $component_name = $args[0];
        $schemas_path = get_template_directory() . '/Schemas';
        $schema_file = $schemas_path . '/' . $component_name . '.json';

        if (!file_exists($schema_file)) {
            \WP_CLI::error("Schema no encontrado: {$schema_file}");
            return;
        }

        \WP_CLI::line('');
        \WP_CLI::line("🔄 Sincronizando componente: {$component_name}");
        \WP_CLI::line('');

        $result = $this->syncSchemaToDatabase($schema_file, $component_name);

        \WP_CLI::line('');
        if ($result['success']) {
            \WP_CLI::success($result['message']);
            \WP_CLI::line('');
            \WP_CLI::line('📊 Estadísticas:');
            \WP_CLI::line('   ├─ Campos insertados: ' . $result['stats']['inserted']);
            \WP_CLI::line('   ├─ Campos actualizados: ' . $result['stats']['updated']);
            \WP_CLI::line('   ├─ Campos eliminados (obsoletos): ' . $result['stats']['deleted']);
            \WP_CLI::line('   └─ Total en schema: ' . $result['stats']['total']);
            \WP_CLI::line('');
        } else {
            \WP_CLI::error($result['message']);
        }
    }

    /**
     * Sincronizar todos los componentes desde schemas/ a la BD
     *
     * ## EJEMPLOS
     *
     *     wp roi-theme sync-all-components
     *
     * @param array $args Argumentos posicionales
     * @param array $assoc_args Argumentos asociativos
     * @return void
     */
    public function sync_all_components(array $args, array $assoc_args): void
    {
        $schemas_path = get_template_directory() . '/Schemas';

        if (!is_dir($schemas_path)) {
            \WP_CLI::error("Directorio de schemas no encontrado: {$schemas_path}");
            return;
        }

        $schema_files = glob($schemas_path . '/*.json');

        if (empty($schema_files)) {
            \WP_CLI::warning('No se encontraron schemas JSON en ' . $schemas_path);
            return;
        }

        \WP_CLI::line('');
        \WP_CLI::line('🔄 Sincronizando todos los componentes...');
        \WP_CLI::line('');

        $total_stats = [
            'components' => 0,
            'inserted' => 0,
            'updated' => 0,
            'total' => 0,
            'errors' => 0
        ];

        foreach ($schema_files as $schema_file) {
            $component_name = basename($schema_file, '.json');

            \WP_CLI::line("   → Procesando: {$component_name}");

            $result = $this->syncSchemaToDatabase($schema_file, $component_name);

            if ($result['success']) {
                $total_stats['components']++;
                $total_stats['inserted'] += $result['stats']['inserted'];
                $total_stats['updated'] += $result['stats']['updated'];
                $total_stats['total'] += $result['stats']['total'];
                \WP_CLI::line("     ✓ {$result['message']}");
            } else {
                $total_stats['errors']++;
                \WP_CLI::line("     ✗ Error: {$result['message']}");
            }
        }

        \WP_CLI::line('');
        \WP_CLI::success('Sincronización completada');
        \WP_CLI::line('');
        \WP_CLI::line('📊 Estadísticas totales:');
        \WP_CLI::line('   ├─ Componentes sincronizados: ' . $total_stats['components']);
        \WP_CLI::line('   ├─ Campos insertados: ' . $total_stats['inserted']);
        \WP_CLI::line('   ├─ Campos actualizados: ' . $total_stats['updated']);
        \WP_CLI::line('   ├─ Total procesado: ' . $total_stats['total']);
        \WP_CLI::line('   └─ Errores: ' . $total_stats['errors']);
        \WP_CLI::line('');
    }

    /**
     * Sincronizar un schema JSON a la base de datos
     *
     * @param string $schema_file Ruta completa al archivo JSON
     * @param string $component_name Nombre del componente
     * @return array{success: bool, message: string, stats: array{inserted: int, updated: int, deleted: int, total: int}}
     */
    private function syncSchemaToDatabase(string $schema_file, string $component_name): array
    {
        global $wpdb;

        // Leer y decodificar JSON
        $json_content = file_get_contents($schema_file);
        if ($json_content === false) {
            return [
                'success' => false,
                'message' => 'Error al leer archivo JSON',
                'stats' => ['inserted' => 0, 'updated' => 0, 'deleted' => 0, 'total' => 0]
            ];
        }

        $schema = json_decode($json_content, true);
        if ($schema === null) {
            return [
                'success' => false,
                'message' => 'Error al decodificar JSON: ' . json_last_error_msg(),
                'stats' => ['inserted' => 0, 'updated' => 0, 'deleted' => 0, 'total' => 0]
            ];
        }

        $table = $wpdb->prefix . 'roi_theme_component_settings';
        $stats = ['inserted' => 0, 'updated' => 0, 'deleted' => 0, 'total' => 0];

        // Iterar grupos y campos
        if (!isset($schema['groups']) || !is_array($schema['groups'])) {
            return [
                'success' => false,
                'message' => 'Schema inválido: falta clave "groups"',
                'stats' => $stats
            ];
        }

        // Construir lista de campos válidos del schema
        $validFields = [];
        foreach ($schema['groups'] as $group_name => $group_data) {
            if (!isset($group_data['fields']) || !is_array($group_data['fields'])) {
                continue;
            }
            foreach ($group_data['fields'] as $attribute_name => $field_config) {
                $validFields[] = $group_name . '::' . $attribute_name;
            }
        }

        // PASO 1: Eliminar registros obsoletos (que no están en el schema)
        $existing_records = $wpdb->get_results($wpdb->prepare(
            "SELECT id, group_name, attribute_name FROM {$table} WHERE component_name = %s",
            $component_name
        ));

        foreach ($existing_records as $record) {
            $key = $record->group_name . '::' . $record->attribute_name;
            if (!in_array($key, $validFields, true)) {
                $wpdb->delete($table, ['id' => $record->id], ['%d']);
                $stats['deleted']++;
            }
        }

        // PASO 2: Insertar/Actualizar campos del schema
        foreach ($schema['groups'] as $group_name => $group_data) {
            if (!isset($group_data['fields']) || !is_array($group_data['fields'])) {
                continue;
            }

            foreach ($group_data['fields'] as $attribute_name => $field_config) {
                $stats['total']++;

                // Verificar si el campo ya existe
                $existing = $wpdb->get_row($wpdb->prepare(
                    "SELECT id, attribute_value FROM {$table}
                     WHERE component_name = %s
                     AND group_name = %s
                     AND attribute_name = %s",
                    $component_name,
                    $group_name,
                    $attribute_name
                ));

                $is_editable = isset($field_config['editable']) ? (bool)$field_config['editable'] : false;
                $default_value = isset($field_config['default']) ? $field_config['default'] : '';

                // Convertir valor a string para almacenamiento
                if (is_array($default_value) || is_object($default_value)) {
                    $default_value = json_encode($default_value);
                } elseif (is_bool($default_value)) {
                    $default_value = $default_value ? '1' : '0';
                } else {
                    $default_value = (string)$default_value;
                }

                if ($existing === null) {
                    // INSERT: Campo nuevo, usar default del JSON
                    $result = $wpdb->insert(
                        $table,
                        [
                            'component_name' => $component_name,
                            'group_name' => $group_name,
                            'attribute_name' => $attribute_name,
                            'attribute_value' => $default_value,
                            'is_editable' => $is_editable ? 1 : 0
                        ],
                        ['%s', '%s', '%s', '%s', '%d']
                    );

                    if ($result !== false) {
                        $stats['inserted']++;
                    }
                } else {
                    // UPDATE: Campo existente, preservar valor del usuario, solo actualizar is_editable
                    $result = $wpdb->update(
                        $table,
                        ['is_editable' => $is_editable ? 1 : 0],
                        [
                            'component_name' => $component_name,
                            'group_name' => $group_name,
                            'attribute_name' => $attribute_name
                        ],
                        ['%d'],
                        ['%s', '%s', '%s']
                    );

                    if ($result !== false) {
                        $stats['updated']++;
                    }
                }
            }
        }

        return [
            'success' => true,
            'message' => "Componente '{$component_name}' sincronizado correctamente",
            'stats' => $stats
        ];
    }

    /**
     * Migra configuración de visibilidad para todos los componentes
     *
     * ## EXAMPLES
     *
     *     wp roi-theme migrate-visibility
     *
     * @when after_wp_load
     */
    public function migrate_visibility(): void
    {
        $container = DIContainer::getInstance();
        $service = $container->getMigratePageVisibilityService();

        $result = $service->migrate();

        \WP_CLI::success(sprintf(
            'Migración completada: %d creados, %d omitidos',
            $result['created'],
            $result['skipped']
        ));
    }

    /**
     * Shortcodes que DEBEN ser preservados
     */
    private const PROTECTED_SHORTCODES = ['[roi_apu_search', '[roi_'];

    /**
     * Máximo porcentaje de contenido que puede eliminarse
     */
    private const MAX_CONTENT_LOSS_PERCENT = 50;

    /**
     * Limpia contenido Thrive congelado de páginas (H2 y paginación)
     *
     * LIMPIEZA QUIRÚRGICA CON VALIDACIONES DE SEGURIDAD:
     * - Elimina H2 con data-shortcode="tcb_post_title"
     * - Elimina paginación rota ([tcb_pagination_current_page], [tcb_pagination_total_pages])
     * - PRESERVA todo el demás contenido incluyendo shortcodes [roi_apu_search]
     * - Verifica que shortcodes importantes NO sean eliminados
     * - Aborta si se detecta pérdida excesiva de contenido (>50%)
     *
     * ## OPTIONS
     *
     * [--dry-run]
     * : Mostrar qué se limpiaría sin modificar nada (OBLIGATORIO primero)
     *
     * [--force]
     * : Ejecutar la limpieza real después de verificar dry-run
     *
     * [--include-others]
     * : Incluir otras páginas afectadas (Blog, Curso)
     *
     * ## EXAMPLES
     *
     *     # Ver qué se limpiaría (modo seguro) - SIEMPRE PRIMERO
     *     wp roi-theme clean_thrive --dry-run
     *
     *     # Ejecutar limpieza real (requiere --force)
     *     wp roi-theme clean_thrive --force
     *
     * @when after_wp_load
     */
    public function clean_thrive(array $args, array $assoc_args): void
    {
        $affectedPageIds = [
            107264, 107312, 107340, 107345, 107351, 107357, 107362,
            107369, 107374, 107379, 107384, 107389, 107395, 107399,
            107403, 107407, 107411, 107416, 107421, 107425, 185752
        ];
        $otherAffectedIds = [252030, 290709];

        $dryRun = isset($assoc_args['dry-run']);
        $includeOthers = isset($assoc_args['include-others']);
        $force = isset($assoc_args['force']);

        $pageIds = $affectedPageIds;
        if ($includeOthers) {
            $pageIds = array_merge($pageIds, $otherAffectedIds);
        }

        \WP_CLI::line('');
        \WP_CLI::line('╔══════════════════════════════════════════════════════════════════╗');
        \WP_CLI::line('║   LIMPIEZA QUIRÚRGICA DE CONTENIDO THRIVE CONGELADO (v2.0)       ║');
        \WP_CLI::line('║   Con validaciones de seguridad para proteger shortcodes         ║');
        \WP_CLI::line('╚══════════════════════════════════════════════════════════════════╝');
        \WP_CLI::line('');

        if ($dryRun) {
            \WP_CLI::warning('MODO DRY-RUN: No se modificará ningún contenido');
        } else {
            \WP_CLI::error('MODO REAL DESHABILITADO: Ejecuta primero con --dry-run', false);
            \WP_CLI::line('');
            \WP_CLI::line('Para ejecutar la limpieza real, primero revisa el dry-run:');
            \WP_CLI::line('  wp roi-theme clean_thrive --dry-run');
            \WP_CLI::line('');
            \WP_CLI::line('Si el dry-run es correcto y deseas ejecutar:');
            \WP_CLI::line('  wp roi-theme clean_thrive --force');

            if (!$force) {
                return;
            }
            \WP_CLI::warning('MODO REAL CON --force: Se modificará el contenido');
        }

        \WP_CLI::line('');
        \WP_CLI::line('Páginas a procesar: ' . count($pageIds));
        \WP_CLI::line('Shortcodes protegidos: ' . implode(', ', self::PROTECTED_SHORTCODES));
        \WP_CLI::line('Máxima pérdida permitida: ' . self::MAX_CONTENT_LOSS_PERCENT . '%');
        \WP_CLI::line('');

        $totalH2Removed = 0;
        $totalPaginationRemoved = 0;
        $totalBytesFreed = 0;
        $pagesModified = 0;
        $pagesSkipped = 0;
        $errors = [];

        foreach ($pageIds as $id) {
            $page = get_post($id);
            if (!$page) {
                \WP_CLI::warning("Página {$id} no encontrada, saltando...");
                continue;
            }

            $originalContent = $page->post_content;
            $originalSize = strlen($originalContent);

            $hasThrive = strpos($originalContent, 'tcb_post_title') !== false ||
                         strpos($originalContent, 'tcb_pagination') !== false;

            if (!$hasThrive) {
                \WP_CLI::line(sprintf("[SIN THRIVE] ID %d: %s", $id, mb_substr($page->post_title, 0, 50)));
                continue;
            }

            $h2Count = preg_match_all('/<h2[^>]*>\s*<span[^>]*data-shortcode="tcb_post_title"[^>]*>.*?<\/span>\s*<\/h2>/s', $originalContent);
            $protectedBefore = $this->countProtectedShortcodes($originalContent);
            $cleanResult = $this->cleanThriveContentSafely($originalContent);

            if ($cleanResult['error']) {
                $errors[] = "ID {$id}: {$cleanResult['error']}";
                \WP_CLI::error(sprintf("[ERROR] ID %d: %s - %s", $id, mb_substr($page->post_title, 0, 40), $cleanResult['error']), false);
                $pagesSkipped++;
                continue;
            }

            $cleanedContent = $cleanResult['content'];
            $newSize = strlen($cleanedContent);
            $protectedAfter = $this->countProtectedShortcodes($cleanedContent);

            if ($protectedAfter < $protectedBefore) {
                $errors[] = "ID {$id}: Se perderían shortcodes protegidos ({$protectedBefore} → {$protectedAfter})";
                \WP_CLI::error(sprintf("[ABORTADO] ID %d: Se perderían shortcodes protegidos (%d → %d)", $id, $protectedBefore, $protectedAfter), false);
                $pagesSkipped++;
                continue;
            }

            $lossPercent = $originalSize > 0 ? (($originalSize - $newSize) / $originalSize) * 100 : 0;
            if ($lossPercent > self::MAX_CONTENT_LOSS_PERCENT) {
                $errors[] = "ID {$id}: Pérdida excesiva de contenido ({$lossPercent}%)";
                \WP_CLI::error(sprintf("[ABORTADO] ID %d: Pérdida excesiva %.1f%% (máx %d%%)", $id, $lossPercent, self::MAX_CONTENT_LOSS_PERCENT), false);
                $pagesSkipped++;
                continue;
            }

            $hasChanges = $originalContent !== $cleanedContent;
            $bytesSaved = $originalSize - $newSize;
            $paginationRemoved = (strpos($originalContent, 'tcb_pagination_current_page') !== false && strpos($cleanedContent, 'tcb_pagination_current_page') === false) ? 1 : 0;

            if ($hasChanges) {
                $pagesModified++;
                $totalH2Removed += $h2Count;
                $totalPaginationRemoved += $paginationRemoved;
                $totalBytesFreed += $bytesSaved;

                $status = $dryRun ? '[DRY-RUN]' : '[LIMPIADO]';
                \WP_CLI::line(sprintf("%s ID %d: %s", $status, $id, mb_substr($page->post_title, 0, 50) . (mb_strlen($page->post_title) > 50 ? '...' : '')));
                \WP_CLI::line(sprintf("         → H2 eliminados: %d | Paginación: %s | Pérdida: %.1f%%", $h2Count, $paginationRemoved ? 'Sí' : 'No', $lossPercent));
                \WP_CLI::line(sprintf("         → Shortcodes [roi_*] preservados: %d | Bytes liberados: %s", $protectedAfter, $this->formatBytes($bytesSaved)));

                if (!$dryRun && $force) {
                    wp_update_post(['ID' => $id, 'post_content' => $cleanedContent]);
                }
            } else {
                \WP_CLI::line(sprintf("[SIN CAMBIOS] ID %d: %s", $id, mb_substr($page->post_title, 0, 50)));
            }
        }

        \WP_CLI::line('');
        \WP_CLI::line('════════════════════════════════════════════════════════════════════');
        \WP_CLI::line('RESUMEN:');
        \WP_CLI::line(sprintf('  Páginas modificadas:    %d', $pagesModified));
        \WP_CLI::line(sprintf('  Páginas omitidas:       %d', $pagesSkipped));
        \WP_CLI::line(sprintf('  Total H2 eliminados:    %d', $totalH2Removed));
        \WP_CLI::line(sprintf('  Paginaciones removidas: %d', $totalPaginationRemoved));
        \WP_CLI::line(sprintf('  Espacio liberado:       %s', $this->formatBytes($totalBytesFreed)));
        \WP_CLI::line('════════════════════════════════════════════════════════════════════');

        if (count($errors) > 0) {
            \WP_CLI::line('');
            \WP_CLI::warning('ERRORES ENCONTRADOS:');
            foreach ($errors as $error) {
                \WP_CLI::line("  - {$error}");
            }
        }

        if ($dryRun && $pagesModified > 0 && count($errors) === 0) {
            \WP_CLI::line('');
            \WP_CLI::success('Dry-run completado SIN errores.');
            \WP_CLI::line('');
            \WP_CLI::warning('Para ejecutar la limpieza real:');
            \WP_CLI::line('  wp roi-theme clean_thrive --force');
        } elseif (!$dryRun && $force && $pagesModified > 0) {
            \WP_CLI::line('');
            \WP_CLI::success('Limpieza completada exitosamente.');
            \WP_CLI::line('');
            \WP_CLI::warning('IMPORTANTE: Purga el caché del sitio para ver los cambios.');
        }
    }

    /**
     * Limpia el contenido con validaciones de seguridad
     * @return array{content: string, error: string|null}
     */
    private function cleanThriveContentSafely(string $content): array
    {
        $originalContent = $content;

        // Patrón específico: H2 que contiene span con data-shortcode="tcb_post_title"
        // Estructura: <h2><span data-shortcode="tcb_post_title"...>...</span></h2>
        $result = preg_replace('/<h2[^>]*>\s*<span[^>]*data-shortcode="tcb_post_title"[^>]*>.*?<\/span>\s*<\/h2>/s', '', $content);
        if ($result === null) {
            return ['content' => $originalContent, 'error' => 'preg_replace falló en patrón H2'];
        }
        $content = $result;

        $result = preg_replace('/<p[^>]*>.*?\[tcb_pagination_current_page\].*?\[tcb_pagination_total_pages\].*?<\/p>/s', '', $content);
        if ($result === null) {
            return ['content' => $originalContent, 'error' => 'preg_replace falló en patrón paginación'];
        }
        $content = $result;

        $result = preg_replace('/<p[^>]*data-button_layout="[^"]*"[^>]*data-page="[^"]*"[^>]*>.*?<\/p>/s', '', $content);
        if ($result === null) {
            return ['content' => $originalContent, 'error' => 'preg_replace falló en patrón botones'];
        }
        $content = $result;

        $content = str_replace('[tcb_pagination_current_page]', '', $content);
        $content = str_replace('[tcb_pagination_total_pages]', '', $content);

        $result = preg_replace('/(\r?\n){3,}/', "\n\n", $content);
        if ($result === null) {
            return ['content' => $originalContent, 'error' => 'preg_replace falló en limpieza líneas'];
        }
        $content = trim($result);

        if (empty($content) && !empty($originalContent)) {
            return ['content' => $originalContent, 'error' => 'El contenido quedó vacío'];
        }

        return ['content' => $content, 'error' => null];
    }

    /**
     * Cuenta shortcodes protegidos en el contenido
     */
    private function countProtectedShortcodes(string $content): int
    {
        $count = 0;
        foreach (self::PROTECTED_SHORTCODES as $shortcode) {
            $count += substr_count($content, $shortcode);
        }
        return $count;
    }

    /**
     * Formatea bytes a formato legible
     */
    private function formatBytes(int $bytes): string
    {
        if ($bytes < 1024) {
            return $bytes . ' B';
        } elseif ($bytes < 1048576) {
            return round($bytes / 1024, 1) . ' KB';
        } else {
            return round($bytes / 1048576, 2) . ' MB';
        }
    }
}

// Registrar comando WP-CLI
if (defined('WP_CLI') && WP_CLI) {
    \WP_CLI::add_command('roi-theme', MigrationCommand::class);
}
