<?php

namespace BWDK\Woo\Order;

use BWDK\Support\Constant;

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

/**
 * Safety net: block ill-advised returns to processing from terminal Woo statuses for BWDK orders.
 * Cancelled -> processing is allowed (recovery after wrongful cancel / BWDK sync).
 */
class OrderStatusGuard {

    /** @var array<int, true> Per-request idempotency for HPOS completed guard. */
    private static $hpos_completed_guard_ran = [];

    /** @var array<int, true> Order IDs whose completed status was persisted this POST. */
    private static $hpos_completed_persisted_request = [];

    /** @var bool */
    private static $hpos_status_refresh_redirect_sent = false;

    public function register(): void {
        add_action( 'woocommerce_order_status_changed', [ $this, 'prevent_processing_downgrade' ], 1, 4 );
        add_action( 'woocommerce_before_order_object_save', [ $this, 'respect_admin_manual_final_status' ], 1, 2 );
        add_filter( 'woocommerce_rest_pre_insert_shop_order_object', [ $this, 'respect_admin_manual_final_status_rest' ], 1, 3 );
        add_action( 'woocommerce_after_order_object_save', [ $this, 'enforce_admin_manual_final_status_after_save' ], 999, 2 );
        add_action( 'woocommerce_admin_process_shop_order_object', [ $this, 'capture_admin_manual_status' ], 1 );

        $this->register_hpos_admin_save_hooks();
    }

    /**
     * HPOS Orders UI saves on load-woocommerce_page_wc-orders; recheck after WC_Meta_Box_Order_Data::save (prio 40).
     */
    private function register_hpos_admin_save_hooks(): void {
        add_action( 'load-woocommerce_page_wc-orders', [ $this, 'hpos_on_wc_orders_screen_load' ], 999 );
        add_action( 'load-woocommerce_page_wc-orders', [ $this, 'hpos_maybe_redirect_after_completed_persist' ], 10000 );
        add_action( 'woocommerce_process_shop_order_meta', [ $this, 'hpos_force_completed_for_digiexpress_late' ], 100, 2 );
        add_action( 'shutdown', [ $this, 'hpos_shutdown_force_completed_for_digiexpress' ], 99999 );
    }

    /**
     * HPOS-compatible: capture admin manual final status changes on save.
     */
    public function capture_admin_manual_status( \WC_Order $order ): void {
        if ( ! $order->get_meta( \BWDK\Support\Constant::META_BWDK_UUID, true ) ) {
            return;
        }

        $posted_status = isset($_POST['order_status'])
            ? wc_clean(wp_unslash($_POST['order_status']))
            : '';

        $posted_status = preg_replace('/^wc-/', '', $posted_status);

        if ( $posted_status === '' ) {
            return;
        }

        if ( $posted_status === 'processing' ) {
            $this->clear_bwdk_admin_manual_status_lock_meta( $order );
            return;
        }

        $order->update_meta_data( '_bwdk_admin_manual_status_lock', time() );
        $order->update_meta_data( '_bwdk_admin_requested_status', $posted_status );

        if ( in_array( $posted_status, [ 'completed', 'cancelled', 'refunded', 'failed' ], true ) ) {
            $order->set_status( $posted_status );
        }
    }

    public function respect_admin_manual_final_status( \WC_Order $order, $data_store ): void {
        if ( ! $order->get_meta( Constant::META_BWDK_UUID, true ) ) {
            return;
        }

        if ( ! is_admin() ) {
            return;
        }

        $posted_status = wc_clean( wp_unslash( $_POST['order_status'] ?? '' ) );
        $posted_status = preg_replace( '/^wc-/', '', $posted_status );

        if ( $posted_status === '' ) {
            return;
        }

        if ( $posted_status === 'processing' ) {
            $this->clear_bwdk_admin_manual_status_lock_meta( $order );
            return;
        }

        $order->update_meta_data( '_bwdk_admin_manual_status_lock', time() );
        $order->update_meta_data( '_bwdk_admin_requested_status', $posted_status );

        if ( in_array( $posted_status, [ 'completed', 'cancelled', 'refunded', 'failed' ], true ) ) {
            $order->set_status( $posted_status );
        }
    }

