<?php
/**
 * Shipping Module — bootstrapper.
 *
 * Responsibilities:
 *   1. Hook into `titancart_checkout_shipping_cost` filter so CheckoutManager
 *      receives the real shipping cost calculated from zones + rates.
 *   2. Register REST routes (ShippingController).
 *   3. Seed a default shipping method + zone on first activation.
 *
 * @package TitanCart\Modules\Shipping
 */

namespace TitanCart\Modules\Shipping;

use TitanCart\REST\Shipping\ShippingController;
use TitanCart\REST\Shipping\ShipmentController;

defined( 'ABSPATH' ) || exit;

class ShippingModule {

    /**
     * Initialise the Shipping module.
     * Called from Plugin::boot() before `titancart_loaded` fires.
     */
    public static function init(): void {
        $instance = new self();

        // 1. Inject the real shipping cost into checkout totals.
        //    Signature: apply_filters( 'titancart_checkout_shipping_cost',
        //                             0.0, $shipping_method_id, $cart, $shipping_address, $service_code )
        //    $service_code (5th arg, S379/KI-32) selects a specific realtime carrier
        //    service; empty string = core (zone) method, unchanged behaviour.
        add_filter( 'titancart_checkout_shipping_cost', [ $instance, 'calculate_shipping_cost' ], 10, 5 );

        // Expose the available methods for a cart+address so CheckoutManager can VALIDATE
        // that a real, available method was selected — a physical cart must never fall
        // back to a silent $0 when none is configured. Extensions (e.g. live Printful
        // rates) participate automatically because get_available_rates runs the
        // titancart_shipping_rates filter they hook.
        add_filter( 'titancart_checkout_shipping_methods', [ $instance, 'available_methods' ], 10, 3 );

        // Note: shipping methods are loaded via AJAX (POST /checkout/shipping-methods)
        // after the customer enters their address, not pre-populated at page load.

        // 2. REST routes (admin CRUD + public checkout endpoint).
        add_action( 'rest_api_init', [ $instance, 'register_routes' ] );

        // 3. Seed default method once on first load (transient-guarded).
        add_action( 'titancart_loaded', [ $instance, 'maybe_seed_defaults' ] );
    }

    // ══════════════════════════════════════════════════════════════════════════
    // CHECKOUT SHIPPING COST FILTER
    // ══════════════════════════════════════════════════════════════════════════

    /**
     * Filter: `titancart_checkout_shipping_cost`
     *
     * Replaces the 0.0 default with the real shipping cost for the selected
     * method + cart + shipping address.
     *
     * @param  float  $default          Always 0.0 (the filter's initial value from CheckoutManager).
     * @param  int    $shipping_method_id
     * @param  array  $cart             Full cart array from CartManager.
     * @param  array  $shipping_address Shipping address fields from checkout form.
     * @param  string $service_code     Realtime carrier service to charge (S379/KI-32);
     *                                  empty string selects the core (zone) rate, unchanged.
     * @return float
     */
    public function calculate_shipping_cost(
        float  $default,
        int    $shipping_method_id,
        array  $cart,
        array  $shipping_address,
        string $service_code = ''
    ): float {
        if ( $shipping_method_id <= 0 ) {
            return 0.0;
        }

        $manager = new ShippingManager();
        return $manager->get_cost_for_method( $shipping_method_id, $cart, $shipping_address, $service_code );
    }

    /**
     * Filter: `titancart_checkout_shipping_methods`
     *
     * Return the shipping methods available for a cart + address (each with its
     * synthetic/real method_id), so CheckoutManager can confirm the customer selected a
     * real, currently-available method rather than silently charging $0.
     *
     * @param  array $methods  Running list (usually []).
     * @param  array $cart
     * @param  array $address  Empty at render time (get_checkout_data fires this filter
     *                         without an address — methods load via AJAX once the shopper
     *                         enters one); populated at place_order for validation.
     * @return array[]
     */
    public function available_methods( array $methods, array $cart = [], array $address = [] ): array {
        // Render-time call (no address yet) — leave the value untouched so the checkout
        // page behaves exactly as before (the picker is populated via the AJAX
        // /checkout/shipping-methods endpoint after the address is entered).
        if ( empty( $address ) ) {
            return $methods;
        }
        return ( new ShippingManager() )->get_available_rates( $cart, $address );
    }

    // ══════════════════════════════════════════════════════════════════════════
    // REST ROUTES
    // ══════════════════════════════════════════════════════════════════════════

    /**
     * Register ShippingController routes.
     */
    public function register_routes(): void {
        ( new ShippingController() )->register_routes();
        ( new ShipmentController() )->register_routes();
    }

    // ══════════════════════════════════════════════════════════════════════════
    // DEFAULT SEEDING
    // ══════════════════════════════════════════════════════════════════════════

    /**
     * Delegate to ShippingManager's seed method.
     */
    public function maybe_seed_defaults(): void {
        ( new ShippingManager() )->maybe_seed_defaults();
    }
}
