<?php

/**
 * Manual Subscription Controller
 *
 * @package ArraySubs\Features\SubscriptionAdmin\REST
 */

namespace ArraySubs\Features\SubscriptionAdmin\REST;

defined('ABSPATH') || exit;

use ArraySubs\Supports\BaseRestController;
use WP_REST_Request;
use WP_REST_Response;

/**
 * Manual subscription management REST API
 */
class ManualController extends BaseRestController
{
    public static $loadable = true;

    protected $namespace = 'arraysubs/v1';
    protected $rest_base = 'subscriptions/manual';

    public function __construct()
    {
        parent::__construct();
    }

    public function registerRoutes(): void
    {
        // Record manual payment
        register_rest_route($this->namespace, '/' . $this->rest_base . '/payment', [
            'methods' => 'POST',
            'callback' => [$this, 'recordPayment'],
            'permission_callback' => [$this, 'checkAdminPermission'],
            'args' => [
                'subscription_id' => [
                    'required' => true,
                    'type' => 'integer',
                ],
                'amount' => [
                    'required' => true,
                    'type' => 'number',
                ],
                'payment_method' => [
                    'required' => false,
                    'type' => 'string',
                ],
                'transaction_id' => [
                    'required' => false,
                    'type' => 'string',
                ],
                'note' => [
                    'required' => false,
                    'type' => 'string',
                ],
            ],
        ]);

        // Skip next payment
        register_rest_route($this->namespace, '/' . $this->rest_base . '/skip-payment', [
            'methods' => 'POST',
            'callback' => [$this, 'skipPayment'],
            'permission_callback' => [$this, 'checkAdminPermission'],
            'args' => [
                'subscription_id' => [
                    'required' => true,
                    'type' => 'integer',
                ],
            ],
        ]);

        // Change product
        register_rest_route($this->namespace, '/' . $this->rest_base . '/change-product', [
            'methods' => 'POST',
            'callback' => [$this, 'changeProduct'],
            'permission_callback' => [$this, 'checkAdminPermission'],
            'args' => [
                'subscription_id' => [
                    'required' => true,
                    'type' => 'integer',
                ],
                'new_product_id' => [
                    'required' => true,
                    'type' => 'integer',
                ],
                'prorate' => [
                    'required' => false,
                    'type' => 'boolean',
                    'default' => false,
                ],
            ],
        ]);

        // Update dates
        register_rest_route($this->namespace, '/' . $this->rest_base . '/update-dates', [
            'methods' => 'POST',
            'callback' => [$this, 'updateDates'],
            'permission_callback' => [$this, 'checkAdminPermission'],
            'args' => [
                'subscription_id' => [
                    'required' => true,
                    'type' => 'integer',
                ],
                'end_date' => [
                    'required' => false,
                    'type' => 'string',
                ],
            ],
        ]);

        // Add admin note
        register_rest_route($this->namespace, '/' . $this->rest_base . '/add-note', [
            'methods' => 'POST',
            'callback' => [$this, 'addNote'],
            'permission_callback' => [$this, 'checkAdminPermission'],
            'args' => [
                'subscription_id' => [
                    'required' => true,
                    'type' => 'integer',
                ],
                'note' => [
                    'required' => true,
                    'type' => 'string',
                ],
            ],
        ]);

        // Get subscription notes
        register_rest_route($this->namespace, '/' . $this->rest_base . '/(?P<id>\d+)/notes', [
            'methods' => 'GET',
            'callback' => [$this, 'getNotes'],
            'permission_callback' => [$this, 'checkAdminPermission'],
        ]);
    }

    /**
     * Check admin permission
     *
     * @return bool
     */
    public function checkAdminPermission(): bool
    {
        return current_user_can('manage_woocommerce') || current_user_can('manage_options');
    }