    /**
     * HPOS / new Orders UI saves via REST, not classic $_POST.
     * Ensure admin-requested final status is respected before insert/update.
     *
     * @param WC_Order         $order
     * @param \WP_REST_Request $request
     * @param bool             $creating
     * @return WC_Order
     */
    public function respect_admin_manual_final_status_rest( $order, $request, $creating ) {
        if ( ! $order instanceof WC_Order ) {
            return $order;
        }

        // Only apply to BWDK orders.
        $uuid = (string) $order->get_meta( Constant::META_BWDK_UUID, true );
        if ( $uuid === '' ) {
            return $order;
        }

        if ( ! ( defined( 'REST_REQUEST' ) && REST_REQUEST ) ) {
            return $order;
        }

        if ( ! $request instanceof \WP_REST_Request ) {
            return $order;
        }

        $status = $request->get_param( 'status' );
        if ( ! is_string( $status ) || $status === '' ) {
            // Fallback for non-standard payloads.
            $status = $request->get_param( 'order_status' );
        }
        $status = is_string( $status ) ? $status : '';
        $status = preg_replace( '/^wc-/', '', $status );

        if ( $status === '' ) {
            return $order;
        }

        if ( $status === 'processing' ) {
            $this->clear_bwdk_admin_manual_status_lock_meta( $order );
            return $order;
        }

        $order->update_meta_data( '_bwdk_admin_manual_status_lock', time() );
        $order->update_meta_data( '_bwdk_admin_requested_status', $status );

        if ( in_array( $status, [ 'completed', 'cancelled', 'refunded', 'failed' ], true ) ) {
            $order->set_status( $status );
        }

        return $order;
    }

    /**
     * Final safety: if admin requested a final status, make sure it sticks after save.
     */
    public function enforce_admin_manual_final_status_after_save( \WC_Order $order, $data_store ): void {
        $uuid = (string) $order->get_meta( Constant::META_BWDK_UUID, true );
        if ( $uuid === '' ) {
            return;
        }

        $requested_early = preg_replace( '/^wc-/', '', (string) $order->get_meta( '_bwdk_admin_requested_status', true ) );

        // Only drop lock when admin explicitly chose processing — not when completed save lagged as processing.
        if ( $order->get_status() === 'processing' && $requested_early === 'processing' ) {
            $had_lock = ( (int) $order->get_meta( '_bwdk_admin_manual_status_lock', true ) ) > 0;
            if ( $had_lock ) {
                remove_action( 'woocommerce_after_order_object_save', [ $this, 'enforce_admin_manual_final_status_after_save' ], 999 );
                try {
                    $this->clear_bwdk_admin_manual_status_lock_meta( $order );
                    $order->save();
                } finally {
                    add_action( 'woocommerce_after_order_object_save', [ $this, 'enforce_admin_manual_final_status_after_save' ], 999, 2 );
                }
            }
            return;
        }

        $lock_time = (int) $order->get_meta( '_bwdk_admin_manual_status_lock', true );
        if ( $lock_time <= 0 || ( time() - $lock_time ) >= 300 ) {
            return;
        }

        $requested = (string) $order->get_meta( '_bwdk_admin_requested_status', true );
        $requested = preg_replace( '/^wc-/', '', $requested );

        $final_statuses = array( 'completed', 'cancelled', 'refunded', 'failed' );
        if ( $requested === '' || ! in_array( $requested, $final_statuses, true ) ) {
            return;
        }

        if ( $order->get_status() === $requested ) {
            return;
        }

        // Prevent recursion.
        remove_action( 'woocommerce_after_order_object_save', [ $this, 'enforce_admin_manual_final_status_after_save' ], 999 );
        try {
            $order->set_status( $requested );
            if ( $requested === 'completed'
                && class_exists( '\BWDK\Admin\BWDK_Admin_DX_Meta_Box' )
                && \BWDK\Admin\BWDK_Admin_DX_Meta_Box::order_is_digiexpress_for_list( $order ) ) {
                $order->add_order_note(
                    __( 'BWDK DigiExpress order manually marked as completed in WooCommerce.', 'bwdk' ),
                    false
                );
            }
            $order->save();
        } finally {
            add_action( 'woocommerce_after_order_object_save', [ $this, 'enforce_admin_manual_final_status_after_save' ], 999, 2 );
        }
    }

