<?php

/**
 * Basic (Local) Audit Engine
 *
 * Lightweight audit that runs locally without a license/server calls.
 *
 * @package ProRank\SEO\Core\Audits
 */
declare(strict_types=1);

namespace ProRank\SEO\Core\Audits;

use WP_Error;

if ( ! defined( 'ABSPATH' ) ) {
    exit;
}




class BasicAuditEngine {
    private const AUDIT_ID_PREFIX = 'local_basic_';

    private string $audits_table;
    private string $audit_urls_table;
    private string $audit_issues_table;

    public function __construct() {
        global $wpdb;
        $this->audits_table = $wpdb->prefix . 'prorank_audits';
        $this->audit_urls_table = $wpdb->prefix . 'prorank_audit_urls';
        $this->audit_issues_table = $wpdb->prefix . 'prorank_audit_issues';
    }

    /**
     * Run a basic audit synchronously and persist results.
     *
     * @param array $settings Optional settings (unused for now).
     * @return array|WP_Error Status payload on success, WP_Error on failure.
     */
    public function run(array $settings = []) {
        global $wpdb;

        $this->maybe_create_tables();

        $audit_id = self::AUDIT_ID_PREFIX . wp_generate_uuid4();
        $now_mysql = current_time('mysql');
        $site_url = home_url('/');

        $issues = $this->collect_issues();
        $counts = $this->count_severities($issues);
        $score = $this->calculate_score($counts);

        $stats = [
            'mode' => 'local_basic',
            'unique_issues' => [
                'critical' => $counts['critical'],
                'high' => $counts['high'],
                'medium' => $counts['medium'],
                'low' => $counts['low'],
                'warning' => $counts['warning'],
                'total' => $counts['total'],
            ],
        ];

        $inserted = $wpdb->insert(
            $this->audits_table,
            [
                'audit_id' => $audit_id,
                'status' => 'completed',
                'started_at' => $now_mysql,
                'completed_at' => $now_mysql,
                'total_urls' => 1,
                'score' => $score,
                'stats' => wp_json_encode($stats),
                'options' => wp_json_encode($settings),
                'error' => null,
            ],
            [
                '%s', // audit_id
                '%s', // status
                '%s', // started_at
                '%s', // completed_at
                '%d', // total_urls
                '%d', // score
                '%s', // stats
                '%s', // options
                '%s', // error
            ]
        );

        if ($inserted === false) {
            return new WP_Error('db_error', __('Failed to store audit result', 'prorank-seo'));
        }

        // Store single URL entry (home page) and attach all issues to it.
        $issues_count = $counts['critical'] + $counts['high'];
        $warnings_count = $counts['medium'] + $counts['low'];

        $url_inserted = $wpdb->insert(
            $this->audit_urls_table,
            [
                'audit_id' => $audit_id,
                'url' => $site_url,
                'status' => 'checked',
                'issues_count' => $issues_count,
                'warnings_count' => $warnings_count,
                'passed_count' => 0,
                'score' => $score,
                'created_at' => $now_mysql,
                'checked_at' => $now_mysql,
            ],
            [
                '%s', // audit_id
                '%s', // url
                '%s', // status
                '%d', // issues_count
                '%d', // warnings_count
                '%d', // passed_count
                '%d', // score
                '%s', // created_at
                '%s', // checked_at
            ]
        );

        $url_id = (int) $wpdb->insert_id;

        if ($url_inserted === false || $url_id <= 0) {
            return new WP_Error('db_error', __('Failed to store audit URLs', 'prorank-seo'));
        }

        foreach ($issues as $issue) {
            $wpdb->insert(
                $this->audit_issues_table,
                [
                    'audit_id' => $audit_id,
                    'url_id' => $url_id,
                    'type' => $issue['type'],
                    'severity' => $issue['severity'],
                    'message' => $issue['message'],
                    'data' => wp_json_encode($issue),
                    'created_at' => $now_mysql,
                ],
                [
                    '%s', // audit_id
                    '%d', // url_id
                    '%s', // type
                    '%s', // severity
                    '%s', // message
                    '%s', // data
                    '%s', // created_at
                ]
            );
        }

        // Ensure the dashboard reflects the latest audit immediately.
        delete_transient('prorank_dashboard_stats');

        return $this->format_status($audit_id, [
            'started_at' => $now_mysql,
            'completed_at' => $now_mysql,
            'total_urls' => 1,
            'score' => $score,
        ], $counts);
    }

