<?php
/**
 * DBA_TIS_Analyzer — AI Content Classification Engine
 *
 * The "neural network" brain of TIS. Sends batches of page content
 * to OpenAI (via the existing DBA_OpenAI class) and classifies each
 * page into weighted topic categories.
 *
 * Classification pipeline:
 *   1. Group pages into batches of 5 (fit within token limits)
 *   2. Send batch + topic taxonomy to GPT-4o for classification
 *   3. Aggregate per-page scores into site-wide topic distribution
 *   4. Compute topic density, coverage gaps, and trend data
 *
 * Topic taxonomy is configurable via the admin page.
 * Default taxonomy covers Damalion's core business verticals.
 *
 * @since 2.17.0
 */
if ( ! defined( 'ABSPATH' ) ) exit;

class DBA_TIS_Analyzer {

    /** How many pages per AI batch (keep under token limit) */
    const BATCH_SIZE = 5;

    /** Option key for custom topic taxonomy */
    const TAXONOMY_KEY = 'dba_tis_taxonomy';

    /** Option key for scan results history */
    const RESULTS_KEY = 'dba_tis_scan_results';

    /** Max scans to keep in history */
    const MAX_HISTORY = 24;

    /** Progress transient key */
    const PROGRESS_KEY = 'dba_tis_progress';

    /* ══════════════════════════════════════════════════════════════
     *  DEFAULT TOPIC TAXONOMY
     * ══════════════════════════════════════════════════════════════ */

    public static function get_default_taxonomy() {
        return array(
            'company_formation' => array(
                'label'    => 'Company Formation & Registration',
                'keywords' => array( 'company formation', 'incorporation', 'business registration', 'create a company', 'setting up a company', 'LLC', 'S.A.', 'S.à r.l.', 'GmbH', 'SAS', 'limited company' ),
            ),
            'soparfi' => array(
                'label'    => 'SOPARFI & Holding Companies',
                'keywords' => array( 'soparfi', 'holding company', 'société de participations financières', 'participation exemption', 'holding structure', 'parent-subsidiary directive' ),
            ),
            'investment_funds' => array(
                'label'    => 'Investment Funds & Fund Management',
                'keywords' => array( 'investment fund', 'fund management', 'fund formation', 'fund setup', 'fund structuring', 'asset management', 'fund administration', 'ManCo', 'management company' ),
            ),
            'raif' => array(
                'label'    => 'RAIF (Reserved Alternative Investment Fund)',
                'keywords' => array( 'RAIF', 'reserved alternative investment fund', 'fonds d\'investissement alternatif réservé', 'FIAR', 'alternative fund' ),
            ),
            'ucits_sicav' => array(
                'label'    => 'UCITS / SICAV / SIF',
                'keywords' => array( 'UCITS', 'SICAV', 'SIF', 'specialized investment fund', 'undertakings for collective investment', 'Part II fund', 'AIFMD' ),
            ),
            'sicar_securitization' => array(
                'label'    => 'SICAR & Securitization',
                'keywords' => array( 'SICAR', 'société d\'investissement en capital à risque', 'securitization', 'SPV', 'special purpose vehicle', 'risk capital' ),
            ),
            'spf' => array(
                'label'    => 'SPF (Private Wealth Management)',
                'keywords' => array( 'SPF', 'société de gestion de patrimoine familial', 'private wealth', 'family wealth management', 'family office' ),
            ),
            'banking_finance' => array(
                'label'    => 'Banking & Financial Services',
                'keywords' => array( 'bank account', 'banking', 'payment institution', 'e-money', 'PSF', 'financial services', 'CSSF', 'financial license' ),
            ),
            'tax_advisory' => array(
                'label'    => 'Tax Advisory & Compliance',
                'keywords' => array( 'tax', 'taxation', 'fiscal', 'VAT', 'transfer pricing', 'tax treaty', 'double tax', 'tax planning', 'BEPS', 'substance', 'CRS', 'FATCA' ),
            ),
            'luxembourg_jurisdiction' => array(
                'label'    => 'Luxembourg Jurisdiction & Ecosystem',
                'keywords' => array( 'Luxembourg', 'Grand Duchy', 'CSSF', 'ALFI', 'financial center', 'EU hub', 'Luxembourg advantage', 'business in Luxembourg' ),
            ),
            'corporate_governance' => array(
                'label'    => 'Corporate Governance & Domiciliation',
                'keywords' => array( 'corporate governance', 'domiciliation', 'registered office', 'board of directors', 'nominee director', 'corporate secretary', 'AGM' ),
            ),
            'immigration' => array(
                'label'    => 'Immigration & Visa Services',
                'keywords' => array( 'immigration', 'visa', 'work permit', 'residence permit', 'digital nomad', 'expatriate', 'relocation' ),
            ),
            'real_estate' => array(
                'label'    => 'Real Estate & Property',
                'keywords' => array( 'real estate', 'property', 'SCI', 'OPCI', 'real estate fund', 'property investment' ),
            ),
            'intellectual_property' => array(
                'label'    => 'Intellectual Property',
                'keywords' => array( 'intellectual property', 'IP', 'trademark', 'patent', 'copyright', 'IP box', 'brand protection' ),
            ),
            'accounting' => array(
                'label'    => 'Accounting & Bookkeeping',
                'keywords' => array( 'accounting', 'bookkeeping', 'audit', 'payroll', 'financial statements', 'annual accounts', 'RCS filing' ),
            ),
            'private_equity' => array(
                'label'    => 'Private Equity & Venture Capital',
                'keywords' => array( 'private equity', 'venture capital', 'PE fund', 'VC fund', 'carried interest', 'LP', 'GP', 'limited partnership', 'SCSp', 'SCS' ),
            ),
            'international_expansion' => array(
                'label'    => 'International Business & Expansion',
                'keywords' => array( 'international', 'cross-border', 'global expansion', 'branch office', 'subsidiary', 'multinational', 'EU market access' ),
            ),
            'esg_sustainability' => array(
                'label'    => 'ESG & Sustainable Finance',
                'keywords' => array( 'ESG', 'sustainable finance', 'SFDR', 'taxonomy regulation', 'green bond', 'impact investing', 'Article 8', 'Article 9' ),
            ),
            'startup_entrepreneurship' => array(
                'label'    => 'Startups & Entrepreneurship',
                'keywords' => array( 'startup', 'entrepreneur', 'innovation', 'incubator', 'accelerator', 'seed funding', 'angel investor', 'tech hub' ),
            ),
            'other' => array(
                'label'    => 'Other / General',
                'keywords' => array(),
            ),
        );
    }

