<?php
/**
 * REST API Controller for Maple Local Fonts.
 *
 * @package Maple_Local_Fonts
 */

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

/**
 * Class MLF_Rest_Controller
 *
 * Provides REST API endpoints for font management.
 */
class MLF_Rest_Controller extends WP_REST_Controller {

    /**
     * Namespace for the API.
     *
     * @var string
     */
    protected $namespace = 'mlf/v1';

    /**
     * Resource name.
     *
     * @var string
     */
    protected $rest_base = 'fonts';

    /**
     * Rate limiter instance.
     *
     * @var MLF_Rate_Limiter
     */
    private $rate_limiter;

    /**
     * Constructor.
     */
    public function __construct() {
        $this->rate_limiter = new MLF_Rate_Limiter(10, 60);
    }

    /**
     * Register the routes for the controller.
     */
    public function register_routes() {
        // GET /wp-json/mlf/v1/fonts - List all fonts
        register_rest_route(
            $this->namespace,
            '/' . $this->rest_base,
            [
                [
                    'methods'             => WP_REST_Server::READABLE,
                    'callback'            => [$this, 'get_items'],
                    'permission_callback' => [$this, 'get_items_permissions_check'],
                ],
                [
                    'methods'             => WP_REST_Server::CREATABLE,
                    'callback'            => [$this, 'create_item'],
                    'permission_callback' => [$this, 'create_item_permissions_check'],
                    'args'                => $this->get_create_item_args(),
                ],
                'schema' => [$this, 'get_public_item_schema'],
            ]
        );

        // DELETE /wp-json/mlf/v1/fonts/{id} - Delete a font
        register_rest_route(
            $this->namespace,
            '/' . $this->rest_base . '/(?P<id>[\d]+)',
            [
                [
                    'methods'             => WP_REST_Server::DELETABLE,
                    'callback'            => [$this, 'delete_item'],
                    'permission_callback' => [$this, 'delete_item_permissions_check'],
                    'args'                => [
                        'id' => [
                            'description' => __('Unique identifier for the font.', 'maple-local-fonts'),
                            'type'        => 'integer',
                            'required'    => true,
                        ],
                    ],
                ],
            ]
        );

        // GET /wp-json/mlf/v1/fonts/{id} - Get a single font
        register_rest_route(
            $this->namespace,
            '/' . $this->rest_base . '/(?P<id>[\d]+)',
            [
                [
                    'methods'             => WP_REST_Server::READABLE,
                    'callback'            => [$this, 'get_item'],
                    'permission_callback' => [$this, 'get_items_permissions_check'],
                    'args'                => [
                        'id' => [
                            'description' => __('Unique identifier for the font.', 'maple-local-fonts'),
                            'type'        => 'integer',
                            'required'    => true,
                        ],
                    ],
                ],
            ]
        );
    }

    /**
     * Check if a given request has access to get items.
     *
     * @param WP_REST_Request $request Full data about the request.
     * @return bool|WP_Error True if the request has read access, WP_Error object otherwise.
     */
    public function get_items_permissions_check($request) {
        $capability = function_exists('mlf_get_capability') ? mlf_get_capability() : 'edit_theme_options';
        if (!current_user_can($capability)) {
            return new WP_Error(
                'rest_forbidden',
                __('Sorry, you are not allowed to view fonts.', 'maple-local-fonts'),
                ['status' => rest_authorization_required_code()]
            );
        }
        return true;
    }

    /**
     * Check if a given request has access to create items.
     *
     * @param WP_REST_Request $request Full data about the request.
     * @return bool|WP_Error True if the request has create access, WP_Error object otherwise.
     */
    public function create_item_permissions_check($request) {
        $capability = function_exists('mlf_get_capability') ? mlf_get_capability() : 'edit_theme_options';
        if (!current_user_can($capability)) {
            return new WP_Error(
                'rest_forbidden',
                __('Sorry, you are not allowed to create fonts.', 'maple-local-fonts'),
                ['status' => rest_authorization_required_code()]
            );
        }

        // Rate limit check
        if (!$this->rate_limiter->check_and_record('rest_create')) {
            return new WP_Error(
                'rest_rate_limited',
                __('Too many requests. Please wait a moment and try again.', 'maple-local-fonts'),
                ['status' => 429]
            );
        }

        return true;
    }

    /**
     * Check if a given request has access to delete a specific item.
     *
     * @param WP_REST_Request $request Full data about the request.
     * @return bool|WP_Error True if the request has delete access, WP_Error object otherwise.
     */
    public function delete_item_permissions_check($request) {
        $capability = function_exists('mlf_get_capability') ? mlf_get_capability() : 'edit_theme_options';
        if (!current_user_can($capability)) {
            return new WP_Error(
                'rest_forbidden',
                __('Sorry, you are not allowed to delete fonts.', 'maple-local-fonts'),
                ['status' => rest_authorization_required_code()]
            );
        }

        // Rate limit check
        if (!$this->rate_limiter->check_and_record('rest_delete')) {
            return new WP_Error(
                'rest_rate_limited',
                __('Too many requests. Please wait a moment and try again.', 'maple-local-fonts'),
                ['status' => 429]
            );
        }

        return true;
    }

