<?php

namespace ZenoChatbot\Framework\Http;

use ZenoChatbot\Framework\Support\Arr;
use ZenoChatbot\Framework\Validation\Validator as DataValidator;
use ZenoChatbot\Framework\Validation\ValidationException;

class Request
{
    /**
     * Request query parameters
     *
     * @var array
     */
    protected $query = [];

    /**
     * Request post data
     *
     * @var array
     */
    protected $request = [];

    /**
     * Request files
     *
     * @var array
     */
    protected $files = [];

    /**
     * Request headers
     *
     * @var array
     */
    protected $headers = [];

    /**
     * Request method
     *
     * @var string
     */
    protected $method;

    /**
     * Constructor
     *
     * @param \WP_REST_Request|null $wp_rest_request WordPress REST request object
     */
    public function __construct($wp_rest_request = null)
    {
        $this->query = $_GET;
        $this->request = $_POST;
        $this->files = $_FILES;
        $this->headers = $this->getRequestHeaders();
        $this->method = $_SERVER['REQUEST_METHOD'] ?? 'GET';

        if ($wp_rest_request instanceof \WP_REST_Request) {
            $this->bindRestRequest($wp_rest_request);
        }
    }

    /**
     * Capture the current request from globals
     *
     * @return static
     */
    public static function capture(): self
    {
        return new static();
    }