    /**
     * Get the active taxonomy (custom or default).
     */
    public static function get_taxonomy() {
        $custom = get_option( self::TAXONOMY_KEY, array() );
        if ( ! empty( $custom ) ) {
            return $custom;
        }
        return self::get_default_taxonomy();
    }

    /**
     * Save a custom taxonomy.
     */
    public static function save_taxonomy( $taxonomy ) {
        update_option( self::TAXONOMY_KEY, $taxonomy, false );
    }

    /* ══════════════════════════════════════════════════════════════
     *  PROGRESS TRACKING
     * ══════════════════════════════════════════════════════════════ */

    private static function progress( $step, $total, $label, $detail = '' ) {
        $pct = $total > 0 ? round( ( $step / $total ) * 100 ) : 0;
        set_transient( self::PROGRESS_KEY, array(
            'step'   => $step,
            'total'  => $total,
            'pct'    => $pct,
            'label'  => $label,
            'detail' => $detail,
            'time'   => current_time( 'mysql' ),
        ), 1800 );
        error_log( "DBA TIS [{$step}/{$total}] {$label}" . ( $detail ? " — {$detail}" : '' ) );
    }

    public static function get_progress() {
        return get_transient( self::PROGRESS_KEY ) ?: array(
            'pct' => 0, 'label' => 'Idle', 'step' => 0, 'total' => 0,
        );
    }

    public static function clear_progress() {
        delete_transient( self::PROGRESS_KEY );
    }

    /* ══════════════════════════════════════════════════════════════
     *  MAIN SCAN — Full site topic analysis
     * ══════════════════════════════════════════════════════════════ */

