<?php
declare(strict_types=1);
// ==================================================
// Nuclear Fancy Pants Edition™ -- NUCLEAR+++
// File: /home/ploi/automarketer-ai.com/public/wp-content/plugins/automarketer-ai/includes/class-competitor-gap.php
// Description: Competitor Gap feature -- analyzes competitor URLs, saves them, queues gap topics for blog generator - NUCLEAR+++
// Last Edited: 2026-05-18
// ==================================================

namespace AutoMarketerAI;

defined('ABSPATH') || exit;

final class CompetitorGap
{
    // Register AJAX hooks
    public static function register(): void
    {
        add_action('wp_ajax_amai_competitor_gap_analyze',   [self::class, 'handleAnalyze']);
        add_action('wp_ajax_amai_competitor_gap_queue',     [self::class, 'handleQueue']);
        add_action('wp_ajax_amai_competitor_gap_dequeue',   [self::class, 'handleDequeue']);
        add_action('wp_ajax_amai_competitor_gap_save',      [self::class, 'handleSave']);
        add_action('wp_ajax_amai_competitor_gap_delete',    [self::class, 'handleDelete']);
        add_action('wp_ajax_amai_competitor_gap_reanalyze', [self::class, 'handleReanalyzeAll']);
        add_action('wp_ajax_amai_competitor_reviews_analyze', [self::class, 'handleReviewsAnalyze']);
    }

    // --------------------------------------------------
    // AJAX -- analyze a single competitor URL
    // --------------------------------------------------
    public static function handleAnalyze(): void
    {
        check_ajax_referer('amai_competitor_gap', 'nonce');
        if (!current_user_can('edit_posts')) {
            wp_send_json_error(['message' => 'Unauthorized.'], 403);
        }

        $url = esc_url_raw(trim((string) wp_unslash($_POST['url'] ?? '')));
        if ($url === '' || !filter_var($url, FILTER_VALIDATE_URL)) {
            wp_send_json_error(['message' => 'Please enter a valid URL.'], 400);
        }

        $scheme = strtolower((string) parse_url($url, PHP_URL_SCHEME));
        if (!in_array($scheme, ['http', 'https'], true)) {
            wp_send_json_error(['message' => 'URL must start with http or https.'], 400);
        }

        $gaps = self::callCoreAnalyze($url);
        if ($gaps === null) {
            wp_send_json_error(['message' => 'Analysis failed. Make sure the URL is publicly accessible and try again.'], 502);
        }

        $queue_count = GapTopicQueue::count();

        wp_send_json_success([
            'gaps'        => $gaps,
            'queue_count' => $queue_count,
            'queue_full'  => $queue_count >= GapTopicQueue::MAX_QUEUE,
        ]);
    }

    // --------------------------------------------------
    // AJAX -- queue a gap topic for the blog generator
    // --------------------------------------------------
    public static function handleQueue(): void
    {
        check_ajax_referer('amai_competitor_gap', 'nonce');
        if (!current_user_can('edit_posts')) {
            wp_send_json_error(['message' => 'Unauthorized.'], 403);
        }

        $topic          = sanitize_text_field(wp_unslash((string) ($_POST['topic']          ?? '')));
        $competitor_url = esc_url_raw(trim((string) wp_unslash($_POST['competitor_url'] ?? '')));

        if ($topic === '') {
            wp_send_json_error(['message' => 'Missing topic.'], 400);
        }

        $new_count = GapTopicQueue::push($topic, $competitor_url);

        if ($new_count === null) {
            $current = GapTopicQueue::count();
            if ($current >= GapTopicQueue::MAX_QUEUE) {
                wp_send_json_error([
                    'message' => 'Queue is full (10 topics). Publish or remove a topic to add more.',
                    'queue_full' => true,
                ], 422);
            }
            wp_send_json_error(['message' => 'This topic is already in the queue.'], 422);
        }

        wp_send_json_success([
            'queue_count' => $new_count,
            'queue_full'  => $new_count >= GapTopicQueue::MAX_QUEUE,
            'queued'      => $topic,
        ]);
    }

