<?php
/**
 * AI Provider Manager
 *
 * Routes AI requests to appropriate provider based on task type and configuration
 *
 * @package WP_Agentic_Writer
 */

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

/**
 * Result object containing provider instance plus selection metadata.
 * Used to satisfy the DoD Provider Transparency contract.
 *
 * @since 0.2.0
 */
class WPAW_Provider_Selection_Result
{
    public $provider; // Provider instance
    public $selected_provider; // Original requested provider name
    public $actual_provider; // Actually used provider name (may differ if fallback)
    public $fallback_used; // True if fallback occurred
    public $warnings; // Array of warning messages

    public function __construct(
        $provider,
        $selected,
        $actual,
        $fallback,
        $warnings = [],
    ) {
        $this->provider = $provider;
        $this->selected_provider = $selected;
        $this->actual_provider = $actual;
        $this->fallback_used = $fallback;
        $this->warnings = $warnings;
    }
}

class WP_Agentic_Writer_Provider_Manager
{
    /**
     * Transient cache TTL for connection test results (5 minutes).
     *
     * @var int
     */
    const CONNECTION_TEST_CACHE_TTL = 300;

    /**
     * Transient cache key prefix for connection tests.
     *
     * @var string
     */
    const CONNECTION_TEST_CACHE_PREFIX = "wpaw_conn_test_";

    /**
     * Get provider instance for specific task type
     *
     * @param string $type Task type (chat, clarity, planning, writing, refinement, image).
     * @return WPAW_Provider_Selection_Result Provider selection result with metadata.
     */
    public static function get_provider_for_task($type)
    {
        $settings = get_option("wp_agentic_writer_settings", []);
        $task_providers = $settings["task_providers"] ?? [];
        $allow_openrouter_fallback = !empty(
            $settings["allow_openrouter_fallback"]
        );

        // Determine which provider to use for this task
        $requested_provider = $task_providers[$type] ?? "openrouter";

        if (defined("WP_DEBUG") && WP_DEBUG) {
            error_log(
                "WPAW Provider Manager: task={$type}, provider_name={$requested_provider}, task_providers=" .
                    json_encode($task_providers),
            );
        }

        $warnings = [];
        $fallback_used = false;
        $actual_provider = $requested_provider;

        // Get provider instance with fallback logic
        $provider = self::get_provider_instance($requested_provider, $type);

        $can_fallback_to_openrouter =
            "openrouter" === $requested_provider || $allow_openrouter_fallback;

        // If provider not configured or unavailable.
        if (!$provider || !$provider->is_configured()) {
            if (defined("WP_DEBUG") && WP_DEBUG) {
                error_log(
                    "Provider '{$requested_provider}' not available for task '{$type}'",
                );
            }

            // Never silently spend OpenRouter credits when user selected another provider.
            if (!$can_fallback_to_openrouter) {
                $warnings[] = "Provider '{$requested_provider}' unavailable. No automatic fallback was applied.";
                return new WPAW_Provider_Selection_Result(
                    $provider,
                    $requested_provider,
                    $requested_provider,
                    false,
                    $warnings,
                );
            }

            $warnings[] = "Provider '{$requested_provider}' unavailable, fell back to OpenRouter";
            $provider = WP_Agentic_Writer_OpenRouter_Provider::get_instance();
            $actual_provider = "openrouter";
            $fallback_used = true;
        }

        // For custom endpoint, verify it's actually reachable before using it.
        // Use transient cache to avoid testing connection on every request (5 min TTL).
        if (
            "local_backend" === $requested_provider &&
            !$fallback_used &&
            method_exists($provider, "test_connection")
        ) {
            $cache_key =
                self::CONNECTION_TEST_CACHE_PREFIX .
                md5($requested_provider . $type);
            $cached_result = get_transient($cache_key);

            if (false !== $cached_result) {
                // Use cached result
                if (is_wp_error($cached_result)) {
                    $test_result = $cached_result;
                } else {
                    // Cached success - skip test entirely
                    if (defined("WP_DEBUG") && WP_DEBUG) {
                        error_log(
                            "WPAW: Using cached connection test result for '{$requested_provider}'",
                        );
                    }
                    $test_result = null; // null means "test passed"
                }
            } else {
                // No cache - run the actual test
                $test_result = $provider->test_connection();

                // Cache the result for CONNECTION_TEST_CACHE_TTL seconds
                set_transient(
                    $cache_key,
                    $test_result,
                    self::CONNECTION_TEST_CACHE_TTL,
                );
            }

            if (isset($test_result) && is_wp_error($test_result)) {
                if (defined("WP_DEBUG") && WP_DEBUG) {
                    error_log(
                        "Custom endpoint not reachable for task '{$type}'. Error: " .
                            $test_result->get_error_message(),
                    );
                }
                if ($can_fallback_to_openrouter) {
                    $warnings[] =
                        "Custom endpoint not reachable, fell back to OpenRouter.";
                    $provider = WP_Agentic_Writer_OpenRouter_Provider::get_instance();
                    $actual_provider = "openrouter";
                    $fallback_used = true;
                } else {
                    $warnings[] =
                        "Custom endpoint not reachable. No automatic fallback was applied.";
                }
            }
        }

        return new WPAW_Provider_Selection_Result(
            $provider,
            $requested_provider,
            $actual_provider,
            $fallback_used,
            $warnings,
        );
    }

