<?php

namespace FleekDash\Core;

use FleekDash\Libs\API\Config;
use FleekDash\Traits\Base;

/**
 * Class API
 *
 * Initializes and configures the API for the FleekDash.
 *
 * @package FleekDash\Core
 */
class API
{
    use Base;

    /**
     * @var Logger Logger instance
     */
    private $logger;

    /**
     * Construtor
     */
    public function __construct()
    {
        $this->logger = Logger::get_instance();
    }

    /**
     * Initializes the API for the FleekDash.
     *
     * @return void
     */
    public function init()
    {
        try {
            $this->registerRestLoggingFilters();
            $this->registerRestCacheFilters();
            $this->registerRestApiBootstrapActions();
        } catch (\Exception $e) {
            $this->logger->exception($e);
        }
    }

    /**
     * @return void
     */
    private function registerRestLoggingFilters()
    {
        \add_filter('rest_pre_dispatch', [$this, 'logApiRequest'], 10, 3);
        \add_filter('rest_post_dispatch', [$this, 'logApiResponse'], 10, 3);
    }

    /**
     * @return void
     */
    private function registerRestCacheFilters(): void
    {
        (new RestApiCacheFilters())->register();
        \add_filter('rest_pre_dispatch', [$this, 'filterRestPreDispatchHardenParams'], 2, 3);
    }

    /**
     * @return void
     */
    private function registerRestApiBootstrapActions()
    {
        \add_action('rest_api_init', function () {
            try {
                Config::set_route_file(\FLEEKDASH_PLUGIN_DIR . 'includes/Routes/Api.php')
                    ->set_namespace(RestApiCacheSupport::API_NAMESPACE)
                    ->init();
            } catch (\Exception $e) {
                $this->logger->error(__('Failed to load API routes: ', 'fleekdash') . $e->getMessage());
                $this->registerFallbackRoutes();
            }
        }, 5);

        \add_action('rest_api_init', function () {
            register_shutdown_function(function () {
                $error = error_get_last();
                if ($error === null) {
                    return;
                }
                $fatalTypes = [E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR];
                if (!in_array($error['type'], $fatalTypes, true)) {
                    return;
                }
                $this->logger->fatal(__('API Fatal Error: ', 'fleekdash') . $error['message'], [
                    'file' => $error['file'],
                    'line' => $error['line'],
                ]);

                if (!\headers_sent()) {
                    \status_header(500);
                    \header('Content-Type: application/json');
                    echo json_encode([
                        'success' => false,
                        'message' => __('Internal server error', 'fleekdash'),
                        'code' => 'server_error',
                    ]);
                    exit;
                }
            });
        });
    }

    /**
     * Clamp pagination / ordering for our namespace and WP core list GETs.
     *
     * @param mixed            $result  Incoming result.
     * @param \WP_REST_Server  $server  REST server.
     * @param \WP_REST_Request $request Request.
     * @return mixed
     */
    public function filterRestPreDispatchHardenParams($result, $server, $request)
    {
        try {
            if (($request instanceof \WP_REST_Request)
                && ($server instanceof \WP_REST_Server)
                && strtoupper($request->get_method()) === 'GET'
            ) {
                $route = (string) $request->get_route();
                $inOurNs = (strpos($route, RestApiCacheSupport::API_NAMESPACE) !== false);
                $inWpNs = (strpos($route, '/wp/v2/') !== false);
                if ($inOurNs || $inWpNs) {
                    $this->clampRestListQueryParams($request, $inOurNs);
                }
            }
        } catch (\Throwable $e) {
            if (defined('WP_DEBUG') && WP_DEBUG) {
                $this->logger->warning(__('REST param hardening error: ', 'fleekdash') . $e->getMessage());
            }
        }

        return $result;
    }

    private function clampRestListQueryParams(\WP_REST_Request $request, bool $inOurNs): void
    {
        $perPage = (int) ($request->get_param('per_page') ?? $request->get_param('perPage') ?? 0);
        if ($perPage) {
            $max = $inOurNs ? 500 : 100;
            $request->set_param('per_page', max(1, min($perPage, $max)));
        }
        $page = (int) ($request->get_param('page') ?? 0);
        if ($page < 1) {
            $request->set_param('page', 1);
        }

        if ($inOurNs) {
            $this->clampFleekdashRestOrderParams($request);
        }
    }

    private function clampFleekdashRestOrderParams(\WP_REST_Request $request): void
    {
        $orderby = (string) ($request->get_param('orderby') ?? 'date');
        $allowedOrderBy = ['date', 'title', 'modified', 'author'];
        if (!in_array(strtolower($orderby), $allowedOrderBy, true)) {
            $request->set_param('orderby', 'date');
        }
        $order = strtoupper((string) ($request->get_param('order') ?? 'DESC'));
        if ($order !== 'ASC' && $order !== 'DESC') {
            $request->set_param('order', 'DESC');
        }
    }

