<?php

namespace BWDK\Woo\Order;

use BWDK\Support\Constant;

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

/**
 * Safety net: never allow downgrades from final statuses back to processing for BWDK orders.
 */
class OrderStatusGuard {
    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 );
    }

    /**
     * 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 ( ! in_array($posted_status, ['completed', 'cancelled', 'refunded', 'failed'], true) ) {
            return;
        }

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

    public function respect_admin_manual_final_status( \WC_Order $order, $data_store ): void {
        if ( ! function_exists( 'bwdk_is_admin_manual_status_change_to_final' ) ) {
            return;
        }

        if ( ! bwdk_is_admin_manual_status_change_to_final( $order ) ) {
            return;
        }

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

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

        $order->set_status( $posted_status );
        $order->update_meta_data( '_bwdk_admin_manual_status_lock', time() );
        $order->update_meta_data( '_bwdk_admin_requested_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 );

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

        $order->set_status( $status );
        $order->update_meta_data( '_bwdk_admin_manual_status_lock', time() );
        $order->update_meta_data( '_bwdk_admin_requested_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;
        }

        $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 );
            $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;
        }

        // If order is already in a final status, never allow it to be set back to processing.
        $final_statuses = [ 'completed', 'cancelled', '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 );
        }
    }
}

