<?php

namespace Pay4AllForMypos\Integrations;

use Pay4AllForMypos\Api\IpcClient;
use Pay4AllForMypos\Backend\ConfigPage;

defined( 'ABSPATH' ) || exit;

/**
 * Renders the myPOS refund panel inline on the Pay4All Form order detail
 * screen - hooked to `p4all_order_after_items` (fired right after the
 * « Produits commandés » card) so the refund UI sits next to the order
 * recap rather than in a sidebar metabox. Only emitted when the order's
 * payment_method is 'mypos'.
 *
 *   POST /wp-admin/admin-post.php?action=pay4all_for_mypos_form_refund
 *     Body: order_id, amount (optional decimal in the order currency),
 *           reason (optional free-text), _wpnonce
 */
final class FormOrderRefund
{
    public const ACTION_SLUG   = 'pay4all_for_mypos_form_refund';
    public const NONCE_KEY     = 'pay4all_for_mypos_form_refund_nonce';

    /**
     * Post meta storing the full refund history for the order. Shape:
     *   [ [ id => 'IPC_TRN_xxx', amount_cents => 500, reason => '...', created_at => 1719800000 ], ... ]
     */
    public const META_REFUNDS_HISTORY = '_pay4all_for_mypos_refunds';

    public static function register() : void
    {
        // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- third-party Pay4All Shop hook name, verbatim.
        add_action( 'p4all_order_after_items',       [ self::class, 'render_after_items' ], 10, 3 );
        add_action( 'admin_post_' . self::ACTION_SLUG, [ self::class, 'handle_refund' ] );
    }

    /**
     * Hooked to `p4all_order_after_items` - renders the myPOS refund card
     * inline on the order detail screen. Emits nothing at all when the
     * order was not paid via myPOS.
     *
     * @param array<string,mixed> $totals
     * @param array<string,mixed> $payment
     */
    public static function render_after_items( \WP_Post $post, array $totals = [], array $payment = [] ) : void
    {
        $order_id = (int) $post->ID;
        if ( $payment === [] ) {
            $payment = \Pay4All\Form\Domain\Order::payment( $order_id );
        }
        $method = (string) ( $payment['method'] ?? '' );
        if ( $method !== Pay4AllForm::METHOD_KEY ) {
            return;
        }
        if ( $totals === [] ) {
            $totals = \Pay4All\Form\Domain\Order::totals( $order_id );
        }
        $payment_state = (string) ( $payment['status'] ?? '' );
        $trnref        = (string) get_post_meta( $order_id, Pay4AllForm::META_TRNREF, true );
        $total         = (float) ( $totals['total'] ?? 0 );

        echo '<div class="p4all-card" style="margin-top:16px;">';
        echo '<h3>' . esc_html__( 'myPOS refund', 'pay4all-for-mypos' ) . '</h3>';
        self::render_panel_body( $order_id, $totals, $payment_state, $trnref, $total );
        echo '</div>';
    }

