<?php
/**
 * SEO REST Controller — Settings + URL Redirects.
 *
 * Routes:
 *   GET    /titancart/v1/seo/settings        Get SEO settings
 *   PUT    /titancart/v1/seo/settings        Save SEO settings
 *   GET    /titancart/v1/seo/redirects       List redirects (paginated)
 *   POST   /titancart/v1/seo/redirects       Create redirect
 *   PUT    /titancart/v1/seo/redirects/{id}  Update redirect
 *   DELETE /titancart/v1/seo/redirects/{id}  Delete redirect
 *   GET    /titancart/v1/seo/robots          Get robots.txt content
 *   PUT    /titancart/v1/seo/robots          Save robots.txt content
 *
 * @package TitanCart\REST\Seo
 */

namespace TitanCart\REST\Seo;

use TitanCart\DB\Database;
use TitanCart\Services\SeoService;
use WP_REST_Server;
use WP_REST_Request;
use WP_REST_Response;
use WP_Error;

defined( 'ABSPATH' ) || exit;

class SeoController {

    private string $namespace = 'titancart/v1';
    private Database $db;

    public function __construct() {
        $this->db = titancart_db();
    }

    public function register_routes(): void {

        register_rest_route( $this->namespace, '/seo/settings', [
            [ 'methods' => WP_REST_Server::READABLE, 'callback' => [ $this, 'get_settings' ], 'permission_callback' => [ $this, 'admin_permission' ] ],
            [ 'methods' => WP_REST_Server::EDITABLE,  'callback' => [ $this, 'save_settings' ], 'permission_callback' => [ $this, 'admin_permission' ] ],
        ] );

        register_rest_route( $this->namespace, '/seo/redirects', [
            [ 'methods' => WP_REST_Server::READABLE, 'callback' => [ $this, 'list_redirects' ], 'permission_callback' => [ $this, 'admin_permission' ] ],
            [ 'methods' => WP_REST_Server::CREATABLE, 'callback' => [ $this, 'create_redirect' ], 'permission_callback' => [ $this, 'admin_permission' ] ],
        ] );

        register_rest_route( $this->namespace, '/seo/redirects/(?P<id>\d+)', [
            [ 'methods' => WP_REST_Server::EDITABLE,  'callback' => [ $this, 'update_redirect' ], 'permission_callback' => [ $this, 'admin_permission' ] ],
            [ 'methods' => WP_REST_Server::DELETABLE, 'callback' => [ $this, 'delete_redirect' ], 'permission_callback' => [ $this, 'admin_permission' ] ],
        ] );

        register_rest_route( $this->namespace, '/seo/robots', [
            [ 'methods' => WP_REST_Server::READABLE, 'callback' => [ $this, 'get_robots' ], 'permission_callback' => [ $this, 'admin_permission' ] ],
            [ 'methods' => WP_REST_Server::EDITABLE,  'callback' => [ $this, 'save_robots' ], 'permission_callback' => [ $this, 'admin_permission' ] ],
        ] );
    }

    // ══════════════════════════════════════════════════════════════════════════
    // SETTINGS
    // ══════════════════════════════════════════════════════════════════════════

    public function get_settings( WP_REST_Request $request ): WP_REST_Response {
        $rows = $this->db->getArray(
            "SELECT `key`, value FROM {$this->db->table('settings')} WHERE section = 'seo'"
        );
        $out = [
            'product_url_pattern'  => '/product/{slug}/',
            'category_url_pattern' => '/shop/{slug}/',
            'canonical_urls'       => 'yes',
            'trailing_slash'       => 'yes',
            'noindex_filter_pages' => 'yes',
            'default_meta_title'   => '{product_name} | {site_name}',
            'default_meta_desc'    => '{short_description}',
        ];
        foreach ( $rows as $r ) {
            $out[ $r['key'] ] = $r['value'];
        }
        return new WP_REST_Response( $out );
    }

