<?php
namespace WooNooW\Api;

use WP_Error;
use WP_REST_Request;
use WC_Order;
use WC_Product;
use WC_Shipping_Zones;
use WC_Shipping_Rate;

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

class CheckoutController {

    /**
     * Register REST routes for checkout quote & submit
     */
    public static function register() {
        $namespace = 'woonoow/v1';
        register_rest_route($namespace, '/checkout/quote', [
            'methods'  => 'POST',
            'callback' => [ new self(), 'quote' ],
            'permission_callback' => [ \WooNooW\Api\Permissions::class, 'anon_or_wp_nonce' ], // consider nonce check later
        ]);
        register_rest_route($namespace, '/checkout/submit', [
            'methods'  => 'POST',
            'callback' => [ new self(), 'submit' ],
            'permission_callback' => [ \WooNooW\Api\Permissions::class, 'anon_or_wp_nonce' ], // consider capability/nonce
        ]);
        register_rest_route($namespace, '/checkout/fields', [
            'methods'  => 'POST',
            'callback' => [ new self(), 'get_fields' ],
            'permission_callback' => [ \WooNooW\Api\Permissions::class, 'anon_or_wp_nonce' ],
        ]);
    }

    /**
     * Build a quote for the given payload:
     * {
     *   items: [{ product_id, variation_id?, qty, meta?[] }],
     *   billing: {...},
     *   shipping: {..., ship_to_different?},
     *   coupons: ["CODE"],
     *   shipping_method: "flat_rate:1" | "free_shipping:3" | ...
     * }
     */
    public function quote(WP_REST_Request $r): array {
        $__t0 = microtime(true);
        $payload = $this->sanitize_payload($r);

        // Allow a fully accurate quote using Woo's Cart mechanics (slower, but parity)
        $useAccurate = (bool) apply_filters('woonoow/quote/accurate', false);
        if ($useAccurate) {
            $resp = $this->accurate_quote_via_wc_cart($payload);
            if (!headers_sent()) {
                header('Server-Timing: app;dur=' . round((microtime(true) - $__t0) * 1000, 1));
            }
            return $resp;
        }

        // Build a transient cart-like calculation using Woo helpers.
        // We will sum line totals and attempt shipping/tax estimates.
        $lines = [];
        $subtotal = 0.0;
        $discount = 0.0; // coupons not applied to calculations in this v0 (placeholder)
        $shipping_total = 0.0;
        $tax_total = 0.0;

        foreach ($payload['items'] as $line) {
            $product = $this->load_product($line);
            if ($product instanceof WP_Error) {
                return ['error' => $product->get_error_message()];
            }
            $qty = max(1, (int)($line['qty'] ?? 1));
            $price = (float) wc_get_price_to_display($product);
            $line_total = $price * $qty;

            $lines[] = [
                'product_id' => $product->get_id(),
                'name'       => $product->get_name(),
                'qty'        => $qty,
                'price'      => $price,
                'line_total' => $line_total,
            ];
            $subtotal += $line_total;
        }

        // --- simple coupon handling (fast path) ---
        if (!empty($payload['coupons'])) {
            foreach ($payload['coupons'] as $code) {
                $coupon = new \WC_Coupon($code);
                if ($coupon->get_id()) {
                    $type = $coupon->get_discount_type(); // 'percent', 'fixed_cart', etc.
                    $amount = (float) $coupon->get_amount();
                    if ($type === 'percent') {
                        $discount += ($amount / 100.0) * $subtotal;
                    } elseif ($type === 'fixed_cart') {
                        $discount += min($amount, $subtotal);
                    }
                    // NOTE: fixed_product & restrictions are ignored in FastQuote
                }
            }
        }
        $discount = min($discount, $subtotal);

        // Simple shipping estimate using zones (optional, best-effort).
        if (!empty($payload['shipping']['postcode']) && !empty($payload['shipping']['country'])) {
            $shipping_total = $this->estimate_shipping($payload['shipping'], $payload['shipping_method'] ?? null);
        }

        $grand = max(0, $subtotal - $discount + $shipping_total + $tax_total);

        if (!headers_sent()) {
            header('Server-Timing: app;dur=' . round((microtime(true) - $__t0) * 1000, 1));
        }

        return [
            'ok'      => true,
            'items'   => $lines,
            'totals'  => [
                'subtotal'        => wc_format_decimal($subtotal, wc_get_price_decimals()),
                'discount_total'  => wc_format_decimal($discount, wc_get_price_decimals()),
                'shipping_total'  => wc_format_decimal($shipping_total, wc_get_price_decimals()),
                'tax_total'       => wc_format_decimal($tax_total, wc_get_price_decimals()),
                'grand_total'     => wc_format_decimal($grand, wc_get_price_decimals()),
                'currency'        => get_woocommerce_currency(),
                'currency_symbol' => get_woocommerce_currency_symbol(),
                'currency_pos'    => get_option('woocommerce_currency_pos', 'left'),
                'decimals'        => wc_get_price_decimals(),
                'decimal_sep'     => wc_get_price_decimal_separator(),
                'thousand_sep'    => wc_get_price_thousand_separator(),
            ],
        ];
    }