    private static function render_panel_body( int $order_id, array $totals, string $payment_state, string $trnref, float $total ) : void
    {
        $refunds         = self::get_refunds( $order_id );
        $total_cents     = (int) round( $total * 100 );
        $refunded_cents  = self::sum_refunded_cents( $refunds );
        $remaining_cents = max( 0, $total_cents - $refunded_cents );

        $currency_raw = $totals['currency'] ?? 'CHF';
        if ( is_array( $currency_raw ) ) { $currency_raw = (string) ( $currency_raw['code'] ?? 'CHF' ); }
        $currency = strtoupper( (string) $currency_raw );

        if ( ! empty( $refunds ) ) {
            self::render_refund_history( $refunds, $currency, $total_cents, $refunded_cents, $remaining_cents );
        }

        if ( $payment_state === 'refunded' ) {
            ?>
            <p style="color:#6e6e6e;margin-top:0;">
                <strong>&#10004;</strong> <?php esc_html_e( 'This order has been fully refunded.', 'pay4all-for-mypos' ); ?>
            </p>
            <?php
            return;
        }

        if ( ! in_array( $payment_state, [ 'paid', 'partially_refunded' ], true ) ) {
            ?><p class="description">
                <?php esc_html_e( 'myPOS refunds are only allowed on orders whose payment status is « paid » or « partially refunded ».', 'pay4all-for-mypos' ); ?>
            </p><?php
            return;
        }

        if ( $trnref === '' ) {
            ?><p class="description" style="color:#a00;">
                <?php esc_html_e( 'Unable to refund: no myPOS transaction reference (IPC_Trnref) is stored on this order. Issue the refund directly from the myPOS merchant portal.', 'pay4all-for-mypos' ); ?>
            </p><?php
            return;
        }

        if ( $remaining_cents <= 0 ) {
            ?><p class="description">
                <?php esc_html_e( 'The full order amount has already been refunded.', 'pay4all-for-mypos' ); ?>
            </p><?php
            return;
        }

        $remaining_display = number_format( $remaining_cents / 100, 2, '.', '' );
        ?>
        <p class="description">
            <?php
            printf(
                /* translators: 1: remaining refundable amount (formatted), 2: currency code */
                esc_html__( 'The refund is processed directly by myPOS. Leave the field empty to refund the remaining balance (%1$s %2$s) or enter a smaller amount.', 'pay4all-for-mypos' ),
                esc_html( $remaining_display ),
                esc_html( $currency )
            );
            ?>
        </p>
        <form method="post" action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>">
            <input type="hidden" name="action"   value="<?php echo esc_attr( self::ACTION_SLUG ); ?>">
            <input type="hidden" name="order_id" value="<?php echo esc_attr( (string) $order_id ); ?>">
            <?php wp_nonce_field( self::NONCE_KEY ); ?>
            <p>
                <label for="pay4all_for_mypos_refund_amount"><strong><?php esc_html_e( 'Amount to refund', 'pay4all-for-mypos' ); ?></strong></label>
                <input type="number"
                       step="0.01"
                       min="0.01"
                       max="<?php echo esc_attr( $remaining_display ); ?>"
                       id="pay4all_for_mypos_refund_amount"
                       name="amount"
                       class="widefat"
                       placeholder="<?php echo esc_attr( sprintf( '%s (%s)', $remaining_display, __( 'remaining', 'pay4all-for-mypos' ) ) ); ?>">
            </p>
            <p>
                <label for="pay4all_for_mypos_refund_reason"><strong><?php esc_html_e( 'Reason (optional)', 'pay4all-for-mypos' ); ?></strong></label>
                <input type="text"
                       id="pay4all_for_mypos_refund_reason"
                       name="reason"
                       maxlength="200"
                       class="widefat"
                       placeholder="<?php esc_attr_e( 'e.g.: damaged item, customer cancellation...', 'pay4all-for-mypos' ); ?>">
            </p>
            <p>
                <button type="submit"
                        class="button button-primary"
                        style="width:100%;"
                        onclick="return confirm('<?php echo esc_js( __( 'Confirm the refund? This action is irreversible on the myPOS side.', 'pay4all-for-mypos' ) ); ?>');">
                    <?php esc_html_e( 'Refund via myPOS', 'pay4all-for-mypos' ); ?>
                </button>
            </p>
            <p class="description" style="font-size:.85em;color:#666;">
                <?php esc_html_e( 'IPC_Trnref:', 'pay4all-for-mypos' ); ?>
                <code style="font-size:.9em;"><?php echo esc_html( $trnref ); ?></code>
            </p>
        </form>
        <?php
    }

    private static function render_refund_history( array $refunds, string $currency, int $total_cents, int $refunded_cents, int $remaining_cents ) : void
    {
        $fmt = static function ( int $cents ) use ( $currency ) : string {
            return number_format( $cents / 100, 2, '.', '' ) . ' ' . $currency;
        };
        ?>
        <div style="margin-bottom:12px;">
            <h4 style="margin:0 0 6px;"><?php esc_html_e( 'Refund history', 'pay4all-for-mypos' ); ?></h4>
            <table class="widefat striped" style="font-size:.85em;">
                <thead>
                    <tr>
                        <th><?php esc_html_e( 'Date', 'pay4all-for-mypos' ); ?></th>
                        <th style="text-align:right;"><?php esc_html_e( 'Amount', 'pay4all-for-mypos' ); ?></th>
                        <th><?php esc_html_e( 'Reason', 'pay4all-for-mypos' ); ?></th>
                    </tr>
                </thead>
                <tbody>
                <?php foreach ( array_reverse( $refunds ) as $r ) :
                    $ts     = (int) ( $r['created_at']   ?? 0 );
                    $amount = (int) ( $r['amount_cents'] ?? 0 );
                    $reason = (string) ( $r['reason']    ?? '' );
                    ?>
                    <tr>
                        <td><?php echo $ts > 0 ? esc_html( wp_date( 'd.m.Y H:i', $ts ) ) : '-'; ?></td>
                        <td style="text-align:right;white-space:nowrap;"><?php echo esc_html( $fmt( $amount ) ); ?></td>
                        <td><?php echo $reason !== '' ? esc_html( $reason ) : '<span style="color:#999;">-</span>'; ?></td>
                    </tr>
                <?php endforeach; ?>
                </tbody>
                <tfoot>
                    <tr>
                        <th colspan="2"><?php esc_html_e( 'Refunded total', 'pay4all-for-mypos' ); ?></th>
                        <td style="text-align:right;white-space:nowrap;"><strong><?php echo esc_html( $fmt( $refunded_cents ) ); ?></strong></td>
                    </tr>
                    <tr>
                        <td colspan="3" class="description" style="color:#666;font-style:italic;">
                            <?php
                            printf(
                                /* translators: 1: remaining refundable amount formatted, 2: total amount formatted */
                                esc_html__( '%1$s remaining out of %2$s', 'pay4all-for-mypos' ),
                                esc_html( $fmt( $remaining_cents ) ),
                                esc_html( $fmt( $total_cents ) )
                            );
                            ?>
                        </td>
                    </tr>
                </tfoot>
            </table>
        </div>
        <?php
    }