    public function save_settings( WP_REST_Request $request ): WP_REST_Response|WP_Error {
        $allowed = [ 'product_url_pattern', 'category_url_pattern', 'canonical_urls',
                     'trailing_slash', 'noindex_filter_pages', 'default_meta_title', 'default_meta_desc' ];

        $url_pattern_changed = false;

        // Validate URL patterns against whitelist BEFORE saving.
        $prod_pattern = $request->get_param( 'product_url_pattern' );
        $cat_pattern  = $request->get_param( 'category_url_pattern' );

        if ( $prod_pattern !== null && ! in_array( $prod_pattern, SeoService::ALLOWED_PRODUCT_PATTERNS, true ) ) {
            return new WP_Error( 'tc_invalid_pattern', 'Invalid product URL pattern. Please select from the available options.', [ 'status' => 422 ] );
        }
        if ( $cat_pattern !== null && ! in_array( $cat_pattern, SeoService::ALLOWED_CATEGORY_PATTERNS, true ) ) {
            return new WP_Error( 'tc_invalid_pattern', 'Invalid category URL pattern. Please select from the available options.', [ 'status' => 422 ] );
        }

        // Check prefix collision.
        if ( $prod_pattern && $cat_pattern ) {
            $prod_pfx = trim( explode( '/', trim( $prod_pattern, '/' ) )[0] );
            $cat_pfx  = trim( explode( '/', trim( $cat_pattern, '/' ) )[0] );
            if ( $prod_pfx === $cat_pfx ) {
                return new WP_Error( 'tc_pattern_collision', 'Product and category URL prefixes cannot be the same ("/' . $prod_pfx . '/"). Please choose different patterns.', [ 'status' => 422 ] );
            }
        }

        foreach ( $allowed as $key ) {
            if ( $request->get_param( $key ) === null ) continue;
            $val = sanitize_text_field( $request->get_param( $key ) );

            // Track if URL patterns changed.
            if ( in_array( $key, [ 'product_url_pattern', 'category_url_pattern' ], true ) ) {
                $old = $this->db->getField(
                    "SELECT value FROM {$this->db->table('settings')} WHERE section = 'seo' AND `key` = ?s LIMIT 1",
                    $key
                );
                if ( $old !== $val ) {
                    $url_pattern_changed = true;
                }
            }

            $exists = $this->db->getField(
                "SELECT id FROM {$this->db->table('settings')} WHERE section = 'seo' AND `key` = ?s LIMIT 1",
                $key
            );
            if ( $exists ) {
                $this->db->update( 'settings', [ 'value' => $val ], [ 'section' => 'seo', 'key' => $key ] );
            } else {
                $this->db->insert( 'settings', [ 'section' => 'seo', 'key' => $key, 'value' => $val ] );
            }
        }

        // Flush rewrite rules if URL patterns changed.
        if ( $url_pattern_changed ) {
            SeoService::reset_cache();
            SeoService::flush_rewrite_rules();
        }

        return $this->get_settings( $request );
    }

    // ══════════════════════════════════════════════════════════════════════════
    // REDIRECTS
    // ══════════════════════════════════════════════════════════════════════════

    public function list_redirects( WP_REST_Request $request ): WP_REST_Response {
        $page     = max( 1, (int) ( $request->get_param( 'page' ) ?? 1 ) );
        $per_page = min( 100, max( 1, (int) ( $request->get_param( 'per_page' ) ?? 25 ) ) );
        $offset   = ( $page - 1 ) * $per_page;
        $search   = $request->get_param( 'search' );

        $where  = [];
        $params = [];
        if ( $search ) {
            $like     = '%' . $this->db->escape_like( $search ) . '%';
            $where[]  = '(from_url LIKE ?s OR to_url LIKE ?s)';
            $params[] = $like;
            $params[] = $like;
        }
        $where_sql = $where ? 'WHERE ' . implode( ' AND ', $where ) : '';

        $total = (int) $this->db->getField(
            "SELECT COUNT(*) FROM {$this->db->table('seo_redirects')} {$where_sql}",
            ...$params
        );

        $lp   = $params;
        $lp[] = $per_page;
        $lp[] = $offset;

        $rows = $this->db->getArray(
            "SELECT * FROM {$this->db->table('seo_redirects')} {$where_sql}
             ORDER BY created_at DESC LIMIT ?i OFFSET ?i",
            ...$lp
        );

        return new WP_REST_Response( [
            'redirects' => $rows ?: [],
            'total'     => $total,
            'pages'     => $total > 0 ? (int) ceil( $total / $per_page ) : 1,
            'page'      => $page,
        ] );
    }