    /**
     * Return latest basic audit status (if any).
     */
    public function get_latest_status(): ?array {
        global $wpdb;

        if (!$this->table_exists($this->audits_table)) {
            return null;
        }

        $row = $wpdb->get_row(
            $wpdb->prepare(
                "SELECT audit_id, status, started_at, completed_at, total_urls, score
                 FROM {$this->audits_table}
                 WHERE audit_id LIKE %s
                 ORDER BY completed_at DESC, started_at DESC, id DESC
                 LIMIT 1",
                self::AUDIT_ID_PREFIX . '%'
            )
        );

        if (!$row) {
            return null;
        }

        $counts = $this->get_issue_counts((string) $row->audit_id);

        return $this->format_status((string) $row->audit_id, [
            'started_at' => $row->started_at,
            'completed_at' => $row->completed_at,
            'total_urls' => (int) ($row->total_urls ?? 0),
            'score' => (int) ($row->score ?? 0),
        ], $counts);
    }

    /**
     * Get issues for a specific local audit.
     */
    public function get_issues(string $audit_id): array {
        global $wpdb;

        if (
            !$this->table_exists($this->audit_issues_table) ||
            !$this->table_exists($this->audit_urls_table)
        ) {
            return [];
        }

        $rows = $wpdb->get_results(
            $wpdb->prepare(
                "SELECT i.id, i.type, i.severity, i.message, i.data, u.url
                 FROM {$this->audit_issues_table} i
                 LEFT JOIN {$this->audit_urls_table} u ON u.id = i.url_id
                 WHERE i.audit_id = %s
                 ORDER BY FIELD(i.severity, 'critical', 'high', 'medium', 'low', 'passed'), i.id ASC",
                $audit_id
            )
        );

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

        $issues = [];
        foreach ($rows as $row) {
            $data = [];
            if (!empty($row->data)) {
                $decoded = json_decode((string) $row->data, true);
                if (is_array($decoded)) {
                    $data = $decoded;
                }
            }

            $issues[] = [
                'id' => (string) ($data['id'] ?? $row->id),
                'type' => (string) ($data['type'] ?? $row->type),
                'issue_type' => (string) ($data['type'] ?? $row->type),
                'severity' => (string) ($data['severity'] ?? $row->severity),
                'category' => (string) ($data['category'] ?? 'content'),
                'display_category' => (string) ($data['display_category'] ?? ($data['category'] ?? 'content')),
                'title' => (string) ($data['title'] ?? $row->type),
                'description' => (string) ($data['description'] ?? $row->message),
                'url' => (string) ($data['url'] ?? ($row->url ?? home_url('/'))),
                'impact' => (string) ($data['impact'] ?? ''),
                'how_to_fix' => (string) ($data['how_to_fix'] ?? ''),
                'fix_suggestion' => (string) ($data['how_to_fix'] ?? ''),
                'reference' => $data['reference'] ?? null,
            ];
        }

        return $issues;
    }

    /**
     * Get basic audit history.
     */
    public function get_history(int $limit = 10): array {
        global $wpdb;

        if (!$this->table_exists($this->audits_table)) {
            return [];
        }

        $limit = max(1, min(50, $limit));

        $rows = $wpdb->get_results(
            $wpdb->prepare(
                "SELECT audit_id, status, started_at, completed_at, total_urls, score
                 FROM {$this->audits_table}
                 WHERE audit_id LIKE %s
                 ORDER BY completed_at DESC, started_at DESC, id DESC
                 LIMIT %d",
                self::AUDIT_ID_PREFIX . '%',
                $limit
            )
        );

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

        $domain = wp_parse_url(home_url('/'), PHP_URL_HOST) ?: '';

        $audits = [];
        foreach ($rows as $row) {
            $audit_id = (string) $row->audit_id;
            $counts = $this->get_issue_counts($audit_id);

            $audits[] = [
                'id' => $audit_id,
                'audit_id' => $audit_id,
                'domain' => $domain,
                'status' => (string) ($row->status ?? 'completed'),
                'state' => 'completed',
                'audit_type' => 'local_basic',
                'total_urls' => (int) ($row->total_urls ?? 0),
                'progress' => 100,
                'overallScore' => (int) ($row->score ?? 0),
                'overall_score' => (int) ($row->score ?? 0),
                'issues' => [
                    'critical' => $counts['critical'],
                    'high' => $counts['high'],
                    'medium' => $counts['medium'],
                    'low' => $counts['low'],
                    'warning' => $counts['warning'],
                    'total' => $counts['total'],
                ],
                'started_at' => $row->started_at,
                'completed_at' => $row->completed_at,
                'created_at' => $row->completed_at ?: $row->started_at,
            ];
        }

        return $audits;
    }