    /**
     * Submit an order:
     * {
     *   items: [{ product_id, variation_id?, qty, meta?[] }],
     *   billing: {...},
     *   shipping: {..., ship_to_different?},
     *   coupons: ["CODE"],
     *   shipping_method: "flat_rate:1",
     *   payment_method: "cod" | "bacs" | ...
     * }
     */
    public function submit(WP_REST_Request $r): array {
        $__t0 = microtime(true);
        $payload = $this->sanitize_payload($r);

        if (empty($payload['items'])) {
            return ['error' => __('No items provided', 'woonoow')];
        }

        // Create order
        $order = wc_create_order();
        if (is_wp_error($order)) {
            return ['error' => $order->get_error_message()];
        }

        // Add items
        foreach ($payload['items'] as $line) {
            $product = $this->load_product($line);
            if (is_wp_error($product)) {
                return ['error' => $product->get_error_message()];
            }
            $qty = max(1, (int)($line['qty'] ?? 1));

            $args = [];
            if (!empty($line['meta']) && is_array($line['meta'])) {
                $args['item_meta_array'] = $line['meta'];
            }

            $order->add_product($product, $qty, $args);
        }

        // Addresses
        if (!empty($payload['billing'])) {
            $order->set_address($this->only_address_fields($payload['billing']), 'billing');
        }
        if (!empty($payload['shipping'])) {
            $ship = $payload['shipping'];
            // If ship_to_different is false, copy billing
            if (empty($ship['ship_to_different'])) {
                $ship = $this->only_address_fields($payload['billing'] ?: []);
            }
            $order->set_address($this->only_address_fields($ship), 'shipping');
        }

        // Coupons (best‑effort using Woo Coupon objects)
        if (!empty($payload['coupons']) && is_array($payload['coupons'])) {
            foreach ($payload['coupons'] as $code) {
                try {
                    $coupon = new \WC_Coupon(wc_clean(wp_unslash($code)));
                    if ($coupon->get_id()) {
                        $order->apply_coupon($coupon);
                    }
                } catch (\Throwable $e) {
                    // ignore invalid in v0
                }
            }
        }

        // Shipping (best‑effort estimate)
        if (!empty($payload['shipping_method'])) {
            $rate = $this->find_shipping_rate_for_order($order, $payload['shipping_method']);
            if ($rate instanceof WC_Shipping_Rate) {
                $item = new \WC_Order_Item_Shipping();
                $item->set_props([
                    'method_title' => $rate->get_label(),
                    'method_id'    => $rate->get_method_id(),
                    'instance_id'  => $rate->get_instance_id(),
                    'total'        => $rate->get_cost(),
                    'taxes'        => $rate->get_taxes(),
                ]);
                $order->add_item($item);
            }
        }

        // Payment method
        if (!empty($payload['payment_method'])) {
            $order->set_payment_method($payload['payment_method']);
        }

        // Totals
        $order->calculate_totals();

        // Mirror Woo hooks so extensions still work
        do_action('woocommerce_checkout_create_order', $order->get_id(), $order);
        do_action('woocommerce_thankyou', $order->get_id());

        $order->save();

        if (!headers_sent()) {
            header('Server-Timing: app;dur=' . round((microtime(true) - $__t0) * 1000, 1));
        }

        return [
            'ok' => true,
            'order_id' => $order->get_id(),
            'order_key' => $order->get_order_key(),
            'status' => $order->get_status(),
            'pay_url' => $order->get_checkout_payment_url(),
            'thankyou_url' => $order->get_checkout_order_received_url(),
        ];
    }

