<?php
/**
 * Cron Jobs - Rapports hebdomadaires et mensuels AVI + Worker de scan mots-clés (Batch 10)
 */

namespace Avision;

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

class Avision_Cron {

    const HOOK_WEEKLY = 'avision_weekly_report';
    const HOOK_MONTHLY = 'avision_monthly_report';
    const HOOK_KEYWORD_SCAN_QUEUE = 'avision_keyword_scan_queue';
    const HOOK_PUBLISH_SCHEDULED = 'avision_publish_scheduled_posts';

    const KEYWORD_SCAN_BATCH_SIZE = 10;

    public function __construct() {
        add_action(self::HOOK_WEEKLY, [$this, 'run_weekly_report']);
        add_action(self::HOOK_MONTHLY, [$this, 'run_monthly_report']);
        add_action(self::HOOK_KEYWORD_SCAN_QUEUE, [$this, 'process_keyword_scan_queue']);
        // [SPRINT40-1A] Désactivé — double cron avi_saas_posts.
        // Ne résout pas les placeholders {name}/{city} et ignore le refresh token.
        // Remplacé par avi_publish_scheduled_bulk_posts dans functions.php (Sprint 39).
        // NE PAS réactiver sans test bulk post complet.
        // add_action( self::HOOK_PUBLISH_SCHEDULED, array( $this, 'publish_scheduled_posts' ) );
        add_action('init', [$this, 'schedule_events']);
        add_filter('cron_schedules', [$this, 'add_minute_schedule']);
    }

    /**
     * Ajoute l'intervalle "toutes les minutes" pour le worker de scan.
     *
     * @param array $schedules
     * @return array
     */
    public function add_minute_schedule($schedules) {
        if (!isset($schedules['every_minute'])) {
            $schedules['every_minute'] = [
                'interval' => 60,
                'display'  => __('Every Minute', 'avision-dashboard'),
            ];
        }
        if (!isset($schedules['avision_15min'])) {
            $schedules['avision_15min'] = [
                'interval' => 900,
                'display'  => __('Every 15 Minutes', 'avision-dashboard'),
            ];
        }
        return $schedules;
    }

    /**
     * Worker de scan par paquets (Batch 10) : sélectionne les 10 mots-clés pending/NULL les plus anciens,
     * appelle DataForSEO pour chaque mot-clé avec les métadonnées du profil (City, Country),
     * met à jour rank_init (si vide), current_rank et passe le statut à completed.
     * Hook : avision_keyword_scan_queue (enregistré dans schedule_events via wp_schedule_event).
     */
    public function process_keyword_scan_queue() {
        if ( function_exists( 'error_log' ) ) {
            error_log( '[AVI CRON] Worker waking up...' );
        }
        if (!class_exists('Avision\Avision_Database')) {
            return;
        }
        $db = new Avision_Database();
        $db->init();
        if (!method_exists($db, 'get_pending_keywords_for_scan') || !method_exists($db, 'run_single_keyword_scan')) {
            return;
        }
        $adaptive = self::KEYWORD_SCAN_BATCH_SIZE;
        if ( class_exists( 'Avision\\Cron_Task_Manager' ) ) {
            $adaptive = \Avision\Cron_Task_Manager::get_adaptive_keyword_batch_size();
        }
        $batch_size = (int) apply_filters( 'avision_keyword_scan_batch_size', $adaptive );
        $batch_size = max( 1, min( 50, $batch_size ) );
        $keywords = $db->get_pending_keywords_for_scan( $batch_size );
        if (empty($keywords)) {
            return;
        }
        $profile_ids_seen = [];
        $scanned = 0;
        foreach ($keywords as $kw) {
            if ( function_exists( 'error_log' ) ) {
                error_log( '[AVI CRON] Processing keyword: ' . ( isset( $kw['keyword'] ) ? $kw['keyword'] : '?' ) );
            }
            $profile_id = isset($kw['profile_id']) ? (int) $kw['profile_id'] : 0;
            if ($profile_id <= 0) {
                continue;
            }
            $profile = $db->get_saas_profile_for_sync($profile_id);
            if (empty($profile)) {
                if (function_exists('error_log')) {
                    error_log('[AVI CRON] Profile ID ' . $profile_id . ' not found, skip keyword ID ' . (isset($kw['id']) ? $kw['id'] : '?'));
                }
                continue;
            }
            // run_single_keyword_scan appelle fetch_dataforseo_maps_sector puis update_keyword_scan_completed (rank_init forcé si NULL).
            $did = $db->run_single_keyword_scan( $kw, $profile );
            if ( $did ) {
                $scanned++;
                $profile_ids_seen[ $profile_id ] = true;
            }
        }
        if ($scanned > 0 && function_exists('error_log')) {
            $ids = array_keys($profile_ids_seen);
            error_log('[AVI CRON] Scanned ' . $scanned . ' keywords for profile ID(s) ' . implode(', ', $ids));
        }
        // Concurrents encore absents : forcer le fallback DataForSEO (thème) sans attendre un second passage first-sync.
        global $wpdb;
        if (isset($wpdb) && $wpdb && function_exists('avi_get_competitors_token_ready_fallback')) {
            foreach (array_keys($profile_ids_seen) as $pid) {
                $pid = (int) $pid;
                if ($pid <= 0) {
                    continue;
                }
                $t = $wpdb->prefix . 'avi_saas_competitors';
                if ($wpdb->get_var($wpdb->prepare('SHOW TABLES LIKE %s', $t)) !== $t) {
                    continue;
                }
                $cnt = (int) $wpdb->get_var($wpdb->prepare("SELECT COUNT(*) FROM `{$t}` WHERE profile_id = %d", $pid));
                if ($cnt === 0) {
                    avi_get_competitors_token_ready_fallback($pid);
                }
            }
        }
    }