    // --------------------------------------------------
    // AJAX -- remove a topic from the queue by ID
    // --------------------------------------------------
    public static function handleDequeue(): void
    {
        check_ajax_referer('amai_competitor_gap', 'nonce');
        if (!current_user_can('edit_posts')) {
            wp_send_json_error(['message' => 'Unauthorized.'], 403);
        }

        $id = (int) ($_POST['id'] ?? 0);
        if ($id <= 0) {
            wp_send_json_error(['message' => 'Invalid ID.'], 400);
        }

        $deleted = GapTopicQueue::delete($id);
        if (!$deleted) {
            wp_send_json_error(['message' => 'Topic not found.'], 404);
        }

        $new_count = GapTopicQueue::count();

        wp_send_json_success([
            'deleted'     => $id,
            'queue_count' => $new_count,
            'queue_full'  => $new_count >= GapTopicQueue::MAX_QUEUE,
        ]);
    }

    // --------------------------------------------------
    // AJAX -- save a competitor URL
    // --------------------------------------------------
    public static function handleSave(): void
    {
        check_ajax_referer('amai_competitor_gap', 'nonce');
        if (!current_user_can('edit_posts')) {
            wp_send_json_error(['message' => 'Unauthorized.'], 403);
        }

        $url   = esc_url_raw(trim((string) wp_unslash($_POST['url']   ?? '')));
        $label = sanitize_text_field(wp_unslash((string) ($_POST['label'] ?? '')));

        if ($url === '' || !filter_var($url, FILTER_VALIDATE_URL)) {
            wp_send_json_error(['message' => 'Invalid URL.'], 400);
        }

        if ($label === '') {
            $label = (string) parse_url($url, PHP_URL_HOST);
        }

        global $wpdb;
        $table = $wpdb->prefix . 'amai_competitor_urls';

        $existing = $wpdb->get_var($wpdb->prepare(
            "SELECT id FROM `{$table}` WHERE url = %s LIMIT 1",
            $url
        ));

        if ($existing) {
            $wpdb->update($table, ['label' => $label], ['id' => (int) $existing]);
            $id = (int) $existing;
        } else {
            $wpdb->insert($table, [
                'url'        => $url,
                'label'      => $label,
                'created_at' => current_time('mysql', true),
            ]);
            $id = (int) $wpdb->insert_id;
        }

        wp_send_json_success(['id' => $id, 'label' => $label, 'url' => $url]);
    }

    // --------------------------------------------------
    // AJAX -- delete a saved competitor URL
    // --------------------------------------------------
    public static function handleDelete(): void
    {
        check_ajax_referer('amai_competitor_gap', 'nonce');
        if (!current_user_can('edit_posts')) {
            wp_send_json_error(['message' => 'Unauthorized.'], 403);
        }

        $id = (int) ($_POST['id'] ?? 0);
        if ($id <= 0) {
            wp_send_json_error(['message' => 'Invalid ID.'], 400);
        }

        global $wpdb;
        $wpdb->delete($wpdb->prefix . 'amai_competitor_urls', ['id' => $id]);
        wp_send_json_success(['deleted' => $id]);
    }

    // --------------------------------------------------
    // AJAX -- re-analyze all saved competitors
    // --------------------------------------------------
    public static function handleReanalyzeAll(): void
    {
        check_ajax_referer('amai_competitor_gap', 'nonce');
        if (!current_user_can('edit_posts')) {
            wp_send_json_error(['message' => 'Unauthorized.'], 403);
        }

        global $wpdb;
        $table = $wpdb->prefix . 'amai_competitor_urls';
        $rows  = $wpdb->get_results("SELECT id, url, label FROM `{$table}` ORDER BY created_at ASC");

        if (empty($rows)) {
            wp_send_json_error(['message' => 'No saved competitors to analyze.'], 400);
        }

        $all_gaps = [];
        foreach ($rows as $row) {
            $gaps = self::callCoreAnalyze((string) $row->url);
            if ($gaps === null) {
                continue;
            }
            foreach ($gaps as $gap) {
                $gap['source']          = (string) $row->label;
                $gap['competitor_url']  = (string) $row->url;
                $all_gaps[]             = $gap;
            }
            $wpdb->update($table, ['last_analyzed_at' => current_time('mysql', true)], ['id' => (int) $row->id]);
        }

        if (empty($all_gaps)) {
            wp_send_json_error(['message' => 'Could not fetch any competitor pages. Try again later.'], 502);
        }

        $seen    = [];
        $deduped = [];
        foreach ($all_gaps as $gap) {
            $key = strtolower(trim((string) ($gap['topic'] ?? '')));
            if ($key === '' || isset($seen[$key])) {
                continue;
            }
            $seen[$key] = true;
            $deduped[]  = $gap;
        }

        $queue_count = GapTopicQueue::count();

        wp_send_json_success([
            'gaps'        => $deduped,
            'queue_count' => $queue_count,
            'queue_full'  => $queue_count >= GapTopicQueue::MAX_QUEUE,
        ]);
    }