    public function prevent_processing_downgrade( int $order_id, string $old_status, string $new_status, \WC_Order $order ): void {
        if ( $new_status !== 'processing' ) {
            return;
        }

        // Only apply to BWDK orders.
        $uuid = (string) $order->get_meta( Constant::META_BWDK_UUID, true );
        if ( $uuid === '' ) {
            return;
        }

        // Block return to processing from completed/refunded/failed (not cancelled — BWDK recovery).
        $final_statuses = [ 'completed', 'refunded', 'failed' ];
        if ( ! in_array( $old_status, $final_statuses, true ) ) {
            return;
        }

        // Prevent recursion.
        remove_action( 'woocommerce_order_status_changed', [ $this, 'prevent_processing_downgrade' ], 1 );
        try {
            $order->update_status( $old_status, 'BWDK: prevented downgrade back to processing.', false );
        } finally {
            add_action( 'woocommerce_order_status_changed', [ $this, 'prevent_processing_downgrade' ], 1, 4 );
        }
    }

    /**
     * Drop manual-final lock so recovered processing orders are not forced back to cancelled.
     */
    private function clear_bwdk_admin_manual_status_lock_meta( \WC_Order $order ): void {
        $order->delete_meta_data( '_bwdk_admin_manual_status_lock' );
        $order->delete_meta_data( '_bwdk_admin_requested_status' );
    }

    /**
     * HPOS: early persist on load-woocommerce_page_wc-orders when admin POST requests completed.
     */
    public function hpos_on_wc_orders_screen_load(): void {
        if ( ! is_admin() || ! current_user_can( 'manage_woocommerce' ) ) {
            return;
        }
        if ( ( $_SERVER['REQUEST_METHOD'] ?? '' ) !== 'POST' ) {
            return;
        }

        $posted_status = function_exists( 'bwdk_normalize_admin_posted_order_status' )
            ? bwdk_normalize_admin_posted_order_status()
            : '';
        if ( $posted_status !== 'completed' ) {
            return;
        }

        $order_id = function_exists( 'bwdk_resolve_admin_order_id_from_request' )
            ? bwdk_resolve_admin_order_id_from_request()
            : 0;
        if ( $order_id <= 0 ) {
            return;
        }

        $this->hpos_persist_completed_if_needed( $order_id, 'hpos_on_wc_orders_screen_load' );
    }

    /**
     * HPOS: after WC_Meta_Box_Order_Data::save (priority 40), recheck completed for BWDK + DigiExpress.
     *
     * @param int|string $order_id
     * @param \WC_Order|null $order
     */
    public function hpos_force_completed_for_digiexpress_late( $order_id, $order = null ): void {
        $order_id = absint( $order_id );
        if ( $order_id <= 0 ) {
            $order_id = function_exists( 'bwdk_resolve_admin_order_id_from_request' )
                ? bwdk_resolve_admin_order_id_from_request()
                : 0;
        }
        if ( $order_id <= 0 ) {
            return;
        }

        $this->hpos_persist_completed_if_needed( $order_id, 'hpos_force_completed_for_digiexpress_late' );
    }