    /**
     * @return array<int,array{id:string,amount_cents:int,reason:string,created_at:int}>
     */
    public static function get_refunds( int $order_id ) : array
    {
        $raw = get_post_meta( $order_id, self::META_REFUNDS_HISTORY, true );
        return is_array( $raw ) ? array_values( $raw ) : [];
    }

    public static function sum_refunded_cents( array $refunds ) : int
    {
        $sum = 0;
        foreach ( $refunds as $r ) {
            $sum += (int) ( $r['amount_cents'] ?? 0 );
        }
        return $sum;
    }

    public static function handle_refund() : void
    {
        if ( ! current_user_can( 'manage_options' ) ) {
            wp_die( esc_html__( 'Permission denied.', 'pay4all-for-mypos' ), 403 );
        }
        $nonce = isset( $_REQUEST['_wpnonce'] ) ? sanitize_text_field( wp_unslash( (string) $_REQUEST['_wpnonce'] ) ) : '';
        if ( ! wp_verify_nonce( $nonce, self::NONCE_KEY ) ) {
            wp_die( esc_html__( 'Invalid nonce.', 'pay4all-for-mypos' ), 400 );
        }
        // phpcs:ignore WordPress.Security.NonceVerification.Missing -- nonce verified above.
        $order_id    = isset( $_POST['order_id'] ) ? (int) $_POST['order_id'] : 0;
        // phpcs:ignore WordPress.Security.NonceVerification.Missing -- nonce verified above.
        $amount_raw  = isset( $_POST['amount'] )   ? sanitize_text_field( wp_unslash( (string) $_POST['amount'] ) ) : '';
        // phpcs:ignore WordPress.Security.NonceVerification.Missing -- nonce verified above.
        $reason      = isset( $_POST['reason'] )   ? sanitize_text_field( wp_unslash( (string) $_POST['reason'] ) ) : '';

        $order = $order_id > 0 ? get_post( $order_id ) : null;
        if ( ! $order || $order->post_type !== \Pay4All\Form\PostTypes::ORDER ) {
            wp_die( esc_html__( 'Order not found.', 'pay4all-for-mypos' ), 404 );
        }

        $trnref = (string) get_post_meta( $order_id, Pay4AllForm::META_TRNREF, true );
        if ( $trnref === '' ) {
            self::flash_notice( 'error', __( 'No myPOS transaction reference found on this order.', 'pay4all-for-mypos' ) );
            self::redirect_back( $order_id );
        }

        $totals                 = \Pay4All\Form\Domain\Order::totals( $order_id );
        $total_cents            = (int) round( ( (float) ( $totals['total'] ?? 0 ) ) * 100 );
        $existing               = self::get_refunds( $order_id );
        $already_refunded_cents = self::sum_refunded_cents( $existing );
        $remaining_cents        = max( 0, $total_cents - $already_refunded_cents );

        if ( $remaining_cents <= 0 ) {
            self::flash_notice( 'error', __( 'The full amount has already been refunded.', 'pay4all-for-mypos' ) );
            self::redirect_back( $order_id );
        }

        if ( $amount_raw === '' || (float) $amount_raw <= 0 ) {
            $amount_cents = $remaining_cents;
        } else {
            $amount_cents = (int) round( (float) $amount_raw * 100 );
            if ( $amount_cents > $remaining_cents ) {
                $amount_cents = $remaining_cents;
            }
        }

        $currency_raw = $totals['currency'] ?? 'CHF';
        if ( is_array( $currency_raw ) ) { $currency_raw = (string) ( $currency_raw['code'] ?? 'CHF' ); }
        $currency = strtoupper( (string) $currency_raw );

        $mode   = ConfigPage::get_mode();
        $client = new IpcClient( $mode );
        $result = $client->refund( $trnref, $amount_cents / 100, $currency );

        if ( empty( $result['ok'] ) ) {
            $err = (string) ( $result['error'] ?? __( 'Unknown error', 'pay4all-for-mypos' ) );
            self::add_admin_note( $order_id, sprintf(
                /* translators: %s = error message returned by myPOS */
                __( 'myPOS refund failed: %s', 'pay4all-for-mypos' ),
                $err
            ) );
            self::flash_notice( 'error', sprintf(
                /* translators: %s = error message returned by myPOS */
                __( 'The refund failed: %s', 'pay4all-for-mypos' ),
                $err
            ) );
            self::redirect_back( $order_id );
        }

        $refund_ref = (string) ( $result['trnref'] ?? '' );

        $existing[] = [
            'id'           => $refund_ref !== '' ? $refund_ref : ( 'refund_' . time() ),
            'amount_cents' => $amount_cents,
            'reason'       => $reason,
            'created_at'   => time(),
        ];
        update_post_meta( $order_id, self::META_REFUNDS_HISTORY, $existing );

        $new_refunded_cents = $already_refunded_cents + $amount_cents;
        $is_now_full        = $new_refunded_cents >= $total_cents;
        \Pay4All\Form\Domain\Order::update_payment_status(
            $order_id,
            $is_now_full ? 'refunded' : 'partially_refunded'
        );

        $amount_str = number_format( $amount_cents / 100, 2, '.', '' ) . ' ' . $currency;
        $note = $reason !== ''
            ? sprintf(
                /* translators: 1: myPOS refund IPC_Trnref, 2: refund amount, 3: merchant reason */
                __( 'myPOS refund %1$s - %2$s - reason: %3$s', 'pay4all-for-mypos' ),
                $refund_ref !== '' ? $refund_ref : '-',
                $amount_str,
                $reason
            )
            : sprintf(
                /* translators: 1: myPOS refund IPC_Trnref, 2: refund amount */
                __( 'myPOS refund %1$s - %2$s.', 'pay4all-for-mypos' ),
                $refund_ref !== '' ? $refund_ref : '-',
                $amount_str
            );
        self::add_admin_note( $order_id, $note );

        $success_msg = $is_now_full
            ? __( 'Refund processed. The order is now fully refunded.', 'pay4all-for-mypos' )
            : sprintf(
                /* translators: %s = amount + currency (e.g. « 15.00 CHF ») */
                __( 'Refund processed successfully (%s).', 'pay4all-for-mypos' ),
                $amount_str
            );
        self::flash_notice( 'success', $success_msg );
        self::redirect_back( $order_id );
    }