    /**
     * Fallback diagnostic payload when primary route registration failed.
     */
    public function serveFallbackDiagnostic(\WP_REST_Request $request): \WP_REST_Response
    {
        global $wp_rest_server;
        $namespace = RestApiCacheSupport::API_NAMESPACE;
        $routes = $wp_rest_server ? $wp_rest_server->get_routes() : [];
        $our_routes = [];
        foreach (array_keys($routes) as $route) {
            if (strpos($route, $namespace) !== false) {
                $our_routes[] = $route;
            }
        }

        return \rest_ensure_response([
            'success' => true,
            'plugin_loaded' => defined('\FLEEKDASH_PLUGIN_DIR'),
            'api_class_loaded' => class_exists('FleekDash\Core\API'),
            'route_class_loaded' => class_exists('FleekDash\Libs\API\Route'),
            'config_class_loaded' => class_exists('FleekDash\Libs\API\Config'),
            'wordpress_rest_available' => function_exists('register_rest_route'),
            'our_routes_count' => count($our_routes),
            'our_routes' => $our_routes,
            'debug_enabled' => defined('WP_DEBUG') && WP_DEBUG,
            'php_version' => PHP_VERSION,
            'wp_version' => \get_bloginfo('version'),
            'request_route' => $request->get_route(),
        ]);
    }

    /**
     * Register fallback routes in case the main route file fails to load
     */
    private function registerFallbackRoutes()
    {
        try {
            $ns = RestApiCacheSupport::API_NAMESPACE;

            \register_rest_route($ns, '/diagnostic', [
                'methods' => 'GET',
                'callback' => [$this, 'serveFallbackDiagnostic'],
                'permission_callback' => [AuthMiddleware::class, 'createAdminPermissionCallback'],
            ]);

            \register_rest_route($ns, '/search', [
                'methods' => 'GET',
                'callback' => function () {
                    return \rest_ensure_response([
                        'success' => true,
                        'message' => \__('Search endpoint available', 'fleekdash'),
                        'data' => [],
                    ]);
                },
                'permission_callback' => [AuthMiddleware::class, 'createAuthenticatedPermissionCallback'],
            ]);

            \register_rest_route($ns, '/auth/status', [
                'methods' => 'GET',
                'callback' => function () {
                    return \rest_ensure_response([
                        'success' => true,
                        'is_authenticated' => \is_user_logged_in(),
                        'user_id' => \get_current_user_id(),
                    ]);
                },
                'permission_callback' => [AuthMiddleware::class, 'createAuthenticatedPermissionCallback'],
            ]);

            \register_rest_route($ns, '/debug/status', [
                'methods' => 'GET',
                'callback' => function () {
                    return \rest_ensure_response([
                        'success' => true,
                        'debug_enabled' => defined('WP_DEBUG') && WP_DEBUG,
                        'debug_log_enabled' => defined('WP_DEBUG_LOG') && WP_DEBUG_LOG,
                    ]);
                },
                'permission_callback' => [AuthMiddleware::class, 'createAdminPermissionCallback'],
            ]);

            $this->logger->info(__('Fallback routes registered successfully', 'fleekdash'));
        } catch (\Exception $e) {
            $this->logger->error(__('Failed to register fallback routes: ', 'fleekdash') . $e->getMessage());
        }
    }

    /**
     * Optional hook: log REST requests when debug is enabled
     *
     * @param mixed            $result  Dispatch result (null pre-dispatch)
     * @param \WP_REST_Server  $server  REST server
     * @param \WP_REST_Request $request Incoming request
     * @return mixed
     */
    public function logApiRequest($result, $server, $request)
    {
        if ($server instanceof \WP_REST_Server
            && $request instanceof \WP_REST_Request
            && Config::is_debug_log_api()
        ) {
            // Reserved: enable detailed request logging when debugging REST traffic.
        }

        return $result;
    }

    /**
     * Optional hook: log REST responses when debug is enabled
     *
     * @param \WP_REST_Response $response Outgoing response
     * @param \WP_REST_Server   $server   REST server
     * @param \WP_REST_Request  $request  Original request
     * @return \WP_REST_Response
     */
    public function logApiResponse($response, $server, $request)
    {
        if (!($response instanceof \WP_REST_Response)
            || !($server instanceof \WP_REST_Server)
            || !($request instanceof \WP_REST_Request)
        ) {
            return $response;
        }

        if (Config::is_debug_log_api()) {
            $route = $request->get_route();
            $method = $request->get_method();

            if (strpos($route, RestApiCacheSupport::API_NAMESPACE) !== false) {
                $status = $response->get_status();
                // 4xx are expected client/validation outcomes (often shown in UI) — avoid debug.log noise.
                if ($status >= 500) {
                    $this->logger->error("API Response [$method] $route [$status]", [
                        'data' => $response->get_data(),
                    ]);
                }
            }
        }

        return $response;
    }
}
