<?php
/**
 * Orders REST Controller.
 *
 * Provides the admin-facing REST API for order management.
 *
 * Routes:
 *   GET    /titancart/v1/orders                — list orders (paginated/filterable)
 *   GET    /titancart/v1/orders/statuses       — list all order statuses
 *   GET    /titancart/v1/orders/{id}           — get single order (admin or owner)
 *   PATCH  /titancart/v1/orders/{id}/status    — update order status
 *   GET    /titancart/v1/orders/{id}/notes     — get order notes
 *   POST   /titancart/v1/orders/{id}/notes     — add a note to an order
 *   GET    /titancart/v1/my-orders             — current customer's order list
 *   GET    /titancart/v1/my-orders/{id}        — single order (ownership enforced)
 *
 * Permissions:
 *   Admin routes  → manage_options capability.
 *   Customer routes → logged-in WP user, ownership-checked in handler.
 *
 * @package TitanCart\REST\Orders
 */

namespace TitanCart\REST\Orders;

use TitanCart\Modules\Accounts\CustomerRepository;
use TitanCart\Modules\Orders\OrderManager;
use TitanCart\Core\LogService;
use WP_REST_Controller;
use WP_REST_Server;
use WP_REST_Request;
use WP_REST_Response;
use WP_Error;

defined( 'ABSPATH' ) || exit;

class OrdersController extends WP_REST_Controller {

    protected $namespace = 'titancart/v1';
    protected $rest_base = 'orders';

    private OrderManager $manager;

    public function __construct( OrderManager $manager ) {
        $this->manager = $manager;
    }

    // ══════════════════════════════════════════════════════════════════════════
    // ROUTE REGISTRATION
    // ══════════════════════════════════════════════════════════════════════════

    public function register_routes(): void {

        // ── Admin: List + statuses ────────────────────────────────────────────
        register_rest_route( $this->namespace, '/' . $this->rest_base, [
            [
                'methods'             => WP_REST_Server::READABLE,
                'callback'            => [ $this, 'get_items' ],
                'permission_callback' => [ $this, 'admin_permission' ],
                'args'                => $this->list_args(),
            ],
        ] );

        register_rest_route( $this->namespace, '/' . $this->rest_base . '/statuses', [
            [
                'methods'             => WP_REST_Server::READABLE,
                'callback'            => [ $this, 'get_statuses' ],
                'permission_callback' => [ $this, 'admin_permission' ],
            ],
        ] );

        // ── Admin: Single order ───────────────────────────────────────────────
        register_rest_route( $this->namespace, '/' . $this->rest_base . '/(?P<id>\d+)', [
            [
                'methods'             => WP_REST_Server::READABLE,
                'callback'            => [ $this, 'get_item' ],
                'permission_callback' => [ $this, 'admin_permission' ],
                'args'                => [ 'id' => [ 'validate_callback' => function( $v ) { return is_numeric( $v ); } ] ],
            ],
        ] );

        // ── Admin: Update status ──────────────────────────────────────────────
        register_rest_route( $this->namespace, '/' . $this->rest_base . '/(?P<id>\d+)/status', [
            [
                'methods'             => 'PATCH',
                'callback'            => [ $this, 'update_status' ],
                'permission_callback' => [ $this, 'admin_permission' ],
                'args'                => [
                    'id'      => [ 'validate_callback' => function( $v ) { return is_numeric( $v ); } ],
                    'status'  => [
                        'required'          => true,
                        'type'              => 'string',
                        'sanitize_callback' => 'sanitize_key',
                    ],
                    'comment' => [
                        'type'              => 'string',
                        'default'           => '',
                        'sanitize_callback' => 'sanitize_textarea_field',
                    ],
                    'notify'  => [
                        'type'    => 'boolean',
                        'default' => false,
                    ],
                ],
            ],
        ] );

        // ── Admin: Notes ──────────────────────────────────────────────────────
        register_rest_route( $this->namespace, '/' . $this->rest_base . '/(?P<id>\d+)/notes', [
            [
                'methods'             => WP_REST_Server::READABLE,
                'callback'            => [ $this, 'get_notes' ],
                'permission_callback' => [ $this, 'admin_permission' ],
            ],
            [
                'methods'             => WP_REST_Server::CREATABLE,
                'callback'            => [ $this, 'add_note' ],
                'permission_callback' => [ $this, 'admin_permission' ],
                'args'                => [
                    'id'   => [ 'validate_callback' => function( $v ) { return is_numeric( $v ); } ],
                    'note' => [
                        'required'          => true,
                        'type'              => 'string',
                        'sanitize_callback' => 'wp_kses_post',
                    ],
                    'type' => [
                        'type'    => 'string',
                        'enum'    => [ 'internal', 'customer' ],
                        'default' => 'internal',
                    ],
                ],
            ],
        ] );

        // ── Customer: My orders ───────────────────────────────────────────────
        register_rest_route( $this->namespace, '/my-orders', [
            [
                'methods'             => WP_REST_Server::READABLE,
                'callback'            => [ $this, 'get_my_orders' ],
                'permission_callback' => [ $this, 'customer_permission' ],
                'args'                => [
                    'page'     => [ 'type' => 'integer', 'default' => 1, 'minimum' => 1 ],
                    'per_page' => [ 'type' => 'integer', 'default' => 10, 'minimum' => 1, 'maximum' => 50 ],
                ],
            ],
        ] );

        register_rest_route( $this->namespace, '/my-orders/(?P<id>\d+)', [
            [
                'methods'             => WP_REST_Server::READABLE,
                'callback'            => [ $this, 'get_my_order' ],
                'permission_callback' => [ $this, 'customer_permission' ],
            ],
        ] );
    }

