<?php

/**
 * LolaCore � SiteScanner
 *
 * @package LolaCore
 * @license GPL-2.0-or-later
 */

declare(strict_types=1);

namespace LolaCore\Scanner;

// phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, PluginCheck.Security.DirectDB.UnescapedDBParameter, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.DirectDatabaseQuery.SchemaChange, WordPress.DB.SlowDBQuery.slow_db_query_meta_key -- Required for core functionality, dynamic queries, and performance. Validated inputs.

// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

use LolaCore\Memory\MemoryManager;

/**
 * Class SiteScanner
 *
 * Orchestrates the full site scan process:
 * 1. Collects all local data via ScanDataCollector.
 * 2. Saves the complete JSON to wp_lolacore_config as 'initial_scan'.
 * 3. Converts findings into individual memory facts via MemoryManager.
 * 4. Fires the 'lolacore_scan_complete' action hook.
 *
 * Called on plugin activation and on-demand from chat.
 */
class SiteScanner
{
    private ScanDataCollector $collector;
    private MemoryManager $memoryManager;

    public function __construct(ScanDataCollector $collector, MemoryManager $memoryManager)
    {
        $this->collector = $collector;
        $this->memoryManager = $memoryManager;
    }

    /**
     * Run the full site scan.
     *
     * Called on activation and on-demand from chat.
     *
     * 1. Collect all data via ScanDataCollector::collectAll()
     * 2. Save full JSON to config 'initial_scan'
     * 3. Save individual facts to memory with source='scan'
     * 4. Fire action 'lolacore_scan_complete' with $scanData
     *
     * @return array The complete scan data.
     */
    public function runFullScan(): array
    {
        $scanData = $this->collector->collectAll();

        // Save complete scan to config
        $this->saveToConfig($scanData);

        // Convert scan data into individual memory facts
        $this->saveFactsFromScan($scanData);

        // Fire action hook for extensibility
        \do_action('lolacore_scan_complete', $scanData);

        return $scanData;
    }