    /**
     * Run a full site scan: discover → classify → aggregate → store.
     *
     * @param  array $options  Optional: language, post_types, use_ai.
     * @return array           Complete scan results.
     */
    public static function run_scan( $options = array() ) {
        $start = microtime( true );
        $taxonomy = self::get_taxonomy();

        // Step 1: Discover all content
        self::progress( 1, 10, 'Discovering content...', 'Querying database' );
        $pages = DBA_TIS_Crawler::discover_local( $options );

        if ( empty( $pages ) ) {
            self::clear_progress();
            return new WP_Error( 'no_content', 'No published pages or posts found.' );
        }

        $total_pages = count( $pages );
        self::progress( 2, 10, 'Found ' . $total_pages . ' pages', 'Starting classification' );

        // Step 2: Keyword-based classification (fast, no API cost)
        self::progress( 3, 10, 'Keyword classification...', 'Scoring ' . $total_pages . ' pages' );
        $page_scores = self::classify_by_keywords( $pages, $taxonomy );

        // Step 3: AI-powered deep classification (optional, for accuracy)
        $use_ai = ! empty( $options['use_ai'] );
        if ( $use_ai ) {
            self::progress( 4, 10, 'AI deep analysis...', 'Sending batches to GPT-4o' );
            $ai_scores = self::classify_by_ai( $pages, $taxonomy );

            // Merge: AI scores get 70% weight, keyword scores get 30%
            if ( ! is_wp_error( $ai_scores ) && ! empty( $ai_scores ) ) {
                foreach ( $page_scores as $id => &$scores ) {
                    if ( isset( $ai_scores[ $id ] ) ) {
                        foreach ( $scores as $topic => &$score ) {
                            $ai_val = $ai_scores[ $id ][ $topic ] ?? 0;
                            $score = round( ( $score * 0.3 ) + ( $ai_val * 0.7 ), 1 );
                        }
                    }
                }
                unset( $scores, $score );
            }
        }

        // Step 4: Aggregate site-wide topic distribution
        self::progress( 6, 10, 'Aggregating results...', 'Computing site-wide distribution' );
        $distribution = self::aggregate_distribution( $page_scores, $taxonomy );

        // Step 5: Get content stats
        self::progress( 7, 10, 'Computing statistics...', 'Word counts, categories, bot sources' );
        $content_stats = DBA_TIS_Crawler::get_content_stats();

        // Step 6: Compute keyword coverage for target themes
        self::progress( 8, 10, 'Analyzing target keyword coverage...', 'Checking strategic gaps' );
        $target_analysis = self::analyze_target_coverage( $pages, $page_scores, $taxonomy );

        // Step 7: Compile final results
        self::progress( 9, 10, 'Compiling report...', 'Almost done' );

        $elapsed = round( microtime( true ) - $start, 2 );

        $results = array(
            'scan_date'       => current_time( 'mysql' ),
            'scan_duration'   => $elapsed,
            'scan_language'   => $options['language'] ?? 'en',
            'total_pages'     => $total_pages,
            'used_ai'         => $use_ai,
            'distribution'    => $distribution,
            'content_stats'   => $content_stats,
            'target_analysis' => $target_analysis,
            'page_scores'     => $page_scores,  // Full per-page data
            'taxonomy_used'   => array_map( function( $t ) { return $t['label']; }, $taxonomy ),
        );

        // Store in history
        self::store_scan( $results );

        self::progress( 10, 10, 'Scan complete!', $total_pages . ' pages analyzed in ' . $elapsed . 's' );

        return $results;
    }

    /* ══════════════════════════════════════════════════════════════
     *  KEYWORD-BASED CLASSIFICATION (fast, free)
     * ══════════════════════════════════════════════════════════════ */

    /**
     * Score each page against topic keywords.
     * Returns array[ post_id => [ topic_slug => score ] ].
     */
    public static function classify_by_keywords( $pages, $taxonomy ) {
        $results = array();

        foreach ( $pages as $page ) {
            $text = mb_strtolower(
                $page['title'] . ' ' .
                $page['meta_desc'] . ' ' .
                $page['content'] . ' ' .
                implode( ' ', array_column( $page['headings'], 'text' ) ) . ' ' .
                implode( ' ', $page['categories'] )
            );

            $scores = array();
            $total  = 0;

            foreach ( $taxonomy as $slug => $topic ) {
                $score = 0;
                foreach ( $topic['keywords'] as $kw ) {
                    $kw_lower = mb_strtolower( $kw );
                    // Count occurrences
                    $count = mb_substr_count( $text, $kw_lower );
                    if ( $count > 0 ) {
                        // Weight by keyword length (longer = more specific = higher value)
                        $weight = max( 1, strlen( $kw ) / 5 );

                        // Bonus for title match
                        if ( mb_strpos( mb_strtolower( $page['title'] ), $kw_lower ) !== false ) {
                            $count += 5;
                        }

                        // Bonus for heading match
                        foreach ( $page['headings'] as $h ) {
                            if ( mb_strpos( mb_strtolower( $h['text'] ), $kw_lower ) !== false ) {
                                $count += 3;
                                break;
                            }
                        }

                        // Bonus for focus keyword match
                        if ( ! empty( $page['focus_keyword'] ) &&
                             mb_strpos( mb_strtolower( $page['focus_keyword'] ), $kw_lower ) !== false ) {
                            $count += 10;
                        }

                        $score += $count * $weight;
                    }
                }
                $scores[ $slug ] = $score;
                $total += $score;
            }

            // Normalize to percentages (0-100)
            if ( $total > 0 ) {
                foreach ( $scores as $slug => &$s ) {
                    $s = round( ( $s / $total ) * 100, 1 );
                }
                unset( $s );
            }

            $results[ $page['id'] ] = $scores;
            $results[ $page['id'] ]['_meta'] = array(
                'title'      => $page['title'],
                'url'        => $page['url'],
                'type'       => $page['type'],
                'word_count' => $page['word_count'],
                'bot_source' => $page['bot_source'],
                'categories' => $page['categories'],
            );
        }

        return $results;
    }