    /**
     * Get a collection of fonts.
     *
     * @param WP_REST_Request $request Full data about the request.
     * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
     */
    public function get_items($request) {
        $registry = new MLF_Font_Registry();
        $fonts = $registry->get_imported_fonts();

        $data = [];
        foreach ($fonts as $font) {
            $data[] = $this->prepare_item_for_response($font, $request);
        }

        return rest_ensure_response($data);
    }

    /**
     * Get a single font.
     *
     * @param WP_REST_Request $request Full data about the request.
     * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
     */
    public function get_item($request) {
        $font_id = absint($request->get_param('id'));
        $font = get_post($font_id);

        if (!$font || $font->post_type !== 'wp_font_family') {
            return new WP_Error(
                'rest_font_not_found',
                __('Font not found.', 'maple-local-fonts'),
                ['status' => 404]
            );
        }

        // Verify it's one we imported
        if (get_post_meta($font_id, '_mlf_imported', true) !== '1') {
            return new WP_Error(
                'rest_font_not_found',
                __('Font not found.', 'maple-local-fonts'),
                ['status' => 404]
            );
        }

        $registry = new MLF_Font_Registry();
        $fonts = $registry->get_imported_fonts();
        $font_data = null;

        foreach ($fonts as $f) {
            if ($f['id'] === $font_id) {
                $font_data = $f;
                break;
            }
        }

        if (!$font_data) {
            return new WP_Error(
                'rest_font_not_found',
                __('Font not found.', 'maple-local-fonts'),
                ['status' => 404]
            );
        }

        return rest_ensure_response($this->prepare_item_for_response($font_data, $request));
    }

    /**
     * Create a font.
     *
     * @param WP_REST_Request $request Full data about the request.
     * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
     */
    public function create_item($request) {
        $font_name = sanitize_text_field($request->get_param('font_name'));
        $weights = array_map('absint', (array) $request->get_param('weights'));
        $styles = array_map('sanitize_text_field', (array) $request->get_param('styles'));

        // Validate font name
        if (!preg_match('/^[a-zA-Z0-9\s\-]+$/', $font_name)) {
            return new WP_Error(
                'rest_invalid_param',
                __('Invalid font name: only letters, numbers, spaces, and hyphens allowed.', 'maple-local-fonts'),
                ['status' => 400]
            );
        }

        if (strlen($font_name) > 100) {
            return new WP_Error(
                'rest_invalid_param',
                __('Font name is too long.', 'maple-local-fonts'),
                ['status' => 400]
            );
        }

        // Validate weights
        $allowed_weights = [100, 200, 300, 400, 500, 600, 700, 800, 900];
        $weights = array_intersect($weights, $allowed_weights);

        if (empty($weights)) {
            return new WP_Error(
                'rest_invalid_param',
                __('At least one valid weight is required.', 'maple-local-fonts'),
                ['status' => 400]
            );
        }

        if (count($weights) > MLF_MAX_WEIGHTS_PER_FONT) {
            return new WP_Error(
                'rest_invalid_param',
                __('Too many weights selected.', 'maple-local-fonts'),
                ['status' => 400]
            );
        }

        // Validate styles
        $allowed_styles = ['normal', 'italic'];
        $styles = array_filter($styles, function($style) use ($allowed_styles) {
            return in_array($style, $allowed_styles, true);
        });

        if (empty($styles)) {
            return new WP_Error(
                'rest_invalid_param',
                __('At least one valid style is required.', 'maple-local-fonts'),
                ['status' => 400]
            );
        }

        try {
            $downloader = new MLF_Font_Downloader();
            $download_result = $downloader->download($font_name, $weights, $styles);

            if (is_wp_error($download_result)) {
                return new WP_Error(
                    'rest_download_failed',
                    $download_result->get_error_message(),
                    ['status' => 400]
                );
            }

            // Register font with WordPress
            $registry = new MLF_Font_Registry();
            $result = $registry->register_font(
                $download_result['font_name'],
                $download_result['font_slug'],
                $download_result['files']
            );

            if (is_wp_error($result)) {
                return new WP_Error(
                    'rest_register_failed',
                    $result->get_error_message(),
                    ['status' => 400]
                );
            }

            // Return the created font
            $fonts = $registry->get_imported_fonts();
            $created_font = null;
            foreach ($fonts as $font) {
                if ($font['id'] === $result) {
                    $created_font = $font;
                    break;
                }
            }

            $response = rest_ensure_response($this->prepare_item_for_response($created_font, $request));
            $response->set_status(201);
            $response->header('Location', rest_url(sprintf('%s/%s/%d', $this->namespace, $this->rest_base, $result)));

            return $response;
        } catch (Exception $e) {
            error_log('MLF REST Create Error: ' . sanitize_text_field($e->getMessage()));
            return new WP_Error(
                'rest_internal_error',
                __('An unexpected error occurred.', 'maple-local-fonts'),
                ['status' => 500]
            );
        }
    }