    private function format_status(string $audit_id, array $audit_row, array $counts): array {
        $total_urls = (int) ($audit_row['total_urls'] ?? 0);
        if ($total_urls <= 0) {
            $total_urls = 1;
        }

        return [
            'audit_id' => $audit_id,
            'state' => 'completed',
            'status' => 'completed',
            'mode' => 'local_basic',
            'message' => __('Basic audit completed locally', 'prorank-seo'),
            'progress' => [
                'percent' => 100,
                'percentage' => 100,
                'total_urls' => $total_urls,
                'checked_urls' => $total_urls,
            ],
            'started_at' => $audit_row['started_at'] ?? null,
            'completed_at' => $audit_row['completed_at'] ?? null,
            'issuesCritical' => $counts['critical'],
            'issuesHigh' => $counts['high'],
            'issuesMedium' => $counts['medium'],
            'issuesLow' => $counts['low'],
            'issuesWarning' => $counts['warning'],
            'overallScore' => (int) ($audit_row['score'] ?? 0),
            'overall_score' => (int) ($audit_row['score'] ?? 0),
            'totalUrls' => $total_urls,
            'total_urls' => $total_urls,
        ];
    }

    private function collect_issues(): array {
        $issues = [];

        $site_url = home_url('/');
        $using_https = function_exists('wp_is_using_https')
            ? (bool) wp_is_using_https()
            : (stripos($site_url, 'https://') === 0);

        // Technical SEO: Search engine visibility
        $blog_public = (int) get_option('blog_public', 1);
        if ($blog_public !== 1) {
            $issues[] = $this->issue([
                'type' => 'discourage_search_engines',
                'severity' => 'critical',
                'category' => 'technical_seo',
                'title' => __('Search engine visibility is disabled', 'prorank-seo'),
                'description' => __('Your site is set to discourage search engines from indexing it.', 'prorank-seo'),
                'how_to_fix' => __('Go to Settings → Reading and uncheck “Discourage search engines from indexing this site”.', 'prorank-seo'),
            ]);
        }

        // Technical SEO: Permalinks
        $permalink_structure = (string) get_option('permalink_structure', '');
        if (trim($permalink_structure) === '') {
            $issues[] = $this->issue([
                'type' => 'permalink_structure',
                'severity' => 'medium',
                'category' => 'technical_seo',
                'title' => __('Permalinks are not enabled', 'prorank-seo'),
                'description' => __('Pretty permalinks improve SEO and readability.', 'prorank-seo'),
                'how_to_fix' => __('Go to Settings → Permalinks and select “Post name”.', 'prorank-seo'),
            ]);
        }

        // Security: HTTPS
        if (!$using_https) {
            $issues[] = $this->issue([
                'type' => 'https_not_enabled',
                'severity' => 'high',
                'category' => 'security',
                'title' => __('HTTPS is not enabled', 'prorank-seo'),
                'description' => __('Your site is not using HTTPS, which can hurt SEO and security.', 'prorank-seo'),
                'how_to_fix' => __('Install an SSL certificate and update WordPress Address + Site Address to https://', 'prorank-seo'),
            ]);
        }

        // Security: Mixed content risk (common WordPress misconfiguration)
        if ($using_https) {
            $home_option = (string) get_option('home', '');
            $siteurl_option = (string) get_option('siteurl', '');
            if (
                (stripos($home_option, 'http://') === 0) ||
                (stripos($siteurl_option, 'http://') === 0)
            ) {
                $issues[] = $this->issue([
                    'type' => 'wordpress_urls_http',
                    'severity' => 'high',
                    'category' => 'security',
                    'title' => __('WordPress URLs are set to http://', 'prorank-seo'),
                    'description' => __('Your admin is running over HTTPS but WordPress Address/Site Address are set to http://. This often causes mixed content warnings (icons, uploads, images).', 'prorank-seo'),
                    'how_to_fix' => __('Go to Settings → General and set both WordPress Address and Site Address to https:// (or define WP_HOME/WP_SITEURL in wp-config.php).', 'prorank-seo'),
                ]);
            }

            $uploads = wp_get_upload_dir();
            $baseurl = is_array($uploads) ? ($uploads['baseurl'] ?? '') : '';
            if (is_string($baseurl) && stripos($baseurl, 'http://') === 0) {
                $issues[] = $this->issue([
                    'type' => 'mixed_content_risk',
                    'severity' => 'high',
                    'category' => 'security',
                    'title' => __('Mixed content risk detected', 'prorank-seo'),
                    'description' => __('Your uploads URL uses http:// while the site is on https://. This can cause mixed content warnings.', 'prorank-seo'),
                    'how_to_fix' => __('Check Settings → General and ensure both WordPress Address and Site Address use https://. Then update hardcoded http:// links.', 'prorank-seo'),
                ]);
            }
        }

        // Site basics: Site icon
        if (function_exists('has_site_icon') && !has_site_icon()) {
            $issues[] = $this->issue([
                'type' => 'site_icon_missing',
                'severity' => 'low',
                'category' => 'on_page_seo',
                'title' => __('Site icon (favicon) is missing', 'prorank-seo'),
                'description' => __('A site icon improves branding in tabs, bookmarks, and some SERP displays.', 'prorank-seo'),
                'how_to_fix' => __('Go to Appearance → Customize → Site Identity and set a Site Icon.', 'prorank-seo'),
            ]);
        }

        // Site basics: Default tagline
        $tagline = trim((string) get_option('blogdescription', ''));
        if ($tagline !== '' && strtolower($tagline) === 'just another wordpress site') {
            $issues[] = $this->issue([
                'type' => 'default_tagline',
                'severity' => 'low',
                'category' => 'on_page_seo',
                'title' => __('Default WordPress tagline detected', 'prorank-seo'),
                'description' => __('Your site tagline is still the default “Just another WordPress site”.', 'prorank-seo'),
                'how_to_fix' => __('Go to Settings → General and update the Tagline to match your brand.', 'prorank-seo'),
            ]);
        }

        // Security: WP_DEBUG enabled
        if (defined('WP_DEBUG') && WP_DEBUG) {
            $issues[] = $this->issue([
                'type' => 'wp_debug_enabled',
                'severity' => 'medium',
                'category' => 'security',
                'title' => __('WP_DEBUG is enabled', 'prorank-seo'),
                'description' => __('Debug mode can leak information and may affect performance.', 'prorank-seo'),
                'how_to_fix' => __('Disable WP_DEBUG on production sites (wp-config.php).', 'prorank-seo'),
            ]);
        }

        // Performance: Cache settings (ProRank)
        $cache_settings = get_option('prorank_cache_settings', []);
        if (is_array($cache_settings)) {
            if (empty($cache_settings['cache_enabled'])) {
                $issues[] = $this->issue([
                    'type' => 'page_cache_disabled',
                    'severity' => 'medium',
                    'category' => 'performance',
                    'title' => __('Page cache is disabled', 'prorank-seo'),
                    'description' => __('Enabling page cache can significantly improve TTFB and repeat visits.', 'prorank-seo'),
                    'how_to_fix' => __('Enable Page Cache in ProRank → Performance → Caching & Delivery.', 'prorank-seo'),
                ]);
            }

            if (empty($cache_settings['browser_cache_enabled'])) {
                $issues[] = $this->issue([
                    'type' => 'browser_cache_disabled',
                    'severity' => 'medium',
                    'category' => 'performance',
                    'title' => __('Browser cache headers are disabled', 'prorank-seo'),
                    'description' => __('Setting cache headers reduces repeat-load times for static assets.', 'prorank-seo'),
                    'how_to_fix' => __('Enable Browser Cache in ProRank → Performance → Caching & Delivery.', 'prorank-seo'),
                ]);
            }
        }

        // Performance: Asset optimization settings (ProRank)
        $asset_settings = get_option('prorank_asset_optimization_settings', []);
        if (is_array($asset_settings)) {
            if (empty($asset_settings['js_minify_enabled'])) {
                $issues[] = $this->issue([
                    'type' => 'js_minify_disabled',
                    'severity' => 'low',
                    'category' => 'performance',
                    'title' => __('JavaScript minification is disabled', 'prorank-seo'),
                    'description' => __('Minifying JS can reduce payload and parsing time.', 'prorank-seo'),
                    'how_to_fix' => __('Enable JS Minify in ProRank → Performance → Assets & JS.', 'prorank-seo'),
                ]);
            }

            if (empty($asset_settings['font_display_swap'])) {
                $issues[] = $this->issue([
                    'type' => 'font_display_swap_disabled',
                    'severity' => 'low',
                    'category' => 'performance',
                    'title' => __('font-display is not set to swap', 'prorank-seo'),
                    'description' => __('Setting font-display: swap keeps text visible during font loading.', 'prorank-seo'),
                    'how_to_fix' => __('Enable “Font Display: swap” in ProRank → Performance → Assets & JS.', 'prorank-seo'),
                ]);
            }
        }

        // Performance: Image settings (ProRank)
        $image_settings = get_option('prorank_image_optimization_settings', []);
        if (is_array($image_settings)) {
            if (isset($image_settings['lazyload_images']) && empty($image_settings['lazyload_images'])) {
                $issues[] = $this->issue([
                    'type' => 'lazyload_images_disabled',
                    'severity' => 'low',
                    'category' => 'performance',
                    'title' => __('Image lazy-loading is disabled', 'prorank-seo'),
                    'description' => __('Lazy-loading images can improve initial load performance.', 'prorank-seo'),
                    'how_to_fix' => __('Enable Image Lazy-load in ProRank → Performance → Image Optimization.', 'prorank-seo'),
                ]);
            }

            if (isset($image_settings['webp_enabled']) && empty($image_settings['webp_enabled'])) {
                $issues[] = $this->issue([
                    'type' => 'next_gen_images_disabled',
                    'severity' => 'low',
                    'category' => 'performance',
                    'title' => __('Next‑gen images (WebP) are disabled', 'prorank-seo'),
                    'description' => __('Serving WebP/AVIF can reduce image transfer size significantly.', 'prorank-seo'),
                    'how_to_fix' => __('Enable WebP in ProRank → Performance → Image Optimization.', 'prorank-seo'),
                ]);
            }
        }

        return $issues;
    }

