<?php
if (!defined('ABSPATH')) {
    exit;
}

/**
 * Core AI action registry for the AI Flash Tune base plugin.
 *
 * The base plugin only keeps the actions it directly needs. Paid addons register
 * their own prompts and Zywrap wrapper mappings through the aiflt_ai_action_registry filter.
 */
class AIFLT_AI_Action_Registry {

    /**
     * Return all registered AI actions.
     *
     * @return array<string,array>
     */
    public static function all(): array {
        $actions = [
            'free_dropoff_question' => [
                'label' => __('Ask AI About a Funnel Drop-Off', 'ai-flash-tune'),
                'description' => __('Answer a specific question about a funnel stage using aggregate funnel data.', 'ai-flash-tune'),
                'feature' => 'funnel_analysis',
                'scope' => 'free_limited',
                'privacy' => 'aggregate_only',
                'zywrap_wrapper_code' => 'eco_funnel_dropoff_diagnosis_base',
                'recommended_usecase_title' => 'Funnel Drop-Off Diagnosis',
                'recommended_category_code' => 'ecommerce_conversion_optimization',
                'zywrap_fit' => 'dedicated',
                'min_data' => ['sessions' => 1],
                'system' => implode("\n", [
                    'You are a senior WooCommerce conversion analyst working inside the AI Flash Tune WordPress plugin.',
                    'Answer the selected Funnel Analysis preset using only the supplied sanitized aggregate funnel data.',
                    'Do not invent causes. Do not claim payment problems, trust problems, shipping/tax confusion, mobile issues, pricing issues, product issues, or UX defects unless the supplied data directly supports them.',
                    'If a cause is plausible but not proven, place it under Likely Causes To Verify or What To Verify Next.',
                    'Never call estimated abandoned cart value or lost sales a cause. It is only a directional value-at-risk signal.',
                    'Use the phrase Estimated abandoned cart value instead of Lost sales.',
                    'If total sessions are below 50, call the report early / directional and avoid strong conclusions.',
                    'Do not recommend discounts, free shipping, fake urgency, testimonials, retargeting ads, new payment gateways, heatmaps, surveys, A/B tests, broad redesigns, or competitor analysis as primary actions unless the supplied data supports them.',
                    'For low traffic, recommend one safe change or one verification test, then monitor for 7–14 days or until more sessions are collected.',
                    'Prefer AI Flash Tune pages in Where to check: Funnel Analysis, Checkout Optimizer, Cart Recovery, Product Optimizer, Reports. Mention WooCommerce settings only when needed.',
                    'Every action must be safe, reversible, measurable, and practical for a WooCommerce store owner.',
                    'Each Funnel Analysis preset must produce a clearly different answer: checkout_dropoff = checkout leak explanation; cart_abandonment = abandonment/recovery readiness; fix_first = ranked action list; simple_words = short plain-English explanation.',
                    'Use short headings and bullets. Do not use Markdown tables.',
                    'Return only the answer. No intro, no disclaimer, no extra commentary.',
                ]),
                'max_tokens' => 950,
                'temperature' => 0.15,
            ],
            'free_weekly_ai_report' => [
                'label' => __('Free Weekly AI Report', 'ai-flash-tune'),
                'description' => __('Generate a short weekly store summary from local aggregate funnel stats.', 'ai-flash-tune'),
                'feature' => 'dashboard',
                'scope' => 'free_limited',
                'privacy' => 'aggregate_only',
                'zywrap_wrapper_code' => 'eco_stor_perf_repo_gene_b03f_base',
                'recommended_usecase_title' => 'Store Performance Report Generator',
                'recommended_category_code' => 'ecommerce_conversion_optimization',
                'zywrap_fit' => 'dedicated',
                'min_data' => ['sessions' => 1],
                'system' => implode("\n", [
                    'You are a senior WooCommerce CRO analyst working inside AI Flash Tune.',
                    'Generate a short weekly AI report from supplied local aggregate store signals only.',
                    'Use only supplied numbers. Do not invent revenue, causes, customer motivations, product issues, payment problems, shipping problems, or trust problems.',
                    'If total sessions are below 50, clearly say the report is early / directional.',
                    'Return 3 short sections: Weekly Snapshot, Biggest Visible Risk, One Safe Next Step.',
                    'Use Estimated abandoned cart value, not Lost Sales.',
                    'Do not recommend discounts, A/B tests, heatmaps, surveys, new payment gateways, or redesigns as first actions for low traffic.',
                    'Keep the answer under 120 words. Use bullets only. No Markdown tables.',
                ]),
                'max_tokens' => 160,
                'temperature' => 0.35,
            ],
        ];

        /**
         * Allow addons to register their own AI actions.
         *
         * The base/free plugin keeps only the AI actions it needs directly.
         * Paid addon actions are registered from their own addon packages through this filter.
         *
         * @param array $actions
         */
        return apply_filters('aiflt_ai_action_registry', $actions);
    }


    /**
     * Get a single AI action definition.
     */
    public static function get(string $action_key): array {
        $action_key = sanitize_key($action_key);
        $actions = self::all();
        return isset($actions[$action_key]) && is_array($actions[$action_key]) ? $actions[$action_key] : [];
    }

    public static function exists(string $action_key): bool {
        return !empty(self::get($action_key));
    }

    public static function get_wrapper_code(string $action_key): string {
        $action = self::get($action_key);
        return isset($action['zywrap_wrapper_code']) ? sanitize_text_field((string) $action['zywrap_wrapper_code']) : '';
    }

    public static function get_label(string $action_key): string {
        $action = self::get($action_key);
        return isset($action['label']) ? (string) $action['label'] : $action_key;
    }