    /**
     * Record manual payment
     *
     * @param WP_REST_Request $request REST request.
     * @return WP_REST_Response
     */
    public function recordPayment(WP_REST_Request $request): WP_REST_Response
    {
        $subscription_id = (int) $request->get_param('subscription_id');
        $amount = (float) $request->get_param('amount');
        $payment_method = sanitize_text_field($request->get_param('payment_method') ?: 'manual');
        $transaction_id = sanitize_text_field($request->get_param('transaction_id') ?: '');
        $note = sanitize_textarea_field($request->get_param('note') ?: '');

        $subscription = arraysubs_get_subscription($subscription_id);
        if (!$subscription instanceof \WP_Post) {
            return arraysubs_rest_response(
                null,
                404,
                __('Subscription not found', 'arraysubs'),
                false
            );
        }

        // Create a manual payment order
        $order = wc_create_order([
            'customer_id' => (int) get_post_meta($subscription_id, '_customer_id', true),
            'status' => 'completed',
        ]);

        if (is_wp_error($order)) {
            return arraysubs_rest_response(
                null,
                500,
                $order->get_error_message(),
                false
            );
        }

        // Add order item
        $product_id = (int) get_post_meta($subscription_id, '_product_id', true);
        $product = wc_get_product($product_id);

        if ($product) {
            $order->add_product($product, 1, [
                'total' => $amount,
            ]);
        }

        // Set order meta
        $order->set_payment_method($payment_method);
        $order->update_meta_data('_subscription_id', $subscription_id);
        $order->update_meta_data('_is_manual_payment', 'yes');
        if ($transaction_id) {
            $order->set_transaction_id($transaction_id);
        }

        $order->calculate_totals();
        $order->save();

        // Update subscription
        $billing_interval = (int) get_post_meta($subscription_id, '_billing_interval', true);
        $billing_period = get_post_meta($subscription_id, '_billing_period', true);

        // For lifetime subscriptions, no next payment date
        if ($billing_period === 'lifetime') {
            $next_payment_date = '';
        } else {
            $next_payment_date = arraysubs_calculate_next_payment_date(
                current_time('mysql', true),
                $billing_interval,
                $billing_period
            );
        }

        update_post_meta($subscription_id, '_last_payment_date', current_time('mysql', true));
        update_post_meta($subscription_id, '_next_payment_date', $next_payment_date);

        // Activate subscription if on-hold
        if ($subscription->post_status === 'arraysubs-on-hold') {
            wp_update_post([
                'ID' => $subscription_id,
                'post_status' => 'arraysubs-active',
            ]);

            do_action('arraysubs_subscription_reactivated', $subscription_id);
        }

        // Add order ID to subscription (stored as JSON)
        $order_ids_json = get_post_meta($subscription_id, '_order_ids', true);
        $order_ids = !empty($order_ids_json) ? json_decode($order_ids_json, true) : [];
        if (!is_array($order_ids)) {
            $order_ids = [];
        }
        if (!in_array($order->get_id(), $order_ids, true)) {
            $order_ids[] = $order->get_id();
            update_post_meta($subscription_id, '_order_ids', \wp_json_encode($order_ids));
        }

        // Add note
        $full_note = sprintf(
            /* translators: 1: payment amount, 2: payment method */
            __('Manual payment recorded. Amount: %1$s. Payment method: %2$s.', 'arraysubs'),
            wc_price($amount),
            esc_html($payment_method)
        );
        if ($transaction_id) {
            $full_note .= ' ' . sprintf(
                /* translators: %s: transaction ID */
                __('Transaction ID: %s', 'arraysubs'),
                esc_html($transaction_id)
            );
        }
        if ($note) {
            $full_note .= ' ' . $note;
        }
        $this->addSubscriptionNote($subscription_id, $full_note);

        do_action('arraysubs_manual_payment_recorded', $subscription_id, $order->get_id(), $amount);

        return arraysubs_rest_response(
            [
                'subscription_id' => $subscription_id,
                'order_id' => $order->get_id(),
                'next_payment_date' => $next_payment_date,
            ],
            200,
            __('Payment recorded successfully', 'arraysubs')
        );
    }