    /* ══════════════════════════════════════════════════════════════
     *  AI-POWERED CLASSIFICATION (accurate, uses API tokens)
     * ══════════════════════════════════════════════════════════════ */

    /**
     * Use GPT-4o to classify page content into topics.
     * Processes in batches to stay within token limits.
     */
    public static function classify_by_ai( $pages, $taxonomy ) {
        if ( ! class_exists( 'DBA_OpenAI' ) ) {
            return new WP_Error( 'no_openai', 'DBA_OpenAI class not available.' );
        }

        $openai   = DBA_OpenAI::get_instance();
        $results  = array();
        $batches  = array_chunk( $pages, self::BATCH_SIZE );
        $total_b  = count( $batches );
        $topic_list = array();
        foreach ( $taxonomy as $slug => $t ) {
            if ( $slug === 'other' ) continue;
            $topic_list[] = $slug . ': ' . $t['label'];
        }
        $topic_str = implode( "\n", $topic_list );

        foreach ( $batches as $b_idx => $batch ) {
            self::progress(
                4 + ( $b_idx / $total_b ) * 2,
                10,
                'AI analysis batch ' . ( $b_idx + 1 ) . '/' . $total_b,
                count( $batch ) . ' pages'
            );

            // Build batch prompt
            $pages_desc = '';
            $batch_ids  = array();
            foreach ( $batch as $idx => $page ) {
                $batch_ids[] = $page['id'];
                $headings_str = implode( ' | ', array_column( $page['headings'], 'text' ) );
                $content_preview = mb_substr( $page['content'], 0, 1500 );
                $pages_desc .= "\n--- PAGE " . ( $idx + 1 ) . " (ID: {$page['id']}) ---\n";
                $pages_desc .= "Title: {$page['title']}\n";
                $pages_desc .= "URL: {$page['url']}\n";
                $pages_desc .= "Type: {$page['type']}\n";
                $pages_desc .= "Categories: " . implode( ', ', $page['categories'] ) . "\n";
                $pages_desc .= "Headings: {$headings_str}\n";
                $pages_desc .= "Content preview: {$content_preview}\n";
            }

            $system = "You are a content classification AI for Damalion, a Luxembourg-based corporate and fund services firm. "
                     . "Classify each page into the topic categories below. For each page, assign percentage weights (must sum to 100). "
                     . "Focus on what the page is PRIMARILY about. Luxembourg jurisdiction should only score high if the page is specifically about Luxembourg advantages/ecosystem, not just because the service is offered from Luxembourg.\n\n"
                     . "TOPIC CATEGORIES:\n{$topic_str}\nother: Other / General\n\n"
                     . "Respond with ONLY valid JSON, no markdown, no backticks. Format:\n"
                     . '{"pages":[{"id":123,"scores":{"company_formation":40,"soparfi":30,...,"other":5}},...]}';

            $user = "Classify these pages:\n" . $pages_desc;

            // Call OpenAI
            $settings = get_option( 'dba_settings', array() );
            $api_key  = $settings['openai_api_key'] ?? '';
            if ( empty( $api_key ) ) {
                error_log( 'DBA TIS: No OpenAI API key configured.' );
                continue;
            }

            $response = wp_remote_post( 'https://api.openai.com/v1/chat/completions', array(
                'timeout' => 60,
                'headers' => array(
                    'Authorization' => 'Bearer ' . $api_key,
                    'Content-Type'  => 'application/json',
                ),
                'body' => wp_json_encode( array(
                    'model'       => 'gpt-4o',
                    'messages'    => array(
                        array( 'role' => 'system', 'content' => $system ),
                        array( 'role' => 'user',   'content' => $user ),
                    ),
                    'temperature' => 0.2,
                    'max_tokens'  => 2000,
                ) ),
            ) );

            if ( is_wp_error( $response ) ) {
                error_log( 'DBA TIS AI: API error — ' . $response->get_error_message() );
                continue;
            }

            $body = json_decode( wp_remote_retrieve_body( $response ), true );
            $text = $body['choices'][0]['message']['content'] ?? '';

            // Parse JSON response
            $text = preg_replace( '/```json|```/', '', $text );
            $text = trim( $text );
            $parsed = json_decode( $text, true );

            if ( $parsed && isset( $parsed['pages'] ) ) {
                foreach ( $parsed['pages'] as $p ) {
                    $pid = (int) $p['id'];
                    if ( in_array( $pid, $batch_ids ) && isset( $p['scores'] ) ) {
                        $results[ $pid ] = $p['scores'];
                    }
                }
            } else {
                error_log( 'DBA TIS AI: Failed to parse batch ' . ( $b_idx + 1 ) . ' response.' );
            }

            // Rate limit: 2 second delay between batches
            if ( $b_idx < $total_b - 1 ) {
                sleep( 2 );
            }
        }

        error_log( 'DBA TIS AI: Classified ' . count( $results ) . '/' . count( $pages ) . ' pages via AI.' );
        return $results;
    }