    public function create_redirect( WP_REST_Request $request ): WP_REST_Response|WP_Error {
        $from = trim( $request->get_param( 'from_url' ) ?? '' );
        $to   = trim( $request->get_param( 'to_url' ) ?? '' );
        if ( ! $from || ! $to ) {
            return new WP_Error( 'tc_invalid', 'from_url and to_url are required.', [ 'status' => 422 ] );
        }

        $id = $this->db->insert( 'seo_redirects', [
            'from_url' => sanitize_text_field( $from ),
            'to_url'   => sanitize_text_field( $to ),
            'type'     => in_array( (int) $request->get_param( 'type' ), [ 301, 302 ], true ) ? (int) $request->get_param( 'type' ) : 301,
            'status'   => 'active',
        ] );

        // KI-89: report a failed INSERT instead of returning 201 with a null body.
        // Database::insert() (Database.php L249-254) returns the insert id or 0 on
        // failure, so this route already had the verdict in hand and discarded it:
        // getRow(... WHERE id = 0) is null, and WP core's REST server flattens a null
        // result to a ZERO-BYTE body at the SAME status (L536-538) ⇒ a 201 for a
        // redirect that was never written.
        if ( ! $id ) {
            return new WP_Error( 'tc_create_failed', 'Failed to create redirect.', [ 'status' => 500 ] );
        }

        return new WP_REST_Response( $this->db->getRow(
            "SELECT * FROM {$this->db->table('seo_redirects')} WHERE id = ?i",
            $id
        ), 201 );
    }

    public function update_redirect( WP_REST_Request $request ): WP_REST_Response|WP_Error {
        $id = (int) $request['id'];
        $exists = $this->db->getField(
            "SELECT id FROM {$this->db->table('seo_redirects')} WHERE id = ?i",
            $id
        );
        if ( ! $exists ) {
            return new WP_Error( 'tc_not_found', 'Redirect not found.', [ 'status' => 404 ] );
        }

        $fields = [];
        if ( $request->get_param( 'from_url' ) !== null ) $fields['from_url'] = sanitize_text_field( $request->get_param( 'from_url' ) );
        if ( $request->get_param( 'to_url' ) !== null )   $fields['to_url']   = sanitize_text_field( $request->get_param( 'to_url' ) );
        if ( $request->get_param( 'type' ) !== null )     $fields['type']     = in_array( (int) $request->get_param( 'type' ), [301,302], true ) ? (int) $request->get_param( 'type' ) : 301;
        if ( $request->get_param( 'status' ) !== null )   $fields['status']   = in_array( $request->get_param( 'status' ), ['active','disabled'], true ) ? $request->get_param( 'status' ) : 'active';

        if ( ! empty( $fields ) ) {
            $this->db->update( 'seo_redirects', $fields, [ 'id' => $id ] );
        }

        return new WP_REST_Response( $this->db->getRow(
            "SELECT * FROM {$this->db->table('seo_redirects')} WHERE id = ?i", $id
        ) );
    }

    public function delete_redirect( WP_REST_Request $request ): WP_REST_Response {
        $this->db->delete( 'seo_redirects', [ 'id' => (int) $request['id'] ] );
        return new WP_REST_Response( [ 'deleted' => true ] );
    }

    // ══════════════════════════════════════════════════════════════════════════
    // ROBOTS.TXT
    // ══════════════════════════════════════════════════════════════════════════

    public function get_robots( WP_REST_Request $request ): WP_REST_Response {
        $val = $this->db->getField(
            "SELECT value FROM {$this->db->table('settings')} WHERE section = 'seo' AND `key` = 'robots_txt' LIMIT 1"
        );
        if ( $val === null ) {
            $val = "User-agent: *\nAllow: /\nDisallow: /wp-admin/\nDisallow: /checkout/\nDisallow: /my-account/\n\nSitemap: " . home_url( '/sitemap.xml' );
        }
        return new WP_REST_Response( [ 'content' => $val ] );
    }

    public function save_robots( WP_REST_Request $request ): WP_REST_Response {
        $content = $request->get_param( 'content' ) ?? '';
        $exists  = $this->db->getField(
            "SELECT id FROM {$this->db->table('settings')} WHERE section = 'seo' AND `key` = 'robots_txt' LIMIT 1"
        );
        if ( $exists ) {
            $this->db->update( 'settings', [ 'value' => $content ], [ 'section' => 'seo', 'key' => 'robots_txt' ] );
        } else {
            $this->db->insert( 'settings', [ 'section' => 'seo', 'key' => 'robots_txt', 'value' => $content ] );
        }
        return new WP_REST_Response( [ 'content' => $content, 'saved' => true ] );
    }

    public function admin_permission(): bool {
        return current_user_can( 'manage_options' );
    }
}