    /**
     * Skip next payment
     *
     * @param WP_REST_Request $request REST request.
     * @return WP_REST_Response
     */
    public function skipPayment(WP_REST_Request $request): WP_REST_Response
    {
        $subscription_id = (int) $request->get_param('subscription_id');

        $subscription = arraysubs_get_subscription($subscription_id);
        if (!$subscription) {
            return arraysubs_rest_response(
                null,
                404,
                __('Subscription not found', 'arraysubs'),
                false
            );
        }

        // Calculate new next payment date (skip one billing cycle)
        $current_next_date = get_post_meta($subscription_id, '_next_payment_date', true);
        $billing_interval = (int) get_post_meta($subscription_id, '_billing_interval', true);
        $billing_period = get_post_meta($subscription_id, '_billing_period', true);

        // Lifetime subscriptions cannot skip payments
        if ($billing_period === 'lifetime') {
            return arraysubs_rest_response(
                null,
                400,
                __('Lifetime subscriptions do not have recurring payments to skip', 'arraysubs'),
                false
            );
        }

        $new_next_date = arraysubs_calculate_next_payment_date(
            $current_next_date ?: current_time('mysql', true),
            $billing_interval,
            $billing_period
        );

        update_post_meta($subscription_id, '_next_payment_date', $new_next_date);

        // Add note
        $this->addSubscriptionNote(
            $subscription_id,
            sprintf(
                /* translators: %s: new payment date */
                __('Next payment skipped. New payment date: %s', 'arraysubs'),
                arraysubs_format_date_local($new_next_date)
            )
        );

        do_action('arraysubs_payment_skipped', $subscription_id);

        return arraysubs_rest_response(
            ['next_payment_date' => $new_next_date],
            200,
            __('Payment skipped successfully', 'arraysubs')
        );
    }

    /**
     * Change product
     *
     * @param WP_REST_Request $request REST request.
     * @return WP_REST_Response
     */
    public function changeProduct(WP_REST_Request $request): WP_REST_Response
    {
        $subscription_id = (int) $request->get_param('subscription_id');
        $new_product_id = (int) $request->get_param('new_product_id');
        $prorate = (bool) $request->get_param('prorate');

        $subscription = arraysubs_get_subscription($subscription_id);
        if (!$subscription) {
            return arraysubs_rest_response(
                null,
                404,
                __('Subscription not found', 'arraysubs'),
                false
            );
        }

        // Get old product info
        $old_product_id = (int) get_post_meta($subscription_id, '_product_id', true);
        $old_product_name = arraysubs_get_subscription_product_name_by_subscription($subscription_id) ?: __('Unknown', 'arraysubs');

        // Get new product info
        $new_product = wc_get_product($new_product_id);
        if (!$new_product) {
            return arraysubs_rest_response(
                null,
                404,
                __('New product not found', 'arraysubs'),
                false
            );
        }

        // Get product name with variation suffix
        $new_product_name = arraysubs_get_subscription_product_name($new_product);

        // Get subscription data from new product
        $new_subscription_data = arraysubs_get_product_subscription_data($new_product_id);
        if (!$new_subscription_data) {
            return arraysubs_rest_response(
                null,
                400,
                __('New product is not a subscription product', 'arraysubs'),
                false
            );
        }

        // Update subscription
        update_post_meta($subscription_id, '_product_id', $new_product_id);
        update_post_meta($subscription_id, '_recurring_amount', $new_subscription_data['price']);
        update_post_meta($subscription_id, '_billing_period', $new_subscription_data['period']);
        update_post_meta($subscription_id, '_billing_interval', $new_subscription_data['interval']);

        // Update post title
        wp_update_post([
            'ID' => $subscription_id,
            'post_title' => sprintf(
                /* translators: %s: product name */
                __('Subscription to %s', 'arraysubs'),
                $new_product_name
            ),
        ]);

        // Add note
        $this->addSubscriptionNote(
            $subscription_id,
            sprintf(
                /* translators: 1: old product name, 2: new product name, 3: new recurring amount */
                __('Product changed from "%1$s" to "%2$s". New recurring amount: %3$s', 'arraysubs'),
                $old_product_name,
                $new_product_name,
                wc_price($new_subscription_data['price'])
            )
        );

        do_action('arraysubs_product_changed', $subscription_id, $old_product_id, $new_product_id);

        return arraysubs_rest_response(
            ['subscription' => arraysubs_get_subscription($subscription_id)],
            200,
            __('Product changed successfully', 'arraysubs')
        );
    }