    /**
     * Get request headers
     *
     * @return array
     */
    protected function getRequestHeaders()
    {
        $headers = [];
        foreach ($_SERVER as $key => $value) {
            if (strpos($key, 'HTTP_') === 0) {
                $headers[str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', substr($key, 5)))))] = $value;
            }
        }
        return $headers;
    }

    /**
     * Get request method
     *
     * @return string
     */
    public function method()
    {
        return $this->method;
    }

    /**
     * Check if request is ajax
     *
     * @return bool
     */
    public function ajax()
    {
        return isset($this->headers['X-Requested-With']) && $this->headers['X-Requested-With'] === 'XMLHttpRequest';
    }

    /**
     * Get input value
     *
     * @param string $key
     * @param mixed $default
     * @return mixed
     */
    public function input($key, $default = null)
    {
        return $this->request[$key] ?? $this->query[$key] ?? $default;
    }

    /**
     * Get all input data
     *
     * @return array
     */
    public function all()
    {
        return array_merge($this->query, $this->request);
    }

    /**
     * Get query parameter
     *
     * @param string $key
     * @param mixed $default
     * @return mixed
     */
    public function query($key, $default = null)
    {
        return $this->query[$key] ?? $default;
    }

    /**
     * Get file
     *
     * @param string $key
     * @return array|null
     */
    public function file($key)
    {
        return $this->files[$key] ?? null;
    }

    /**
     * Get header value
     *
     * @param string $key
     * @param mixed $default
     * @return mixed
     */
    public function header($key, $default = null)
    {
        return $this->headers[$key] ?? $default;
    }

    /**
     * Check if input exists
     *
     * @param string $key
     * @return bool
     */
    public function has($key)
    {
        return isset($this->request[$key]) || isset($this->query[$key]);
    }

    /**
     * Check if request matches method
     *
     * @param string $method
     * @return bool
     */
    public function isMethod($method)
    {
        return strtoupper($method) === $this->method;
    }

    /**
     * Bind WordPress REST request parameters
     *
     * @param \WP_REST_Request $wp_rest_request
     * @return void
     */
    protected function bindRestRequest(\WP_REST_Request $wp_rest_request)
    {
        $this->query = array_merge($this->query, $wp_rest_request->get_query_params());
        $this->request = array_merge($this->request, $wp_rest_request->get_body_params());
        $this->files = array_merge($this->files, $wp_rest_request->get_file_params());
        $this->headers = array_merge($this->headers, $wp_rest_request->get_headers());
        $this->method = $wp_rest_request->get_method();

        // Also get all parameters (includes route parameters like {id})
        $allParams = $wp_rest_request->get_params();
        $this->request = array_merge($this->request, $allParams);

        // For JSON requests, try to get JSON body
        $contentType = $wp_rest_request->get_content_type();
        if ($contentType && isset($contentType['value']) && $contentType['value'] === 'application/json') {
            $jsonBody = json_decode($wp_rest_request->get_body(), true);
            if (is_array($jsonBody)) {
                $this->request = array_merge($this->request, $jsonBody);
            }
        }
    }

    public function get($key, $default = null)
    {
        return $this->input($key, $default);
    }

    /**
     * Get sanitized input value(s)
     *
     * @param array|string $fields Array of ['key' => 'callback'] or single key
     * @param callable|null $callback Sanitization callback for single key (default: sanitize_text_field)
     * @param mixed $default Default value if key doesn't exist
     * @return array|mixed Array of sanitized values or single sanitized value
     */
    public function getSafe($fields, $callback = null, $default = null)
    {
        // Handle array input: ['key' => 'callback', 'key2' => 'callback2']
        if (is_array($fields)) {
            $result = [];
            foreach ($fields as $key => $sanitizer) {
                $value = $this->input($key, $default);

                // Determine the sanitizer callback
                if (is_callable($sanitizer)) {
                    $result[$key] = call_user_func($sanitizer, $value);
                } elseif (is_string($sanitizer) && function_exists($sanitizer)) {
                    $result[$key] = call_user_func($sanitizer, $value);
                } else {
                    // Default to sanitize_text_field
                    $result[$key] = sanitize_text_field($value);
                }
            }
            return $result;
        }

        // Handle string input: getSafe('key', 'callback')
        if (is_string($fields)) {
            $value = $this->input($fields, $default);

            // Determine the sanitizer callback
            if (is_callable($callback)) {
                return call_user_func($callback, $value);
            } elseif (is_string($callback) && function_exists($callback)) {
                return call_user_func($callback, $value);
            } else {
                // Default to sanitize_text_field
                return sanitize_text_field($value);
            }
        }

        throw new \InvalidArgumentException('First parameter must be array or string');
    }

    /**
     * Get current page number for pagination
     *
     * @param string $pageName
     * @return int
     */
    public function page(string $pageName = 'page'): int
    {
        $page = $this->input($pageName, 1);
        return max(1, (int) $page);
    }

    /**
     * Get per page value for pagination
     *
     * @param int $default
     * @param int $max
     * @return int
     */
    public function perPage(int $default = 15, int $max = 100): int
    {
        $perPage = $this->input('per_page', $default);
        return min($max, max(1, (int) $perPage));
    }

    /**
     * Get the current URL for pagination
     *
     * @return string
     */
    public function url(): string
    {
        $scheme = $this->isSecure() ? 'https' : 'http';
        $host = $_SERVER['HTTP_HOST'] ?? 'localhost';
        $uri = $_SERVER['REQUEST_URI'] ?? '/';

        return $scheme . '://' . $host . $uri;
    }

    /**
     * Get the current path for pagination
     *
     * @return string
     */
    public function path(): string
    {
        $uri = $_SERVER['REQUEST_URI'] ?? '/';
        return strtok($uri, '?');
    }

    /**
     * Check if request is secure (HTTPS)
     *
     * @return bool
     */
    public function isSecure(): bool
    {
        return (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off')
            || $_SERVER['SERVER_PORT'] == 443
            || (!empty($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https');
    }

    public function only($keys = [])
    {
        return Arr::only($this->all(), $keys);
    }

    /**
     * Validate the current request inputs against the given rules.
     *
     * Usage:
     *   $validated = $request->validate([
     *       'email' => 'required|email',
     *       'age'   => ['nullable', 'integer', 'min:18']
     *   ], [
     *       'email.required' => 'Email is required.',
     *   ]);
     *
     * @param array<string, string|array<int, string>> $rules
     * @param array<string, string> $messages
     * @return array<string, mixed>
     * @throws ValidationException
     */
    public function validate(array $rules, array $messages = []): array
    {
        $validator = DataValidator::make($this->all(), $rules, $messages);
        if ($validator->fails()) {
            throw new ValidationException('The given data was invalid.', $validator->errors());
        }
        return $validator->validated();
    }

}