    // --------------------------------------------------
    // Call Core /api/seo/competitor-gap
    // --------------------------------------------------
    public static function callCoreAnalyze(string $url): ?array
    {
        $published = get_posts([
            'post_type'   => 'post',
            'post_status' => 'publish',
            'numberposts' => -1,
            'fields'      => 'ids',
        ]);
        $published_titles = [];
        foreach ((array) $published as $pid) {
            $t = get_the_title((int) $pid);
            if ($t !== '') {
                $published_titles[] = $t;
            }
        }

        $profile           = (new BusinessProfile())->get();
        $business_name     = get_bloginfo('name');
        $business_category = '';
        if (is_array($profile)) {
            $cat_map           = BusinessProfile::BUSINESS_CATEGORIES;
            $cat_key           = (string) ($profile['business_category'] ?? '');
            $business_category = $cat_map[$cat_key] ?? $cat_key;
        }

        $payload = json_encode([
            'url'               => $url,
            'published_titles'  => $published_titles,
            'business_name'     => $business_name,
            'business_category' => $business_category,
        ]);

        if ($payload === false) {
            return null;
        }

        $method    = 'POST';
        $path      = '/api/seo/competitor-gap';
        $core_url  = rtrim((string) AMAI_CORE_URL, '/') . $path;
        $timestamp = (string) time();
        $nonce     = bin2hex(random_bytes(16));
        $tenant    = amai_tenant_id();
        $key_id    = 'WP_PROXY';

        $raw_secret = (string) get_option('amai_tenant_hmac_secret', '');
        if ($raw_secret === '') {
            return null;
        }

        $secret    = hex2bin($raw_secret);
        $body_b64  = base64_encode(hash('sha256', $payload, true));
        $canonical = implode('|', [$method, $path, $timestamp, $nonce, $tenant, $key_id, $body_b64]);
        $signature = rtrim(base64_encode(hash_hmac('sha256', $canonical, $secret, true)), "\n");

        $response = wp_remote_post($core_url, [
            'timeout' => 30,
            'headers' => [
                'Content-Type'       => 'application/json',
                'X-Amai-Tenant'      => $tenant,
                'X-Amai-Key-Id'      => $key_id,
                'X-Amai-Timestamp'   => $timestamp,
                'X-Amai-Nonce'       => $nonce,
                'X-Amai-Body-SHA256' => $body_b64,
                'X-Amai-Signature'   => $signature,
            ],
            'body' => $payload,
        ]);

        if (is_wp_error($response)) {
            return null;
        }

        $code = (int) wp_remote_retrieve_response_code($response);
        $data = json_decode(wp_remote_retrieve_body($response), true);

        if ($code !== 200 || !is_array($data) || empty($data['gaps'])) {
            return null;
        }

        return (array) $data['gaps'];
    }

    // --------------------------------------------------
    // Return saved competitors
    // --------------------------------------------------
    // --------------------------------------------------
    // AJAX -- analyze competitor Google review complaints
    // --------------------------------------------------
    public static function handleReviewsAnalyze(): void
    {
        check_ajax_referer('amai_competitor_gap', 'nonce');
        if (!current_user_can('edit_posts')) {
            wp_send_json_error(['message' => 'Unauthorized.'], 403);
        }

        $domain = sanitize_text_field(wp_unslash((string) ($_POST['domain'] ?? '')));
        $domain = ltrim(trim($domain), 'www.');
        if ($domain === '') {
            wp_send_json_error(['message' => 'Please enter a competitor domain.'], 400);
        }

        $opportunities = self::callCoreReviews($domain);
        if ($opportunities === null) {
            wp_send_json_error(['message' => 'Analysis failed. Please try again.'], 502);
        }

        $queue_count = GapTopicQueue::count();
        wp_send_json_success([
            'opportunities' => $opportunities,
            'queue_count'    => $queue_count,
            'queue_full'     => $queue_count >= GapTopicQueue::MAX_QUEUE,
        ]);
    }