    /**
     * Update dates
     *
     * @param WP_REST_Request $request REST request.
     * @return WP_REST_Response
     */
    public function updateDates(WP_REST_Request $request): WP_REST_Response
    {
        $subscription_id = (int) $request->get_param('subscription_id');
        $end_date        = $request->get_param('end_date');

        $subscription = arraysubs_get_subscription($subscription_id);
        if (!$subscription) {
            return arraysubs_rest_response(
                null,
                404,
                __('Subscription not found', 'arraysubs'),
                false
            );
        }

        if ($request->has_param('next_payment_date')) {
            return arraysubs_rest_response(
                null,
                400,
                __('Manual next payment date changes are no longer supported. ArraySubs calculates this automatically from the billing schedule.', 'arraysubs'),
                false
            );
        }

        $changes = [];

        if ($end_date) {
            update_post_meta($subscription_id, '_end_date', sanitize_text_field($end_date));
            $changes[] = sprintf(
                /* translators: %s: end date */
                __('End date: %s', 'arraysubs'),
                arraysubs_format_date_local($end_date)
            );
        }

        if (!empty($changes)) {
            $this->addSubscriptionNote(
                $subscription_id,
                __('Subscription dates updated: ', 'arraysubs') . implode(', ', $changes)
            );
        }

        if (empty($changes)) {
            return arraysubs_rest_response(
                ['subscription' => arraysubs_get_subscription($subscription_id)],
                200,
                __('No date changes were applied', 'arraysubs')
            );
        }

        do_action('arraysubs_dates_updated', $subscription_id);

        return arraysubs_rest_response(
            ['subscription' => arraysubs_get_subscription($subscription_id)],
            200,
            __('Dates updated successfully', 'arraysubs')
        );
    }

    /**
     * Add admin note
     *
     * @param WP_REST_Request $request REST request.
     * @return WP_REST_Response
     */
    public function addNote(WP_REST_Request $request): WP_REST_Response
    {
        $subscription_id = (int) $request->get_param('subscription_id');
        $note_text       = trim(wp_strip_all_tags((string) $request->get_param('note')));

        $subscription = arraysubs_get_subscription($subscription_id);
        if (!$subscription) {
            return arraysubs_rest_response(
                null,
                404,
                __('Subscription not found', 'arraysubs'),
                false
            );
        }

        $note_id = $this->addSubscriptionNote($subscription_id, $note_text);

        return arraysubs_rest_response(
            ['note_id' => $note_id],
            200,
            __('Note added successfully', 'arraysubs')
        );
    }

    /**
     * Get subscription notes
     *
     * @param WP_REST_Request $request REST request.
     * @return WP_REST_Response
     */
    public function getNotes(WP_REST_Request $request): WP_REST_Response
    {
        $subscription_id = (int) $request->get_param('id');

        $subscription = arraysubs_get_subscription($subscription_id);
        if (!$subscription) {
            return arraysubs_rest_response(
                null,
                404,
                __('Subscription not found', 'arraysubs'),
                false
            );
        }

        $notes     = arraysubs_get_subscription_notes($subscription_id);
        $formatted = array_map('arraysubs_format_note', $notes);

        return arraysubs_rest_response(
            ['notes' => $formatted],
            200
        );
    }

    /**
     * Add a note to subscription using the canonical notes CPT
     *
     * @param int    $subscription_id Subscription ID.
     * @param string $note            Note text.
     * @return int Note ID or 0 on failure.
     */
    private function addSubscriptionNote(int $subscription_id, string $note): int
    {
        $current_user = isset($GLOBALS['current_user']) ? $GLOBALS['current_user'] : null;

        $note_id = arraysubs_add_subscription_note(
            $subscription_id,
            $note,
            'private',
            isset($current_user->ID) ? (int) $current_user->ID : 0,
            [],
            'subscription'
        );

        return is_int($note_id) ? $note_id : 0;
    }
}