    // ══════════════════════════════════════════════════════════════════════════
    // HANDLERS — Admin
    // ══════════════════════════════════════════════════════════════════════════

    /**
     * GET /titancart/v1/orders
     */
    public function get_items( $request ): WP_REST_Response {
        $result = $this->manager->list_orders( [
            'page'      => (int) $request->get_param( 'page' ),
            'per_page'  => (int) $request->get_param( 'per_page' ),
            'status'    => $request->get_param( 'status' )    ?? '',
            'search'    => $request->get_param( 'search' )    ?? '',
            'date_from' => $request->get_param( 'date_from' ) ?? '',
            'date_to'   => $request->get_param( 'date_to' )   ?? '',
            'orderby'   => $request->get_param( 'orderby' )   ?? 'created_at',
            'order'     => $request->get_param( 'order' )     ?? 'DESC',
            // S187 — ids pass-through. When null (default), OrderManager skips
            // the filter entirely. When array, OrderManager restricts result
            // set to those ids (empty array short-circuits to zero rows).
            'ids'       => $request->get_param( 'ids' ),
        ] );

        $response = rest_ensure_response( $result );
        $response->header( 'X-TC-Total',      $result['total'] );
        $response->header( 'X-TC-TotalPages', $result['pages'] );
        return $response;
    }

    /**
     * GET /titancart/v1/orders/statuses
     */
    public function get_statuses( WP_REST_Request $request ): WP_REST_Response {
        $type     = $request->get_param( 'type' ) ?: 'order';
        $statuses = $this->manager->get_all_statuses( 'en', $type );
        return rest_ensure_response( $statuses );
    }

    /**
     * GET /titancart/v1/orders/{id}
     */
    public function get_item( $request ): WP_REST_Response|WP_Error {
        $order = $this->manager->get_order( (int) $request->get_param( 'id' ) );

        if ( ! $order ) {
            return new WP_Error(
                'tc_order_not_found',
                __( 'Order not found.', 'titancart-lite' ),
                [ 'status' => 404 ]
            );
        }

        return rest_ensure_response( $order );
    }