    private function issue(array $issue): array {
        $defaults = [
            'id' => (string) wp_generate_uuid4(),
            'type' => '',
            'severity' => 'low',
            'category' => 'content',
            'display_category' => null,
            'title' => '',
            'description' => '',
            'message' => '',
            'url' => home_url('/'),
            'impact' => '',
            'how_to_fix' => '',
            'reference' => null,
        ];

        $merged = array_merge($defaults, $issue);

        if (empty($merged['display_category'])) {
            $merged['display_category'] = $merged['category'];
        }

        if (empty($merged['message'])) {
            $merged['message'] = $merged['description'] ?: $merged['title'];
        }

        $allowed = ['critical', 'high', 'medium', 'low', 'passed'];
        if (!in_array($merged['severity'], $allowed, true)) {
            $merged['severity'] = 'low';
        }

        $merged['category'] = (string) $merged['category'];
        $merged['display_category'] = (string) $merged['display_category'];
        $merged['title'] = (string) $merged['title'];
        $merged['description'] = (string) $merged['description'];
        $merged['message'] = (string) $merged['message'];
        $merged['how_to_fix'] = (string) $merged['how_to_fix'];

        return $merged;
    }

    private function count_severities(array $issues): array {
        $counts = [
            'critical' => 0,
            'high' => 0,
            'medium' => 0,
            'low' => 0,
            'warning' => 0,
            'total' => 0,
        ];

        foreach ($issues as $issue) {
            $severity = (string) ($issue['severity'] ?? 'low');
            if (!isset($counts[$severity])) {
                $severity = 'low';
            }
            $counts[$severity]++;
            $counts['total']++;
        }

        // UI often treats low as "warning"
        $counts['warning'] = $counts['low'];

        return $counts;
    }