    /**
     * Get checkout fields with all filters applied
     * Accepts: { items: [...], is_digital_only?: bool }
     * Returns fields with required, hidden, etc. based on addons + cart context
     */
    public function get_fields(WP_REST_Request $r): array {
        $json = $r->get_json_params();
        $items = isset($json['items']) && is_array($json['items']) ? $json['items'] : [];
        $is_digital_only = isset($json['is_digital_only']) ? (bool) $json['is_digital_only'] : false;

        // Initialize WooCommerce checkout if not already
        if (!WC()->checkout()) {
            WC()->initialize_session();
            WC()->initialize_cart();
        }

        // Get checkout fields with all filters applied
        $fields = WC()->checkout()->get_checkout_fields();

        $formatted = [];

        foreach ($fields as $fieldset_key => $fieldset) {
            foreach ($fieldset as $key => $field) {
                // Check if field should be hidden
                $hidden = false;
                
                // Hide shipping fields if digital only (your existing logic)
                if ($is_digital_only && $fieldset_key === 'shipping') {
                    $hidden = true;
                }

                // Check if addon/filter explicitly hides this field
                if (isset($field['class']) && is_array($field['class'])) {
                    if (in_array('hidden', $field['class']) || in_array('hide', $field['class'])) {
                        $hidden = true;
                    }
                }

                // Respect 'enabled' flag if set by addons
                if (isset($field['enabled']) && !$field['enabled']) {
                    $hidden = true;
                }

                $formatted[] = [
                    'key'         => $key,
                    'fieldset'    => $fieldset_key, // billing, shipping, account, order
                    'type'        => $field['type'] ?? 'text',
                    'label'       => $field['label'] ?? '',
                    'placeholder' => $field['placeholder'] ?? '',
                    'required'    => $field['required'] ?? false,
                    'hidden'      => $hidden,
                    'class'       => $field['class'] ?? [],
                    'priority'    => $field['priority'] ?? 10,
                    'options'     => $field['options'] ?? null, // For select fields
                    'custom'      => !in_array($key, $this->get_standard_field_keys()), // Flag custom fields
                    'autocomplete'=> $field['autocomplete'] ?? '',
                    'validate'    => $field['validate'] ?? [],
                ];
            }
        }

        // Sort by priority
        usort($formatted, function($a, $b) {
            return $a['priority'] <=> $b['priority'];
        });

        return [
            'ok' => true,
            'fields' => $formatted,
            'is_digital_only' => $is_digital_only,
        ];
    }

    /**
     * Get list of standard WooCommerce field keys
     */
    private function get_standard_field_keys(): array {
        return [
            'billing_first_name',
            'billing_last_name',
            'billing_company',
            'billing_country',
            'billing_address_1',
            'billing_address_2',
            'billing_city',
            'billing_state',
            'billing_postcode',
            'billing_phone',
            'billing_email',
            'shipping_first_name',
            'shipping_last_name',
            'shipping_company',
            'shipping_country',
            'shipping_address_1',
            'shipping_address_2',
            'shipping_city',
            'shipping_state',
            'shipping_postcode',
            'order_comments',
        ];
    }

    /** ----------------- Helpers ----------------- **/