    private static function add_admin_note( int $order_id, string $content ) : void
    {
        wp_insert_comment( [
            'comment_post_ID'  => $order_id,
            'comment_author'   => 'Pay4All myPOS',
            'comment_content'  => $content,
            'comment_type'     => 'pay4all_for_mypos_note',
            'comment_approved' => 1,
        ] );
    }

    private static function flash_notice( string $type, string $message ) : void
    {
        set_transient( 'pay4all_for_mypos_form_refund_notice_' . get_current_user_id(), [
            'type'    => $type,
            'message' => $message,
        ], 30 );
    }

    private static function redirect_back( int $order_id ) : void
    {
        wp_safe_redirect( admin_url( 'post.php?post=' . $order_id . '&action=edit' ) );
        exit;
    }

    /**
     * Called from Pay4AllForm::register() so the admin notice appears
     * after the redirect. Scoped to Pay4All Shop order edit screens only.
     */
    public static function maybe_print_notice() : void
    {
        $screen = function_exists( 'get_current_screen' ) ? get_current_screen() : null;
        if ( ! $screen instanceof \WP_Screen ) { return; }
        if ( ( $screen->post_type ?? '' ) !== 'p4all_order' ) { return; }

        $key    = 'pay4all_for_mypos_form_refund_notice_' . get_current_user_id();
        $notice = get_transient( $key );
        if ( ! is_array( $notice ) || empty( $notice['message'] ) ) {
            return;
        }
        delete_transient( $key );
        $class = ( $notice['type'] ?? 'success' ) === 'error' ? 'notice-error' : 'notice-success';
        ?>
        <div class="notice <?php echo esc_attr( $class ); ?> is-dismissible">
            <p><?php echo esc_html( (string) $notice['message'] ); ?></p>
        </div>
        <?php
    }
}