    /**
     * Planifie les événements cron
     * Hebdo : lundi 9h | Mensuel : quotidien | Queue scan : toutes les minutes
     */
    public function schedule_events() {
        if (!wp_next_scheduled(self::HOOK_WEEKLY)) {
            $timezone = wp_timezone();
            $monday_9am = new \DateTime('next Monday 9:00', $timezone);
            if ($monday_9am <= new \DateTime('now', $timezone)) {
                $monday_9am->modify('+1 week');
            }
            wp_schedule_event($monday_9am->getTimestamp(), 'weekly', self::HOOK_WEEKLY);
        }
        if (!wp_next_scheduled(self::HOOK_MONTHLY)) {
            wp_schedule_event(time(), 'daily', self::HOOK_MONTHLY);
        }
        if (!wp_next_scheduled(self::HOOK_KEYWORD_SCAN_QUEUE)) {
            wp_schedule_event(time(), 'every_minute', self::HOOK_KEYWORD_SCAN_QUEUE);
            if ( function_exists( 'error_log' ) ) {
                error_log( '[AVI CRON] Hook avision_keyword_scan_queue scheduled (every_minute).' );
            }
        }
        if ( ! wp_next_scheduled( self::HOOK_PUBLISH_SCHEDULED ) ) {
            wp_schedule_event( time(), 'avision_15min', self::HOOK_PUBLISH_SCHEDULED );
            if ( function_exists( 'error_log' ) ) {
                error_log( '[AVI CRON] Hook avision_publish_scheduled_posts planifié (avision_15min).' );
            }
        }
    }
    
    /**
     * Rapport Hebdomadaire - Lundi 9h
     * Condition : isGuardianActive == true
     * Data : completedActions (avis répondus + posts) de la semaine
     */
    public function run_weekly_report() {
        $database = new Avision_Database();
        $database->init();
        $notifications = new Avision_Notifications();
        
        $audits = $database->get_audits_for_weekly_report();
        
        foreach ($audits as $audit) {
            $actions = $database->get_completed_actions_this_week($audit['id']);
            $total = $actions['reviews'] + $actions['posts'];
            if ($total > 0) {
                $notifications->send_weekly_report($audit, $actions);
            }
        }
    }
    