    /**
     * Last-resort HPOS guard only when DB status is still not completed after the request.
     */
    public function hpos_shutdown_force_completed_for_digiexpress(): void {
        if ( ! is_admin() || ! current_user_can( 'manage_woocommerce' ) ) {
            return;
        }

        if ( ! function_exists( 'bwdk_is_hpos_orders_admin_screen' ) || ! bwdk_is_hpos_orders_admin_screen() ) {
            return;
        }

        $posted_status = function_exists( 'bwdk_normalize_admin_posted_order_status' )
            ? bwdk_normalize_admin_posted_order_status()
            : '';
        if ( $posted_status !== 'completed' ) {
            return;
        }

        $order_id = function_exists( 'bwdk_resolve_admin_order_id_from_request' )
            ? bwdk_resolve_admin_order_id_from_request()
            : 0;
        if ( $order_id <= 0 || ! function_exists( 'wc_get_order' ) ) {
            return;
        }

        $order = wc_get_order( $order_id );
        if ( ! $order instanceof \WC_Order || $order->get_status() === 'completed' ) {
            return;
        }

        $this->hpos_persist_completed_if_needed( $order_id, 'hpos_shutdown' );
    }

    /**
     * @param int    $order_id
     * @param string $log_tag
     */
    private function hpos_persist_completed_if_needed( int $order_id, string $log_tag ): void {
        if ( ! is_admin() || ! current_user_can( 'manage_woocommerce' ) ) {
            return;
        }

        if ( isset( self::$hpos_completed_guard_ran[ $order_id ] ) ) {
            return;
        }

        if ( ! function_exists( 'wc_get_order' ) ) {
            return;
        }

        $order = wc_get_order( $order_id );
        if ( ! $order instanceof \WC_Order ) {
            return;
        }

        if ( $order->get_status() === 'completed' ) {
            self::$hpos_completed_guard_ran[ $order_id ] = true;
            return;
        }

        $posted_status = function_exists( 'bwdk_normalize_admin_posted_order_status' )
            ? bwdk_normalize_admin_posted_order_status()
            : '';
        if ( $posted_status !== 'completed' ) {
            $requested_meta = preg_replace(
                '/^wc-/',
                '',
                (string) $order->get_meta( '_bwdk_admin_requested_status', true )
            );
            if ( $requested_meta !== 'completed' ) {
                return;
            }
        }

        $this->hpos_immediate_persist_completed( $order_id, $log_tag );
    }

    private function is_bwdk_digiexpress_order( \WC_Order $order ): bool {
        if ( ! $order->get_meta( Constant::META_BWDK_UUID, true ) ) {
            return false;
        }

        if ( class_exists( '\BWDK\Admin\BWDK_Admin_DX_Meta_Box' )
            && \BWDK\Admin\BWDK_Admin_DX_Meta_Box::order_is_digiexpress_for_list( $order ) ) {
            return true;
        }

        // Fallback: DX shipment meta present on HPOS first save before detection helper warms up.
        $ssd = $order->get_meta( '_bwdk_selected_shipping_data', true );
        if ( is_array( $ssd ) && ! empty( $ssd ) ) {
            return true;
        }

        return trim( (string) $order->get_meta( '_bwdk_dx_shipment_code', true ) ) !== '';
    }