    private function calculate_score(array $counts): int {
        $score = 100;
        $score -= min(60, $counts['critical'] * 15);
        $score -= min(40, $counts['high'] * 8);
        $score -= min(25, $counts['medium'] * 4);
        $score -= min(15, $counts['low'] * 2);

        return max(0, min(100, $score));
    }

    private function get_issue_counts(string $audit_id): array {
        global $wpdb;

        $counts = [
            'critical' => 0,
            'high' => 0,
            'medium' => 0,
            'low' => 0,
            'warning' => 0,
            'total' => 0,
        ];

        if (!$this->table_exists($this->audit_issues_table)) {
            return $counts;
        }

        $rows = $wpdb->get_results(
            $wpdb->prepare(
                "SELECT severity, COUNT(*) as cnt
                 FROM {$this->audit_issues_table}
                 WHERE audit_id = %s
                 GROUP BY severity",
                $audit_id
            )
        );

        foreach ($rows as $row) {
            $sev = (string) $row->severity;
            if (isset($counts[$sev])) {
                $counts[$sev] = (int) $row->cnt;
            }
        }

        $counts['total'] = $counts['critical'] + $counts['high'] + $counts['medium'] + $counts['low'];
        $counts['warning'] = $counts['low'];

        return $counts;
    }

    private function maybe_create_tables(): void {
        global $wpdb;

        if (
            $this->table_exists($this->audits_table) &&
            $this->table_exists($this->audit_urls_table) &&
            $this->table_exists($this->audit_issues_table)
        ) {
            return;
        }

        require_once ABSPATH . 'wp-admin/includes/upgrade.php';

        $charset_collate = $wpdb->get_charset_collate();

        $sql1 = "CREATE TABLE {$this->audits_table} (
            id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
            audit_id varchar(50) NOT NULL,
            status enum('idle','crawling','checking','completed','failed','stopped','stopping') DEFAULT 'idle',
            started_at datetime DEFAULT NULL,
            completed_at datetime DEFAULT NULL,
            total_urls int(11) DEFAULT 0,
            score int(3) DEFAULT NULL,
            stats longtext,
            options longtext,
            error text,
            PRIMARY KEY  (id),
            UNIQUE KEY audit_id (audit_id),
            KEY status (status),
            KEY started_at (started_at)
        ) $charset_collate;";