    /**
     * PATCH /titancart/v1/orders/{id}/status
     */
    public function update_status( WP_REST_Request $request ): WP_REST_Response|WP_Error {
        $order_id = (int) $request->get_param( 'id' );
        $handle   = (string) $request->get_param( 'status' );
        $comment  = (string) $request->get_param( 'comment' );
        $notify   = (bool) $request->get_param( 'notify' );

        $ok = $this->manager->update_status( $order_id, $handle, $comment, $notify );

        if ( ! $ok ) {
            LogService::error( 'orders', 'change', 'Order #' . $order_id . ' status change failed → ' . $handle, [
                'object_type'    => 'order',
                'object_id'      => $order_id,
                'target_status'  => $handle,
                'backtrace'      => true,
            ] );
            return new WP_Error(
                'tc_status_update_failed',
                __( 'Status update failed. Check the order ID and status handle are valid.', 'titancart-lite' ),
                [ 'status' => 422 ]
            );
        }

        $order = $this->manager->get_order( $order_id );

        LogService::info( 'orders', 'change', 'Order #' . $order_id . ' status changed to: ' . $handle, [
            'object_type' => 'order',
            'object_id'   => $order_id,
            'new_status'  => $handle,
            'notify'      => $notify,
            'comment'     => $comment,
        ] );

        return rest_ensure_response( [
            'success' => true,
            'order'   => $order,
        ] );
    }

    /**
     * GET /titancart/v1/orders/{id}/notes
     */
    public function get_notes( WP_REST_Request $request ): WP_REST_Response|WP_Error {
        $order_id = (int) $request->get_param( 'id' );

        if ( ! $this->manager->get_order( $order_id ) ) {
            return new WP_Error( 'tc_order_not_found', __( 'Order not found.', 'titancart-lite' ), [ 'status' => 404 ] );
        }

        return rest_ensure_response( $this->manager->get_notes( $order_id ) );
    }

    /**
     * POST /titancart/v1/orders/{id}/notes
     */
    public function add_note( WP_REST_Request $request ): WP_REST_Response|WP_Error {
        $order_id = (int) $request->get_param( 'id' );

        if ( ! $this->manager->get_order( $order_id ) ) {
            return new WP_Error( 'tc_order_not_found', __( 'Order not found.', 'titancart-lite' ), [ 'status' => 404 ] );
        }

        $note_id = $this->manager->add_note(
            $order_id,
            (string) $request->get_param( 'note' ),
            (string) $request->get_param( 'type' ),
            get_current_user_id() ?: null
        );

        if ( ! $note_id ) {
            return new WP_Error( 'tc_note_failed', __( 'Note could not be saved.', 'titancart-lite' ), [ 'status' => 500 ] );
        }

        LogService::info( 'orders', 'update', 'Note added to order #' . $order_id, [
            'object_type' => 'order',
            'object_id'   => $order_id,
            'note_id'     => $note_id,
        ] );

        return rest_ensure_response( [ 'success' => true, 'note_id' => $note_id ] );
    }

    // ══════════════════════════════════════════════════════════════════════════
    // HANDLERS — Customer (My Orders)
    // ══════════════════════════════════════════════════════════════════════════

    /**
     * GET /titancart/v1/my-orders
     */
    public function get_my_orders( WP_REST_Request $request ): WP_REST_Response {
        // Resolve WP user → tc_customers.id (orders.customer_id stores the TC id).
        $tc_customer_id = $this->resolve_current_customer_id();
        if ( $tc_customer_id === 0 ) {
            $empty = [ 'orders' => [], 'total' => 0, 'pages' => 1 ];
            $response = rest_ensure_response( $empty );
            $response->header( 'X-TC-Total', 0 );
            $response->header( 'X-TC-TotalPages', 1 );
            return $response;
        }

        $result = $this->manager->get_customer_orders(
            $tc_customer_id,
            (int) $request->get_param( 'page' ),
            (int) $request->get_param( 'per_page' )
        );

        $response = rest_ensure_response( $result );
        $response->header( 'X-TC-Total',      $result['total'] );
        $response->header( 'X-TC-TotalPages', $result['pages'] );
        return $response;
    }