    /* ══════════════════════════════════════════════════════════════
     *  AGGREGATION — Site-wide topic distribution
     * ══════════════════════════════════════════════════════════════ */

    /**
     * Aggregate per-page scores into site-wide topic distribution.
     */
    public static function aggregate_distribution( $page_scores, $taxonomy ) {
        $totals      = array();
        $page_counts = array();
        $valid_pages = 0;

        foreach ( $taxonomy as $slug => $t ) {
            $totals[ $slug ]      = 0;
            $page_counts[ $slug ] = 0;
        }

        foreach ( $page_scores as $pid => $scores ) {
            if ( isset( $scores['_meta'] ) ) {
                // Skip _meta key
                $topic_scores = $scores;
                unset( $topic_scores['_meta'] );
            } else {
                $topic_scores = $scores;
            }

            $valid_pages++;
            foreach ( $topic_scores as $slug => $score ) {
                if ( isset( $totals[ $slug ] ) ) {
                    $totals[ $slug ] += $score;
                    if ( $score > 15 ) { // Page is "about" this topic if >15%
                        $page_counts[ $slug ]++;
                    }
                }
            }
        }

        // Normalize totals to percentages
        $grand_total = array_sum( $totals );
        $distribution = array();

        foreach ( $taxonomy as $slug => $t ) {
            $pct = $grand_total > 0 ? round( ( $totals[ $slug ] / $grand_total ) * 100, 1 ) : 0;
            $distribution[ $slug ] = array(
                'label'       => $t['label'],
                'percentage'  => $pct,
                'raw_score'   => round( $totals[ $slug ], 1 ),
                'page_count'  => $page_counts[ $slug ],
                'avg_score'   => $valid_pages > 0 ? round( $totals[ $slug ] / $valid_pages, 1 ) : 0,
            );
        }

        // Sort by percentage descending
        uasort( $distribution, function( $a, $b ) {
            return $b['percentage'] <=> $a['percentage'];
        } );

        return $distribution;
    }

    /* ══════════════════════════════════════════════════════════════
     *  TARGET KEYWORD COVERAGE ANALYSIS
     * ══════════════════════════════════════════════════════════════ */