    /**
     * Get provider instance by name
     *
     * @param string $provider_name Provider identifier.
     * @param string $task_type     Task type for validation.
     * @return WP_Agentic_Writer_AI_Provider_Interface|null Provider instance or null.
     */
    private static function get_provider_instance($provider_name, $task_type)
    {
        switch ($provider_name) {
            case "local_backend":
                if (!class_exists("WP_Agentic_Writer_Local_Backend_Provider")) {
                    require_once plugin_dir_path(__FILE__) .
                        "class-local-backend-provider.php";
                }
                $provider = new WP_Agentic_Writer_Local_Backend_Provider();
                break;

            case "codex":
                if (!class_exists("WP_Agentic_Writer_Codex_Provider")) {
                    require_once plugin_dir_path(__FILE__) .
                        "class-codex-provider.php";
                }
                $provider = new WP_Agentic_Writer_Codex_Provider();
                break;

            case "openrouter":
            default:
                $provider = WP_Agentic_Writer_OpenRouter_Provider::get_instance();
                break;
        }

        // Validate provider supports this task type
        if ($provider && !$provider->supports_task_type($task_type)) {
            error_log(
                "Provider '{$provider_name}' does not support task type '{$task_type}'",
            );
            return null;
        }

        return $provider;
    }

    /**
     * Get all available providers with their status
     *
     * @return array Array of provider info with name, status, supported tasks.
     */
    public static function get_available_providers()
    {
        $providers = [];

        // OpenRouter (always available)
        $openrouter = WP_Agentic_Writer_OpenRouter_Provider::get_instance();
        $providers["openrouter"] = [
            "name" => "OpenRouter",
            "configured" => $openrouter->is_configured(),
            "supports" => [
                "chat",
                "clarity",
                "planning",
                "writing",
                "refinement",
                "image",
            ],
            "icon" => "☁️",
        ];

        // Custom Endpoint
        if (class_exists("WP_Agentic_Writer_Local_Backend_Provider")) {
            $local = new WP_Agentic_Writer_Local_Backend_Provider();
            $providers["local_backend"] = [
                "name" => "Custom Endpoint",
                "configured" => $local->is_configured(),
                "supports" => [
                    "chat",
                    "clarity",
                    "planning",
                    "writing",
                    "refinement",
                ],
                "icon" => "🏠",
            ];
        }

        // Codex
        if (class_exists("WP_Agentic_Writer_Codex_Provider")) {
            $codex = new WP_Agentic_Writer_Codex_Provider();
            $providers["codex"] = [
                "name" => "Codex (OpenAI)",
                "configured" => $codex->is_configured(),
                "supports" => [
                    "chat",
                    "clarity",
                    "planning",
                    "writing",
                    "refinement",
                ],
                "icon" => "🔗",
            ];
        }

        return $providers;
    }

    /**
     * Test all configured providers
     *
     * @return array Results of connection tests.
     */
    public static function test_all_providers()
    {
        $results = [];
        $providers = self::get_available_providers();

        foreach ($providers as $key => $info) {
            if (!$info["configured"]) {
                $results[$key] = [
                    "success" => false,
                    "message" => "Not configured",
                ];
                continue;
            }

            $provider = self::get_provider_instance($key, "chat");
            if ($provider) {
                $test_result = $provider->test_connection();
                $results[$key] = is_wp_error($test_result)
                    ? [
                        "success" => false,
                        "message" => $test_result->get_error_message(),
                    ]
                    : $test_result;
            }
        }

        return $results;
    }

    /**
     * Clear the connection test transient cache.
     * Call this when local backend settings are updated.
     *
     * @return int Number of cache entries deleted.
     */
    public static function clear_connection_test_cache()
    {
        global $wpdb;

        $prefix = self::CONNECTION_TEST_CACHE_PREFIX;
        $count = 0;

        // Delete all transients with our prefix
        $transients = $wpdb->get_col(
            $wpdb->prepare(
                "SELECT option_name FROM {$wpdb->options} WHERE option_name LIKE %s",
                $wpdb->esc_like("_transient_") . $prefix . "%",
            ),
        );

        foreach ($transients as $transient) {
            // Strip '_transient_' prefix if present
            $key = $transient;
            if (0 === strpos($key, "_transient_")) {
                $key = substr($key, 11);
            }
            if (delete_transient($key)) {
                $count++;
            }
        }

        if (defined("WP_DEBUG") && WP_DEBUG) {
            error_log("WPAW: Cleared {$count} connection test cache entries");
        }

        return $count;
    }
}