    private function accurate_quote_via_wc_cart(array $payload): array {
        if (!WC()->customer) { WC()->customer = new \WC_Customer(get_current_user_id(), true); }
        if (!WC()->cart) { WC()->cart = new \WC_Cart(); }

        // Address context for taxes/shipping rules
        $ship = !empty($payload['shipping']) ? $payload['shipping'] : $payload['billing'];
        if (!empty($payload['billing'])) {
            foreach (['country','state','postcode','city','address_1','address_2'] as $k) {
                $setter = 'set_billing_' . $k;
                if (method_exists(WC()->customer, $setter) && isset($payload['billing'][$k])) {
                    WC()->customer->{$setter}(wc_clean($payload['billing'][$k]));
                }
            }
        }
        if (!empty($ship)) {
            foreach (['country','state','postcode','city','address_1','address_2'] as $k) {
                $setter = 'set_shipping_' . $k;
                if (method_exists(WC()->customer, $setter) && isset($ship[$k])) {
                    WC()->customer->{$setter}(wc_clean($ship[$k]));
                }
            }
        }
        WC()->customer->save();

        WC()->cart->empty_cart(true);
        foreach ($payload['items'] as $line) {
            $product = $this->load_product($line);
            if (is_wp_error($product)) {
                return ['error' => $product->get_error_message()];
            }
            WC()->cart->add_to_cart($product->get_id(), max(1, (int) ($line['qty'] ?? 1)));
        }

        if (!empty($payload['coupons'])) {
            foreach ($payload['coupons'] as $code) {
                try {
                    WC()->cart->apply_coupon($code);
                } catch (\Throwable $e) {
                    // ignore invalid in v0
                }
            }
        }

        WC()->cart->calculate_totals();

        $subtotal = (float) WC()->cart->get_subtotal();
        $discount = (float) WC()->cart->get_discount_total();
        $shipping_total = (float) WC()->cart->get_shipping_total();
        $tax_total = (float) WC()->cart->get_total_tax();
        $grand = (float) WC()->cart->get_total('edit');

        $lines = [];
        foreach (WC()->cart->get_cart() as $ci) {
            $p = $ci['data'];
            $qty = (int) $ci['quantity'];
            $price = (float) wc_get_price_to_display($p);
            $lines[] = [
                'product_id' => $p->get_id(),
                'name'       => $p->get_name(),
                'qty'        => $qty,
                'price'      => $price,
                'line_total' => $price * $qty,
            ];
        }

        return [
            'ok' => true,
            'items' => $lines,
            'totals'  => [
                'subtotal'        => wc_format_decimal($subtotal, wc_get_price_decimals()),
                'discount_total'  => wc_format_decimal($discount, wc_get_price_decimals()),
                'shipping_total'  => wc_format_decimal($shipping_total, wc_get_price_decimals()),
                'tax_total'       => wc_format_decimal($tax_total, wc_get_price_decimals()),
                'grand_total'     => wc_format_decimal($grand, wc_get_price_decimals()),
                'currency'        => get_woocommerce_currency(),
                'currency_symbol' => get_woocommerce_currency_symbol(),
                'currency_pos'    => get_option('woocommerce_currency_pos', 'left'),
                'decimals'        => wc_get_price_decimals(),
                'decimal_sep'     => wc_get_price_decimal_separator(),
                'thousand_sep'    => wc_get_price_thousand_separator(),
            ],
        ];
    }

    private function sanitize_payload(WP_REST_Request $r): array {
        $json = $r->get_json_params();

        $items = isset($json['items']) && is_array($json['items']) ? $json['items'] : [];
        $billing = isset($json['billing']) && is_array($json['billing']) ? $json['billing'] : [];
        $shipping = isset($json['shipping']) && is_array($json['shipping']) ? $json['shipping'] : [];
        $coupons = isset($json['coupons']) && is_array($json['coupons']) ? array_map('wc_clean', $json['coupons']) : [];

        return [
            'items' => array_map(function ($i) {
                return [
                    'product_id'   => isset($i['product_id']) ? (int) $i['product_id'] : 0,
                    'variation_id' => isset($i['variation_id']) ? (int) $i['variation_id'] : 0,
                    'qty'          => isset($i['qty']) ? (int) $i['qty'] : 1,
                    'meta'         => isset($i['meta']) && is_array($i['meta']) ? $i['meta'] : [],
                ];
            }, $items),
            'billing' => $billing,
            'shipping'=> $shipping,
            'coupons' => $coupons,
            'shipping_method' => isset($json['shipping_method']) ? wc_clean($json['shipping_method']) : null,
            'payment_method'  => isset($json['payment_method']) ? wc_clean($json['payment_method']) : null,
        ];
    }