    /**
     * Rapport Mensuel - Tous les 30j après inscription
     * Compare score_at_day_1 et current_score
     * Si nouveau Sprint débloqué → bloc Félicitations
     */
    public function run_monthly_report() {
        $database = new Avision_Database();
        $database->init();
        $notifications = new Avision_Notifications();
        
        $audits = $database->get_audits_for_monthly_report();
        
        foreach ($audits as $audit) {
            list($new_sprint, $sprint_label) = $notifications->has_new_sprint_unlocked(
                $audit['score_at_day_1'] ?? 0,
                $audit['score'] ?? 0
            );
            
            $notifications->send_monthly_report($audit, $new_sprint, $sprint_label);
        }
    }

    /**
     * Publie les posts GBP planifiés dont scheduled_at <= NOW().
     * Hook : avision_publish_scheduled_posts (avision_15min = toutes les 15 min)
     */
    public function publish_scheduled_posts() {
        global $wpdb;
        $table = $wpdb->prefix . 'avi_saas_posts';

        // Vérifier que la colonne scheduled_at existe
        $col = $wpdb->get_var( "SHOW COLUMNS FROM {$table} LIKE 'scheduled_at'" );
        if ( ! $col ) {
            return;
        }

        // Récupérer les posts planifiés en attente
        $posts = $wpdb->get_results( $wpdb->prepare(
            "SELECT p.*, pr.uuid as profile_uuid, pr.user_id
             FROM {$table} p
             INNER JOIN {$wpdb->prefix}avi_saas_profiles pr ON pr.id = p.profile_id
             WHERE p.scheduled_at IS NOT NULL
               AND p.scheduled_at <= %s
               AND p.status IN ('pending', 'approved', 'pending_review')
             ORDER BY p.scheduled_at ASC
             LIMIT 20",
            current_time( 'mysql' )
        ) );

        if ( empty( $posts ) ) {
            return;
        }

        foreach ( $posts as $post ) {
            // Marquer en cours pour éviter double publication
            $wpdb->update(
                $table,
                array( 'status' => 'publishing' ),
                array( 'id' => $post->id ),
                array( '%s' ),
                array( '%d' )
            );

            // Publier via API Google
            $result = false;
            if ( function_exists( 'avi_agency_google_create_local_post_wp_remote' ) ) {
                // Récupérer le token OAuth et la location resource depuis le profil
                $profile_meta = $wpdb->get_row( $wpdb->prepare(
                    "SELECT google_access_token, google_location_name
                     FROM {$wpdb->prefix}avi_saas_profiles
                     WHERE id = %d LIMIT 1",
                    $post->profile_id
                ) );

                if ( $profile_meta && ! empty( $profile_meta->google_access_token ) && ! empty( $profile_meta->google_location_name ) ) {
                    $result = avi_agency_google_create_local_post_wp_remote(
                        $profile_meta->google_access_token,
                        $profile_meta->google_location_name,
                        $post->content,
                        $post->image_url ?? '',
                        'LEARN_MORE',
                        ''
                    );
                    $result = ( $result === true );
                } else {
                    $result = false;
                    if ( function_exists( 'error_log' ) ) {
                        error_log( sprintf(
                            '[AVI CRON] publish_scheduled_posts — profile_id=%d token ou location manquant',
                            $post->profile_id
                        ) );
                    }
                }
            }

            // Mettre à jour le statut
            $new_status = ( $result === true ) ? 'published' : 'failed';
            $wpdb->update(
                $table,
                array(
                    'status'       => $new_status,
                    'published_at' => ( 'published' === $new_status ) ? current_time( 'mysql' ) : null,
                ),
                array( 'id' => $post->id ),
                array( '%s', '%s' ),
                array( '%d' )
            );

            if ( function_exists( 'error_log' ) ) {
                error_log( sprintf(
                    '[AVI CRON] publish_scheduled_posts — profile_id=%d post_id=%d status=%s',
                    $post->profile_id,
                    $post->id,
                    $new_status
                ) );
            }
        }
    }
}