    public static function get_privacy_mode(string $action_key): string {
        $action = self::get($action_key);
        return isset($action['privacy']) ? sanitize_key((string) $action['privacy']) : 'aggregate_only';
    }

    public static function get_min_data(string $action_key): array {
        $action = self::get($action_key);
        return isset($action['min_data']) && is_array($action['min_data']) ? $action['min_data'] : [];
    }

    public static function validate_min_data(string $action_key, array $payload = []): array {
        $requirements = self::get_min_data($action_key);
        if (empty($requirements)) {
            return [
                'met' => true,
                'level' => 'not_required',
                'message' => __('No minimum data requirement is defined for this action.', 'ai-flash-tune'),
                'requirements' => [],
                'observed' => [],
            ];
        }

        $observed = [];
        $missing = [];
        foreach ($requirements as $metric => $minimum) {
            $metric = sanitize_key((string) $metric);
            $minimum = max(0, absint($minimum));
            $value = self::extract_metric_value($payload, $metric);
            $observed[$metric] = $value;
            if ($value < $minimum) {
                $missing[$metric] = [
                    'minimum' => $minimum,
                    'observed' => $value,
                ];
            }
        }

        if (empty($missing)) {
            return [
                'met' => true,
                'level' => 'enough_data',
                'message' => __('Minimum data requirement is met.', 'ai-flash-tune'),
                'requirements' => $requirements,
                'observed' => $observed,
            ];
        }

        return [
            'met' => false,
            'level' => 'limited_data',
            'message' => __('Data is below the recommended threshold. Treat AI guidance as early directional advice, not a final conclusion.', 'ai-flash-tune'),
            'requirements' => $requirements,
            'observed' => $observed,
            'missing' => $missing,
        ];
    }

    private static function extract_metric_value(array $payload, string $metric): int {
        $aliases = [
            'sessions' => ['sessions', 'total_sessions', 'visitors', 'visitor_count'],
            'product_views' => ['product_views', 'product_page_views', 'views', 'traffic_views'],
            'abandoned_carts' => ['abandoned_carts', 'total_abandoned_carts', 'cart_abandonments'],
            'checkout_visits' => ['checkout_visits', 'checkouts', 'checkout_starts', 'checkout_page_visits'],
            'cart_entries' => ['cart_entries', 'add_to_carts', 'added_to_cart', 'cart_events'],
        ];
        $keys = $aliases[$metric] ?? [$metric];
        return self::recursive_find_numeric($payload, $keys);
    }

    private static function recursive_find_numeric($value, array $keys): int {
        if (!is_array($value)) {
            return 0;
        }
        $best = 0;
        foreach ($value as $key => $child) {
            $normalized = strtolower(str_replace(['-', ' '], '_', (string) $key));
            if (in_array($normalized, $keys, true) && is_numeric($child)) {
                $best = max($best, absint($child));
            }
            if (is_array($child)) {
                $best = max($best, self::recursive_find_numeric($child, $keys));
            }
        }
        return $best;
    }

    /**
     * Merge caller args with registry defaults for the given action.
     */
    public static function prepare_args(string $action_key, array $args = []): array {
        $action = self::get($action_key);
        $args['task'] = sanitize_key($action_key);

        if (!empty($action['system']) && empty($args['system'])) {
            $args['system'] = (string) $action['system'];
        }
        if (!empty($action['max_tokens']) && empty($args['max_tokens'])) {
            $args['max_tokens'] = absint($action['max_tokens']);
        }
        if (isset($action['temperature']) && !isset($args['temperature'])) {
            $args['temperature'] = (float) $action['temperature'];
        }
        if (!empty($action['zywrap_wrapper_code']) && empty($args['zywrap_wrapper_code'])) {
            $args['zywrap_wrapper_code'] = sanitize_text_field((string) $action['zywrap_wrapper_code']);
        }

        $args['registry_action'] = $action;
        return $args;
    }

    /**
     * Build a safe default user prompt from structured payload data.
     */
    public static function build_user_prompt(string $action_key, array $payload = [], string $instructions = ''): string {
        $action = self::get($action_key);
        $label = !empty($action['label']) ? wp_strip_all_tags((string) $action['label']) : $action_key;
        $parts = [
            'AI action: ' . $label,
        ];
        if ($instructions !== '') {
            $parts[] = 'Instructions: ' . wp_strip_all_tags($instructions);
        }
        $parts[] = "Structured store data JSON:\n" . wp_json_encode($payload, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
        return implode("\n\n", $parts);
    }

    /**
     * Remove obvious private keys before any payload can be sent to external AI.
     */
    public static function sanitize_payload($payload) {
        if (class_exists('AIFLT_AI_Payload_Sanitizer')) {
            return AIFLT_AI_Payload_Sanitizer::sanitize($payload, 'aggregate_only');
        }

        $blocked_keys = [
            'email', 'customer_email', 'billing_email', 'phone', 'billing_phone', 'address', 'address_1', 'address_2',
            'billing_address_1', 'billing_address_2', 'shipping_address_1', 'shipping_address_2', 'ip', 'ip_address',
            'first_name', 'last_name', 'customer_name', 'name', 'user_login', 'password', 'token', 'secret', 'api_key',
        ];

        if (is_array($payload)) {
            $clean = [];
            foreach ($payload as $key => $value) {
                $normalized_key = strtolower((string) $key);
                if (in_array($normalized_key, $blocked_keys, true)) {
                    continue;
                }
                $clean[$key] = self::sanitize_payload($value);
            }
            return $clean;
        }

        if (is_string($payload)) {
            return sanitize_textarea_field($payload);
        }

        if (is_numeric($payload) || is_bool($payload) || is_null($payload)) {
            return $payload;
        }

        return wp_json_encode($payload);
    }
}