    /**
     * GET /titancart/v1/my-orders/{id}
     */
    public function get_my_order( WP_REST_Request $request ): WP_REST_Response|WP_Error {
        // Resolve WP user → tc_customers.id (ownership compares against TC id).
        $tc_customer_id = $this->resolve_current_customer_id();
        if ( $tc_customer_id === 0 ) {
            return new WP_Error( 'tc_order_not_found', __( 'Order not found.', 'titancart-lite' ), [ 'status' => 404 ] );
        }

        $order = $this->manager->get_customer_order(
            (int) $request->get_param( 'id' ),
            $tc_customer_id
        );

        if ( ! $order ) {
            return new WP_Error( 'tc_order_not_found', __( 'Order not found.', 'titancart-lite' ), [ 'status' => 404 ] );
        }

        return rest_ensure_response( $order );
    }

    /**
     * Resolve the currently logged-in WP user to their tc_customers.id.
     * Returns 0 if not logged in or if no tc_customers row exists for them
     * (which is the safe response — they have no orders to show).
     */
    private function resolve_current_customer_id(): int {
        $wp_user_id = get_current_user_id();
        if ( $wp_user_id === 0 ) {
            return 0;
        }
        $repo = new CustomerRepository();
        $row  = $repo->find_by_wp_user( $wp_user_id );
        return $row ? (int) $row['id'] : 0;
    }

    // ══════════════════════════════════════════════════════════════════════════
    // PERMISSIONS
    // ══════════════════════════════════════════════════════════════════════════

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

    public function customer_permission(): bool {
        return is_user_logged_in();
    }

    // ══════════════════════════════════════════════════════════════════════════
    // ARGS SCHEMA
    // ══════════════════════════════════════════════════════════════════════════

    private function list_args(): array {
        return [
            'page'      => [ 'type' => 'integer', 'default' => 1,          'minimum' => 1 ],
            'per_page'  => [ 'type' => 'integer', 'default' => 20,         'minimum' => 1, 'maximum' => 100 ],
            'status'    => [ 'type' => 'string',  'default' => '',         'sanitize_callback' => 'sanitize_key' ],
            'search'    => [ 'type' => 'string',  'default' => '',         'sanitize_callback' => 'sanitize_text_field' ],
            'date_from' => [ 'type' => 'string',  'default' => '',         'sanitize_callback' => 'sanitize_text_field' ],
            'date_to'   => [ 'type' => 'string',  'default' => '',         'sanitize_callback' => 'sanitize_text_field' ],
            'orderby'   => [ 'type' => 'string',  'default' => 'created_at', 'enum' => [ 'id', 'created_at', 'total', 'status_id', 'billing_email' ] ],
            'order'     => [ 'type' => 'string',  'default' => 'DESC',     'enum' => [ 'ASC', 'DESC' ] ],
            // S187 — pass-through filter restricting the result set to specific
            // order IDs. Optional. Empty array short-circuits to zero results
            // (see OrderManager::list_orders). Used by marketplace vendor sales
            // delegation path to scope the read after server-side ownership
            // resolution. Mirrors ProductRepository::get_list's ids filter.
            'ids'       => [
                'type'              => 'array',
                'items'             => [ 'type' => 'integer' ],
                'default'           => null,
                'sanitize_callback' => function( $value ) {
                    if ( ! is_array( $value ) ) {
                        return null;
                    }
                    $clean = [];
                    foreach ( $value as $v ) {
                        $v = (int) $v;
                        if ( $v > 0 ) {
                            $clean[] = $v;
                        }
                    }
                    return $clean;
                },
            ],
        ];
    }
}