    // --------------------------------------------------
    // Call Core /api/seo/competitor-reviews
    // --------------------------------------------------
    public static function callCoreReviews(string $domain): ?array
    {
        $profile           = (new BusinessProfile())->get();
        $business_name     = get_bloginfo('name');
        $business_category = '';
        if (is_array($profile)) {
            $cat_map           = BusinessProfile::BUSINESS_CATEGORIES;
            $cat_key           = (string) ($profile['business_category'] ?? '');
            $business_category = $cat_map[$cat_key] ?? $cat_key;
        }

        $payload = json_encode([
            'domain'            => $domain,
            'business_name'     => $business_name,
            'business_category' => $business_category,
        ]);
        if ($payload === false) {
            return null;
        }

        $method    = 'POST';
        $path      = '/api/seo/competitor-reviews';
        $core_url  = rtrim((string) AMAI_CORE_URL, '/') . $path;
        $timestamp = (string) time();
        $nonce     = bin2hex(random_bytes(16));
        $tenant    = amai_tenant_id();
        $key_id    = 'WP_PROXY';
        $raw_secret = (string) get_option('amai_tenant_hmac_secret', '');
        if ($raw_secret === '') {
            return null;
        }
        $secret    = hex2bin($raw_secret);
        $body_b64  = base64_encode(hash('sha256', $payload, true));
        $canonical = implode('|', [$method, $path, $timestamp, $nonce, $tenant, $key_id, $body_b64]);
        $signature = rtrim(base64_encode(hash_hmac('sha256', $canonical, $secret, true)), "\n");

        $response = wp_remote_post($core_url, [
            'timeout' => 30,
            'headers' => [
                'Content-Type'       => 'application/json',
                'X-Amai-Tenant'      => $tenant,
                'X-Amai-Key-Id'      => $key_id,
                'X-Amai-Timestamp'   => $timestamp,
                'X-Amai-Nonce'       => $nonce,
                'X-Amai-Body-SHA256' => $body_b64,
                'X-Amai-Signature'   => $signature,
            ],
            'body' => $payload,
        ]);

        if (is_wp_error($response)) {
            return null;
        }

        $code = (int) wp_remote_retrieve_response_code($response);
        $data = json_decode(wp_remote_retrieve_body($response), true);

        if ($code !== 200 || !is_array($data) || empty($data['opportunities'])) {
            return null;
        }

        return (array) $data['opportunities'];
    }

    public static function getSavedCompetitors(): array
    {
        global $wpdb;
        $table = $wpdb->prefix . 'amai_competitor_urls';
        if ($wpdb->get_var("SHOW TABLES LIKE '{$table}'") !== $table) {
            return [];
        }
        $rows = $wpdb->get_results(
            "SELECT id, url, label, last_analyzed_at FROM `{$table}` ORDER BY created_at ASC"
        );
        return is_array($rows) ? $rows : [];
    }

    // --------------------------------------------------
    // Find next available Tuesday or Thursday
    // --------------------------------------------------
    public static function nextAvailableSlot(): string
    {
        $now      = new \DateTimeImmutable('now', new \DateTimeZone('UTC'));
        $hour     = (int) $now->format('G');
        $occupied = self::getOccupiedDates();

        for ($i = 0; $i < 84; $i++) {
            $candidate   = $now->modify('+' . $i . ' days');
            $day_of_week = (int) $candidate->format('N');
            if (!in_array($day_of_week, [2, 4], true)) {
                continue;
            }
            $date_str = $candidate->format('Y-m-d');
            if ($date_str === $now->format('Y-m-d') && $hour >= 12) {
                continue;
            }
            if (!in_array($date_str, $occupied, true)) {
                return $date_str;
            }
        }

        return $now->modify('next tuesday')->format('Y-m-d');
    }

    // --------------------------------------------------
    // Return occupied Y-m-d dates
    // --------------------------------------------------
    private static function getOccupiedDates(): array
    {
        global $wpdb;
        $rows = $wpdb->get_col(
            "SELECT DISTINCT DATE(post_date) FROM {$wpdb->posts}"
            . " WHERE post_type = 'post'"
            . " AND post_status IN ('publish', 'draft', 'future', 'pending')"
            . " AND post_date >= CURDATE()"
        );
        $meta_dates = $wpdb->get_col(
            "SELECT DISTINCT meta_value FROM {$wpdb->postmeta}"
            . " INNER JOIN {$wpdb->posts} ON {$wpdb->posts}.ID = {$wpdb->postmeta}.post_id"
            . " WHERE meta_key = '_amai_blog_scheduled_date'"
            . " AND {$wpdb->posts}.post_status IN ('publish', 'draft', 'future', 'pending')"
        );
        $all = array_merge(is_array($rows) ? $rows : [], is_array($meta_dates) ? $meta_dates : []);
        return array_values(array_unique(array_filter($all)));
    }
}