    /**
     * Build a compact plain-text summary of the last scan for LLM context injection.
     *
     * Returns a concise summary (~100-150 tokens) suitable for embedding in the
     * system prompt as a stable prefix. Plugin names with a pending update are
     * marked with an asterisk (*). Up to 12 active plugins are listed by name;
     * any remainder is counted as "+N more".
     *
     * @return string Human-readable scan summary, or a fallback if no scan is available.
     */
    public function getScanSummary(): string
    {
        $scan = $this->getLastScan();
        if (empty($scan)) {
            return 'Site scan not yet available.';
        }

        $lines = [];

        // Platform: WP + PHP + DB + Security
        $wp     = $scan['wordpress']['wp_version'] ?? '?';
        $wpNote = ($scan['wordpress']['is_updated'] ?? true) ? '' : ' [UPDATE AVAILABLE]';
        $php    = $scan['php']['php_version'] ?? '?';
        $db     = $scan['database']['total_size_mb'] ?? '?';
        $ssl    = ($scan['security']['ssl_active'] ?? true) ? 'SSL active' : 'SSL OFF';
        $debug  = ($scan['security']['debug_mode'] ?? false) ? ' · WP_DEBUG ON' : '';
        $lines[] = "WP {$wp}{$wpNote} · PHP {$php} · DB {$db}MB · {$ssl}{$debug}";

        // Theme
        if (!empty($scan['theme']['active_theme_name'])) {
            $t       = $scan['theme'];
            $child   = ($t['is_child_theme'] ?? false) ? ' (child)' : '';
            $tUpdate = ($t['update_available'] ?? false) ? ' [UPDATE]' : '';
            $lines[] = "Theme: {$t['active_theme_name']} {$t['theme_version']}{$child}{$tUpdate}";
        }

        // Plugins: counts + names of active ones (capped at 12)
        if (!empty($scan['plugins']) && \is_array($scan['plugins'])) {
            $all          = $scan['plugins'];
            $active       = \array_values(\array_filter($all, fn($p) => $p['is_active'] ?? false));
            $pendingCount = \count(\array_filter($all, fn($p) => $p['update_available'] ?? false));
            $totalCount   = \count($all);
            $activeCount  = \count($active);
            $pendingNote  = $pendingCount > 0 ? ", {$pendingCount} updates pending" : '';

            $names = \array_map(function ($p) {
                return ($p['name'] ?? '') . (($p['update_available'] ?? false) ? '*' : '');
            }, $active);

            $cap     = 12;
            $shown   = \array_slice($names, 0, $cap);
            $extra   = $activeCount > $cap ? ' +' . ($activeCount - $cap) . ' more' : '';
            $lines[] = "{$totalCount} plugins ({$activeCount} active{$pendingNote}): "
                . \implode(', ', $shown) . $extra;
        }

        // Content counts
        if (!empty($scan['content'])) {
            $c     = $scan['content'];
            $items = [];
            if (($c['posts_count'] ?? 0) > 0) {
                $items[] = $c['posts_count'] . ' posts';
            }
            if (($c['pages_count'] ?? 0) > 0) {
                $items[] = $c['pages_count'] . ' pages';
            }
            if (($c['products_count'] ?? 0) > 0) {
                $items[] = $c['products_count'] . ' products';
            }
            if (($c['media_count'] ?? 0) > 0) {
                $items[] = $c['media_count'] . ' media';
            }
            if (!empty($items)) {
                $lines[] = 'Content: ' . \implode(', ', $items);
            }
        }

        // Backup status
        if (isset($scan['backup'])) {
            $b       = $scan['backup'];
            $lines[] = ($b['has_backup_plugin'] ?? false)
                ? 'Backup: ' . \implode(', ', $b['detected_plugins'] ?? [])
                : 'Backup: none detected';
        }

        return \implode("\n", $lines);
    }

    /**
     * Get the last scan data from config.
     *
     * @return array|null The last scan data, or null if no scan has been performed.
     */
    public function getLastScan(): ?array
    {
        global $wpdb;
        $table = $wpdb->prefix . 'lolacore_config';

        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, PluginCheck.Security.DirectDB.UnescapedDBParameter, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQL.NotPrepared -- Custom LolaCore table query.
        $value = $wpdb->get_var(
            $wpdb->prepare(
                "SELECT config_value FROM {$table} WHERE config_key = %s",
                'initial_scan',
            ),
        );

        if (empty($value)) {
            return null;
        }

        $decoded = \json_decode($value, true);
        return \is_array($decoded) ? $decoded : null;
    }

    /**
     * Save the complete scan data as JSON to the config table.
     *
     * @param array $scanData The complete scan data.
     */
    private function saveToConfig(array $scanData): void
    {
        global $wpdb;
        $table = $wpdb->prefix . 'lolacore_config';

        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, PluginCheck.Security.DirectDB.UnescapedDBParameter, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQL.NotPrepared -- Custom LolaCore table query.
        $wpdb->update(
            $table,
            ['config_value' => \wp_json_encode($scanData)],
            ['config_key' => 'initial_scan'],
        );
    }