        $sql2 = "CREATE TABLE {$this->audit_urls_table} (
            id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
            audit_id varchar(50) NOT NULL,
            url varchar(500) NOT NULL,
            status enum('pending','checking','checked') DEFAULT 'pending',
            depth int(11) DEFAULT 0,
            issues_count int(11) DEFAULT 0,
            warnings_count int(11) DEFAULT 0,
            passed_count int(11) DEFAULT 0,
            score int(3) DEFAULT NULL,
            created_at datetime DEFAULT NULL,
            checked_at datetime DEFAULT NULL,
            PRIMARY KEY  (id),
            KEY audit_id (audit_id),
            KEY status (status),
            KEY url (url(255))
        ) $charset_collate;";

        $sql3 = "CREATE TABLE {$this->audit_issues_table} (
            id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
            audit_id varchar(50) NOT NULL,
            url_id bigint(20) unsigned NOT NULL,
            type varchar(50) NOT NULL,
            severity enum('critical','high','medium','low','passed') DEFAULT 'medium',
            message text,
            data longtext,
            created_at datetime DEFAULT NULL,
            PRIMARY KEY  (id),
            KEY audit_id (audit_id),
            KEY url_id (url_id),
            KEY type (type),
            KEY severity (severity)
        ) $charset_collate;";

        dbDelta($sql1);
        dbDelta($sql2);
        dbDelta($sql3);
    }

    private function table_exists(string $table): bool {
        global $wpdb;
        $found = $wpdb->get_var($wpdb->prepare('SHOW TABLES LIKE %s', $table));
        return $found === $table;
    }
}