    /**
     * Write lock + requested meta and completed status to DB immediately.
     *
     * @param int    $order_id
     * @param string $log_tag
     */
    private function hpos_immediate_persist_completed( int $order_id, string $log_tag ): void {
        if ( ! function_exists( 'wc_get_order' ) ) {
            return;
        }

        $order = wc_get_order( $order_id );
        if ( ! $order instanceof \WC_Order || ! $this->is_bwdk_digiexpress_order( $order ) ) {
            return;
        }

        if ( $order->get_status() === 'completed' ) {
            self::$hpos_completed_guard_ran[ $order_id ] = true;
            return;
        }

        remove_action( 'woocommerce_order_status_changed', [ $this, 'prevent_processing_downgrade' ], 1 );

        try {
            $order->update_meta_data( '_bwdk_admin_manual_status_lock', time() );
            $order->update_meta_data( '_bwdk_admin_requested_status', 'completed' );

            $posted_raw = isset( $_POST['order_status'] ) ? wc_clean( wp_unslash( $_POST['order_status'] ) ) : 'completed';
            $order->set_status( $posted_raw, '', true );
            $order->save();
        } finally {
            add_action( 'woocommerce_order_status_changed', [ $this, 'prevent_processing_downgrade' ], 1, 4 );
        }

        $verify = wc_get_order( $order_id );
        if ( $verify instanceof \WC_Order && $verify->get_status() === 'completed' ) {
            self::$hpos_completed_guard_ran[ $order_id ] = true;
            self::$hpos_completed_persisted_request[ $order_id ] = true;
        } elseif ( function_exists( 'bwdk_dx_status_log_hpos_step' ) ) {
            bwdk_dx_status_log_hpos_step( $log_tag . ':persist', $order_id, [ 'result' => 'not_completed' ] );
        }
    }

    /**
     * After POST save, reload HPOS order edit so status dropdown reflects DB (avoids stale in-memory UI).
     *
     * @param int|string     $order_id
     * @param \WC_Order|null $order
     */
    public function hpos_maybe_redirect_after_completed_persist( $order_id = 0, $order = null ): void {
        if ( self::$hpos_status_refresh_redirect_sent ) {
            return;
        }

        if ( ( $_SERVER['REQUEST_METHOD'] ?? '' ) !== 'POST' ) {
            return;
        }

        if ( ! is_admin() || ! current_user_can( 'manage_woocommerce' ) ) {
            return;
        }

        if ( ! $this->is_hpos_wc_orders_admin_post_request() ) {
            return;
        }

        if ( $this->hpos_status_refresh_query_arg_present() ) {
            return;
        }

        $posted_status = function_exists( 'bwdk_normalize_admin_posted_order_status' )
            ? bwdk_normalize_admin_posted_order_status()
            : '';
        if ( $posted_status !== 'completed' ) {
            return;
        }

        $order_id = absint( $order_id );
        if ( $order_id <= 0 ) {
            $order_id = function_exists( 'bwdk_resolve_admin_order_id_from_request' )
                ? bwdk_resolve_admin_order_id_from_request()
                : 0;
        }
        if ( $order_id <= 0 || ! isset( self::$hpos_completed_persisted_request[ $order_id ] ) ) {
            return;
        }

        if ( ! function_exists( 'wc_get_order' ) ) {
            return;
        }

        $order = wc_get_order( $order_id );
        if ( ! $order instanceof \WC_Order || ! $this->is_bwdk_digiexpress_order( $order ) ) {
            return;
        }

        if ( $order->get_status() !== 'completed' ) {
            return;
        }

        if ( headers_sent() ) {
            return;
        }

        $redirect_url = add_query_arg(
            [
                'page'                     => 'wc-orders',
                'action'                   => 'edit',
                'id'                       => $order_id,
                'bwdk_dx_status_refreshed' => '1',
            ],
            admin_url( 'admin.php' )
        );

        self::$hpos_status_refresh_redirect_sent = true;
        wp_safe_redirect( $redirect_url );
        exit;
    }

    private function is_hpos_wc_orders_admin_post_request(): bool {
        foreach ( [ $_GET, $_POST, $_REQUEST ] as $source ) {
            if ( isset( $source['page'] ) && (string) $source['page'] === 'wc-orders' ) {
                return true;
            }
        }

        return function_exists( 'bwdk_is_hpos_orders_admin_screen' ) && bwdk_is_hpos_orders_admin_screen();
    }

    private function hpos_status_refresh_query_arg_present(): bool {
        foreach ( [ $_GET, $_REQUEST ] as $source ) {
            if ( isset( $source['bwdk_dx_status_refreshed'] ) && (string) $source['bwdk_dx_status_refreshed'] !== '' ) {
                return true;
            }
        }

        return false;
    }
}