    /**
     * Analyze how well the site covers strategic target keywords/topics.
     */
    public static function analyze_target_coverage( $pages, $page_scores, $taxonomy ) {
        $targets = DBA_TIS_Recommender::get_targets();
        $results = array();

        foreach ( $targets as $target ) {
            $slug        = $target['topic_slug'];
            $target_kws  = array_map( 'mb_strtolower', $target['keywords'] );

            // Count pages that cover this target
            $covering_pages = array();
            $total_coverage = 0;

            foreach ( $page_scores as $pid => $scores ) {
                if ( isset( $scores['_meta'] ) && isset( $scores[ $slug ] ) ) {
                    if ( $scores[ $slug ] > 10 ) {
                        $covering_pages[] = array(
                            'id'        => $pid,
                            'title'     => $scores['_meta']['title'],
                            'url'       => $scores['_meta']['url'],
                            'score'     => $scores[ $slug ],
                            'bot'       => $scores['_meta']['bot_source'],
                        );
                        $total_coverage += $scores[ $slug ];
                    }
                }
            }

            // Sort by score descending
            usort( $covering_pages, function( $a, $b ) {
                return $b['score'] <=> $a['score'];
            } );

            // Check keyword presence in titles + headings
            $kw_in_titles = 0;
            $kw_in_headings = 0;
            foreach ( $pages as $page ) {
                $title_lower = mb_strtolower( $page['title'] );
                foreach ( $target_kws as $kw ) {
                    if ( mb_strpos( $title_lower, $kw ) !== false ) {
                        $kw_in_titles++;
                        break;
                    }
                }
                foreach ( $page['headings'] as $h ) {
                    foreach ( $target_kws as $kw ) {
                        if ( mb_strpos( mb_strtolower( $h['text'] ), $kw ) !== false ) {
                            $kw_in_headings++;
                            break 2;
                        }
                    }
                }
            }

            $results[ $target['id'] ] = array(
                'target'          => $target,
                'covering_pages'  => count( $covering_pages ),
                'top_pages'       => array_slice( $covering_pages, 0, 10 ),
                'avg_coverage'    => count( $covering_pages ) > 0
                    ? round( $total_coverage / count( $covering_pages ), 1 ) : 0,
                'kw_in_titles'    => $kw_in_titles,
                'kw_in_headings'  => $kw_in_headings,
                'coverage_grade'  => self::compute_coverage_grade(
                    count( $covering_pages ), $kw_in_titles, $kw_in_headings, count( $pages )
                ),
            );
        }

        return $results;
    }

    /**
     * Compute a coverage grade (A-F) for a target keyword.
     */
    private static function compute_coverage_grade( $covering_pages, $kw_in_titles, $kw_in_headings, $total_pages ) {
        $score = 0;

        // Pages covering the topic (max 40 points)
        $score += min( 40, $covering_pages * 4 );

        // Keyword in titles (max 30 points)
        $score += min( 30, $kw_in_titles * 6 );

        // Keyword in headings (max 20 points)
        $score += min( 20, $kw_in_headings * 2 );

        // Content ratio bonus (max 10 points)
        if ( $total_pages > 0 ) {
            $ratio = $covering_pages / $total_pages;
            $score += min( 10, $ratio * 200 );
        }

        if ( $score >= 80 ) return 'A';
        if ( $score >= 60 ) return 'B';
        if ( $score >= 40 ) return 'C';
        if ( $score >= 20 ) return 'D';
        return 'F';
    }

    /* ══════════════════════════════════════════════════════════════
     *  SCAN HISTORY
     * ══════════════════════════════════════════════════════════════ */

    /**
     * Store a scan in history (minus the full page_scores to save space).
     */
    public static function store_scan( $results ) {
        $history = get_option( self::RESULTS_KEY, array() );

        // Store a lightweight version (no per-page data)
        $lightweight = $results;
        unset( $lightweight['page_scores'] );

        array_unshift( $history, $lightweight );
        $history = array_slice( $history, 0, self::MAX_HISTORY );

        update_option( self::RESULTS_KEY, $history, false );

        // Also store the latest full results separately
        update_option( 'dba_tis_latest_scan', $results, false );

        error_log( 'DBA TIS: Scan stored. History now has ' . count( $history ) . ' entries.' );
    }

    /**
     * Get scan history.
     */
    public static function get_history() {
        return get_option( self::RESULTS_KEY, array() );
    }

    /**
     * Get the latest full scan results.
     */
    public static function get_latest_scan() {
        return get_option( 'dba_tis_latest_scan', null );
    }

    /**
     * Compute trend between two scans.
     */
    public static function compute_trends( $current, $previous ) {
        if ( empty( $previous ) || empty( $current ) ) return array();

        $trends = array();
        foreach ( $current['distribution'] as $slug => $data ) {
            $prev_pct = $previous['distribution'][ $slug ]['percentage'] ?? 0;
            $curr_pct = $data['percentage'];
            $delta = round( $curr_pct - $prev_pct, 1 );

            $trends[ $slug ] = array(
                'label'     => $data['label'],
                'current'   => $curr_pct,
                'previous'  => $prev_pct,
                'delta'     => $delta,
                'direction' => $delta > 0.5 ? 'up' : ( $delta < -0.5 ? 'down' : 'stable' ),
            );
        }

        return $trends;
    }
}