    private function load_product(array $line) {
        $pid = (int)($line['variation_id'] ?? 0) ?: (int)($line['product_id'] ?? 0);
        if (!$pid) {
            return new WP_Error('bad_item', __('Invalid product id', 'woonoow'));
        }
        $product = wc_get_product($pid);
        if (!$product || !$product->is_purchasable()) {
            return new WP_Error('bad_item', __('Product not purchasable', 'woonoow'));
        }
        return $product;
    }

    private function only_address_fields(array $src): array {
        $keys = ['first_name','last_name','company','address_1','address_2','city','state','postcode','country','email','phone'];
        $out = [];
        foreach ($keys as $k) {
            if (isset($src[$k])) $out[$k] = wc_clean(wp_unslash($src[$k]));
        }
        return $out;
    }

    private function estimate_shipping(array $address, ?string $chosen_method): float {
        $country = wc_clean($address['country'] ?? '');
        $postcode = wc_clean($address['postcode'] ?? '');
        $state = wc_clean($address['state'] ?? '');
        $city = wc_clean($address['city'] ?? '');

        $cache_key = 'wnw_ship_' . md5(json_encode([$country, $state, $postcode, $city, (string) $chosen_method]));
        $cached = wp_cache_get($cache_key, 'woonoow');
        if ($cached !== false) { return (float) $cached; }

        if (!$country) return 0.0;

        $packages = [[
            'destination' => compact('country','state','postcode','city'),
            'contents_cost' => 0, // not exact in v0
            'contents' => [],
            'applied_coupons' => [],
            'user' => ['ID' => get_current_user_id()],
        ]];

        $zone = \WC_Shipping_Zones::get_zone_matching_package($packages[0]);
        $methods = $zone ? $zone->get_shipping_methods(true) : [];
        $cost = 0.0;

        foreach ($methods as $method) {
            if (!empty($chosen_method)) {
                $id = $method->id . ':' . $method->get_instance_id();
                if ($id === $chosen_method && method_exists($method, 'get_rates_for_package')) {
                    $rates = $method->get_rates_for_package($packages[0]);
                    if (!empty($rates)) {
                        $rate = reset($rates);
                        $cost = (float) $rate->get_cost();
                        break;
                    }
                }
            }
        }
        wp_cache_set($cache_key, $cost, 'woonoow', 60); // cache for 60 seconds
        return $cost;
    }

    private function find_shipping_rate_for_order(WC_Order $order, string $chosen) {
        $shipping = $order->get_address('shipping');
        $packages = [[
            'destination' => [
                'country'  => $shipping['country'] ?? '',
                'state'    => $shipping['state'] ?? '',
                'postcode' => $shipping['postcode'] ?? '',
                'city'     => $shipping['city'] ?? '',
            ],
            'contents' => [],
            'applied_coupons' => [],
            'user' => ['ID' => $order->get_user_id()],
        ]];

        $zone = \WC_Shipping_Zones::get_zone_matching_package($packages[0]);
        if (!$zone) return null;
        foreach ($zone->get_shipping_methods(true) as $method) {
            $id = $method->id . ':' . $method->get_instance_id();
            if ($id === $chosen && method_exists($method, 'get_rates_for_package')) {
                $rates = $method->get_rates_for_package($packages[0]);
                if (!empty($rates)) {
                    return reset($rates);
                }
            }
        }
        return null;
    }
}