    /**
     * Convert scan data into individual memory facts.
     *
     * Each relevant finding becomes a separate fact in wp_lolacore_memory
     * with source='scan'. Facts are categorized by type and assigned
     * importance scores based on their criticality.
     *
     * @param array $scanData The complete scan data.
     */
    private function saveFactsFromScan(array $scanData): void
    {
        // WordPress core info
        if (isset($scanData['wordpress'])) {
            $wp = $scanData['wordpress'];
            $wpVersion = $wp['wp_version'] ?? 'unknown';
            $isUpdated = $wp['is_updated'] ?? true;

            $content = $isUpdated
                ? "WordPress {$wpVersion} installed, up to date."
                : "WordPress {$wpVersion} installed, update available.";

            $this->memoryManager->saveFact(
                factType: 'site_state',
                content: $content,
                importance: $isUpdated ? 3 : 7,
                source: 'scan',
                structuredData: $wp,
            );
        }

        // Plugins with pending updates
        if (isset($scanData['plugins']) && \is_array($scanData['plugins'])) {
            $outdatedPlugins = [];
            foreach ($scanData['plugins'] as $plugin) {
                if ($plugin['update_available'] ?? false) {
                    $outdatedPlugins[] = $plugin['name'] . ' (' . $plugin['version'] . ' → ' . ($plugin['update_version'] ?? '?') . ')';
                }
            }

            $totalPlugins = \count($scanData['plugins']);
            $activePlugins = \count(\array_filter($scanData['plugins'], fn($p) => $p['is_active'] ?? false));

            $this->memoryManager->saveFact(
                factType: 'site_state',
                content: "{$totalPlugins} plugins installed ({$activePlugins} active).",
                importance: 3,
                source: 'scan',
                structuredData: [
                    'total'   => $totalPlugins,
                    'active'  => $activePlugins,
                    'plugins' => \array_column($scanData['plugins'], 'name'),
                ],
            );

            if (!empty($outdatedPlugins)) {
                $count = \count($outdatedPlugins);
                $list = \implode(', ', \array_slice($outdatedPlugins, 0, 5));
                $suffix = $count > 5 ? " and " . ($count - 5) . " more" : '';

                $this->memoryManager->saveFact(
                    factType: 'site_state',
                    content: "{$count} plugin(s) with pending updates: {$list}{$suffix}.",
                    importance: 6,
                    source: 'scan',
                    structuredData: ['outdated' => $outdatedPlugins],
                );
            }
        }

        // PHP info
        if (isset($scanData['php'])) {
            $php = $scanData['php'];
            $phpVersion = $php['php_version'] ?? 'unknown';
            $missing = [];

            if (isset($php['extensions']) && \is_array($php['extensions'])) {
                foreach ($php['extensions'] as $ext => $loaded) {
                    if (!$loaded) {
                        $missing[] = $ext;
                    }
                }
            }

            $extStatus = empty($missing)
                ? 'all required extensions present'
                : 'missing extensions: ' . \implode(', ', $missing);

            $this->memoryManager->saveFact(
                factType: 'site_state',
                content: "PHP {$phpVersion}, {$extStatus}.",
                importance: empty($missing) ? 3 : 7,
                source: 'scan',
                structuredData: $php,
            );
        }

        // Database health
        if (isset($scanData['database'])) {
            $db = $scanData['database'];
            $sizeMb = $db['total_size_mb'] ?? 0;
            $expiredTransients = $db['expired_transients_count'] ?? 0;
            $revisions = $db['post_revisions_count'] ?? 0;

            $parts = ["Database size: {$sizeMb}MB"];
            if ($expiredTransients > 0) {
                $parts[] = "{$expiredTransients} expired transients found";
            }
            if ($revisions > 100) {
                $parts[] = "{$revisions} post revisions";
            }

            $importance = 3;
            if ($expiredTransients > 1000 || $sizeMb > 500) {
                $importance = 6;
            }

            $this->memoryManager->saveFact(
                factType: 'performance_metric',
                content: \implode(', ', $parts) . '.',
                importance: $importance,
                source: 'scan',
                structuredData: $db,
            );
        }

        // Theme info
        if (isset($scanData['theme'])) {
            $theme = $scanData['theme'];
            $themeName = $theme['active_theme_name'] ?? 'unknown';
            $themeVersion = $theme['theme_version'] ?? '';
            $isChild = $theme['is_child_theme'] ?? false;
            $childInfo = $isChild ? ' (child of ' . ($theme['parent_theme'] ?? 'unknown') . ')' : '';
            $updateInfo = ($theme['update_available'] ?? false) ? ', update available' : '';

            $this->memoryManager->saveFact(
                factType: 'site_state',
                content: "Active theme: {$themeName} v{$themeVersion}{$childInfo}{$updateInfo}.",
                importance: 3,
                source: 'scan',
                structuredData: $theme,
            );
        }

        // Security info
        if (isset($scanData['security'])) {
            $sec = $scanData['security'];
            $issues = [];

            if (!($sec['ssl_active'] ?? true)) {
                $issues[] = 'SSL not active';
            }
            if ($sec['debug_mode'] ?? false) {
                $issues[] = 'WP_DEBUG enabled';
            }
            if ($sec['debug_log'] ?? false) {
                $issues[] = 'WP_DEBUG_LOG enabled';
            }

            if (!empty($issues)) {
                $this->memoryManager->saveFact(
                    factType: 'site_state',
                    content: 'Security concerns: ' . \implode(', ', $issues) . '.',
                    importance: 7,
                    source: 'scan',
                    structuredData: $sec,
                );
            } else {
                $this->memoryManager->saveFact(
                    factType: 'site_state',
                    content: 'Security baseline: SSL active, debug mode off, file editing status checked.',
                    importance: 2,
                    source: 'scan',
                    structuredData: $sec,
                );
            }
        }

        // Content summary
        if (isset($scanData['content'])) {
            $cnt = $scanData['content'];
            $posts = $cnt['posts_count'] ?? 0;
            $pages = $cnt['pages_count'] ?? 0;
            $products = $cnt['products_count'] ?? 0;
            $media = $cnt['media_count'] ?? 0;

            $parts = ["{$posts} posts", "{$pages} pages"];
            if ($products > 0) {
                $parts[] = "{$products} products (WooCommerce)";
            }
            $parts[] = "{$media} media files";

            $this->memoryManager->saveFact(
                factType: 'site_state',
                content: 'Content: ' . \implode(', ', $parts) . '.',
                importance: 3,
                source: 'scan',
                structuredData: $cnt,
            );
        }

        // Backup status
        if (isset($scanData['backup'])) {
            $backup = $scanData['backup'];
            $hasBackup = $backup['has_backup_plugin'] ?? false;

            if ($hasBackup) {
                $plugins = \implode(', ', $backup['detected_plugins'] ?? []);
                $this->memoryManager->saveFact(
                    factType: 'site_state',
                    content: "Backup solution detected: {$plugins}.",
                    importance: 2,
                    source: 'scan',
                    structuredData: $backup,
                );
            } else {
                $this->memoryManager->saveFact(
                    factType: 'site_state',
                    content: 'No known backup plugin detected. Recommend installing a backup solution.',
                    importance: 8,
                    source: 'scan',
                    structuredData: $backup,
                );
            }
        }

        // Elementor info
        if (isset($scanData['elementor']) && $scanData['elementor'] !== null) {
            $el = $scanData['elementor'];
            $version = $el['version'] ?? 'unknown';
            $isPro = ($el['is_pro'] ?? false) ? ' Pro' : '';
            $templates = $el['templates_count'] ?? 0;
            $pages = $el['elementor_pages_count'] ?? 0;

            $this->memoryManager->saveFact(
                factType: 'site_state',
                content: "Elementor{$isPro} v{$version} active: {$pages} pages built, {$templates} templates.",
                importance: 4,
                source: 'scan',
                structuredData: $el,
            );
        }

        // Abilities info
        if (isset($scanData['abilities'])) {
            $abilities = $scanData['abilities'];
            if ($abilities['available'] ?? false) {
                $count = \count($abilities['abilities'] ?? []);
                $this->memoryManager->saveFact(
                    factType: 'site_state',
                    content: "WordPress Abilities API active: {$count} abilities registered by other plugins.",
                    importance: 4,
                    source: 'scan',
                    structuredData: $abilities,
                );
            }
        }
    }
}