    /**
     * Delete a font.
     *
     * @param WP_REST_Request $request Full data about the request.
     * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
     */
    public function delete_item($request) {
        $font_id = absint($request->get_param('id'));
        $font = get_post($font_id);

        if (!$font || $font->post_type !== 'wp_font_family') {
            return new WP_Error(
                'rest_font_not_found',
                __('Font not found.', 'maple-local-fonts'),
                ['status' => 404]
            );
        }

        // Verify it's one we imported
        if (get_post_meta($font_id, '_mlf_imported', true) !== '1') {
            return new WP_Error(
                'rest_cannot_delete',
                __('Cannot delete fonts not imported by this plugin.', 'maple-local-fonts'),
                ['status' => 403]
            );
        }

        try {
            $registry = new MLF_Font_Registry();
            $result = $registry->delete_font($font_id);

            if (is_wp_error($result)) {
                return new WP_Error(
                    'rest_delete_failed',
                    $result->get_error_message(),
                    ['status' => 400]
                );
            }

            return rest_ensure_response([
                'deleted' => true,
                'message' => __('Font deleted successfully.', 'maple-local-fonts'),
            ]);
        } catch (Exception $e) {
            error_log('MLF REST Delete Error: ' . sanitize_text_field($e->getMessage()));
            return new WP_Error(
                'rest_internal_error',
                __('An unexpected error occurred.', 'maple-local-fonts'),
                ['status' => 500]
            );
        }
    }

    /**
     * Prepare a font for the REST response.
     *
     * @param array           $font    Font data.
     * @param WP_REST_Request $request Request object.
     * @return array Prepared font data.
     */
    public function prepare_item_for_response($font, $request) {
        return [
            'id'       => $font['id'],
            'name'     => $font['name'],
            'slug'     => $font['slug'],
            'variants' => $font['variants'],
            '_links'   => [
                'self' => [
                    ['href' => rest_url(sprintf('%s/%s/%d', $this->namespace, $this->rest_base, $font['id']))],
                ],
                'collection' => [
                    ['href' => rest_url(sprintf('%s/%s', $this->namespace, $this->rest_base))],
                ],
            ],
        ];
    }

    /**
     * Get the argument schema for creating items.
     *
     * @return array Arguments schema.
     */
    protected function get_create_item_args() {
        return [
            'font_name' => [
                'description'       => __('The font family name from Google Fonts.', 'maple-local-fonts'),
                'type'              => 'string',
                'required'          => true,
                'sanitize_callback' => 'sanitize_text_field',
            ],
            'weights' => [
                'description' => __('Array of font weights to download.', 'maple-local-fonts'),
                'type'        => 'array',
                'required'    => true,
                'items'       => [
                    'type' => 'integer',
                    'enum' => [100, 200, 300, 400, 500, 600, 700, 800, 900],
                ],
            ],
            'styles' => [
                'description' => __('Array of font styles to download.', 'maple-local-fonts'),
                'type'        => 'array',
                'required'    => true,
                'items'       => [
                    'type' => 'string',
                    'enum' => ['normal', 'italic'],
                ],
            ],
        ];
    }

    /**
     * Get the font schema.
     *
     * @return array Schema definition.
     */
    public function get_item_schema() {
        return [
            '$schema'    => 'http://json-schema.org/draft-04/schema#',
            'title'      => 'font',
            'type'       => 'object',
            'properties' => [
                'id' => [
                    'description' => __('Unique identifier for the font.', 'maple-local-fonts'),
                    'type'        => 'integer',
                    'context'     => ['view'],
                    'readonly'    => true,
                ],
                'name' => [
                    'description' => __('The font family name.', 'maple-local-fonts'),
                    'type'        => 'string',
                    'context'     => ['view'],
                ],
                'slug' => [
                    'description' => __('The font slug.', 'maple-local-fonts'),
                    'type'        => 'string',
                    'context'     => ['view'],
                ],
                'variants' => [
                    'description' => __('Array of font variants.', 'maple-local-fonts'),
                    'type'        => 'array',
                    'context'     => ['view'],
                    'items'       => [
                        'type'       => 'object',
                        'properties' => [
                            'weight' => [
                                'type' => 'string',
                            ],
                            'style' => [
                                'type' => 'string',
                            ],
                        ],
                    ],
                ],
            ],
        ];
    }
}
