<?php

/**
 * Subscription Lifecycle Hooks
 *
 * Fires custom hooks when subscription CPT is created or status changes.
 * Other features should listen to these custom hooks.
 *
 * Custom Hooks Fired:
 * - arraysubs_data_created (subscription_id, post_data)
 * - arraysubs_data_status_changed (subscription_id, new_status, old_status)
 *
 * @package ArraySubs\Features\Subscriptions\Services
 */

namespace ArraySubs\Features\Subscriptions\Services;

defined('ABSPATH') || exit;

/**
 * Subscription lifecycle hooks
 */
class Hooks
{
    /**
     * Enable auto-loading
     *
     * @var bool
     */
    public static $loadable = true;

    /**
     * Constructor
     */
    public function __construct()
    {
        // Hook into post creation - fires after a post is inserted
        add_action('wp_insert_post', [$this, 'onSubscriptionCreated'], 10, 3);

        // Hook into post status transitions
        add_action('transition_post_status', [$this, 'onSubscriptionStatusChanged'], 10, 3);

        // Claim guest subscriptions when WooCommerce converts a guest customer into a user.
        add_action('woocommerce_created_customer', [$this, 'claimGuestSubscriptionsForCustomer'], 20, 1);
        add_action('user_register', [$this, 'claimGuestSubscriptionsForCustomer'], 20, 1);
        add_action('woocommerce_update_new_customer_past_order', [$this, 'claimSubscriptionsForPastOrder'], 10, 2);
    }

    /**
     * Fire custom hook when subscription is created
     *
     * @param int      $post_id Post ID
     * @param \WP_Post $post Post object
     * @param bool     $update Whether this is an update
     */
    public function onSubscriptionCreated(int $post_id, \WP_Post $post, bool $update): void
    {
        // Only handle our subscription CPT
        if ($post->post_type !== 'arraysubs_data') {
            return;
        }

        // Only fire for new posts, not updates
        if ($update) {
            return;
        }

        /**
         * Fires when a new subscription is created
         *
         * @param int      $subscription_id The subscription post ID
         * @param \WP_Post $post The subscription post object
         */
        do_action('arraysubs_data_created', $post_id, $post);
    }

    /**
     * Fire custom hook when subscription status changes
     *
     * @param string   $new_status New post status
     * @param string   $old_status Old post status
     * @param \WP_Post $post Post object
     */
    public function onSubscriptionStatusChanged(string $new_status, string $old_status, \WP_Post $post): void
    {
        // Only handle our subscription CPT
        if ($post->post_type !== 'arraysubs_data') {
            return;
        }

        // Skip if status hasn't changed
        if ($new_status === $old_status) {
            return;
        }

        $this->syncLifecycleMetaForStatusChange($post->ID, $new_status, $old_status);

        /**
         * Fires when subscription status changes
         *
         * @param int    $subscription_id The subscription post ID
         * @param string $new_status The new status
         * @param string $old_status The old status
         */
        do_action('arraysubs_data_status_changed', $post->ID, $new_status, $old_status);

        /**
         * Fires for specific status transitions
         * Example: arraysubs_data_status_arraysubs-pending_to_arraysubs-active
         */
        do_action(
            "arraysubs_data_status_{$old_status}_to_{$new_status}",
            $post->ID,
            $new_status,
            $old_status
        );
    }

    /**
     * Keep lifecycle meta aligned for every subscription status transition.
     *
     * @param int    $subscription_id Subscription ID.
     * @param string $new_status      New post status.
     * @param string $old_status      Old post status.
     * @return void
     */
    private function syncLifecycleMetaForStatusChange(int $subscription_id, string $new_status, string $old_status): void
    {
        if ('arraysubs-on-hold' !== $new_status || 'arraysubs-on-hold' === $old_status) {
            return;
        }

        $existing_on_hold_date = get_post_meta($subscription_id, '_on_hold_date', true);

        if (!empty($existing_on_hold_date)) {
            return;
        }

        update_post_meta($subscription_id, '_on_hold_date', current_time('mysql', true));
    }

    /**
     * Claim guest subscriptions for a newly created customer account.
     *
     * @param int $customer_id Customer user ID.
     * @return void
     */
    public function claimGuestSubscriptionsForCustomer(int $customer_id): void
    {
        $customer_id = absint($customer_id);
        if ($customer_id <= 0) {
            return;
        }

        $customer = get_userdata($customer_id);
        if (!$customer || empty($customer->user_email)) {
            return;
        }

        $subscription_ids = $this->getGuestSubscriptionIdsByEmail($customer->user_email);
        $this->assignSubscriptionsToCustomer($subscription_ids, $customer_id, $customer->user_email);
    }

    /**
     * Claim subscriptions when WooCommerce links a guest order to a new customer account.
     *
     * @param int      $order_id  Order ID.
     * @param \WP_User $customer  Customer user.
     * @return void
     */
    public function claimSubscriptionsForPastOrder(int $order_id, \WP_User $customer): void
    {
        $customer_id = absint($customer->ID);
        if ($customer_id <= 0 || empty($customer->user_email)) {
            return;
        }

        $subscription_ids = function_exists('arraysubs_get_subscription_ids_for_order')
            ? arraysubs_get_subscription_ids_for_order(absint($order_id))
            : [];

        $this->assignSubscriptionsToCustomer($subscription_ids, $customer_id, $customer->user_email);
    }

    /**
     * Get guest subscription IDs by customer email.
     *
     * @param string $email Customer email.
     * @return array<int>
     */
    private function getGuestSubscriptionIdsByEmail(string $email): array
    {
        $email = sanitize_email($email);
        if (empty($email)) {
            return [];
        }

        $statuses = function_exists('arraysubs_get_subscription_statuses')
            ? array_keys(arraysubs_get_subscription_statuses())
            : 'any';

        $subscription_ids = get_posts([
            'post_type'      => 'arraysubs_data',
            'post_status'    => $statuses,
            'posts_per_page' => -1,
            'fields'         => 'ids',
            'meta_query'     => [
                'relation' => 'AND',
                [
                    'key'     => '_customer_email',
                    'value'   => $email,
                    'compare' => '=',
                ],
                [
                    'relation' => 'OR',
                    [
                        'key'     => '_customer_id',
                        'compare' => 'NOT EXISTS',
                    ],
                    [
                        'key'     => '_customer_id',
                        'value'   => 0,
                        'compare' => '<=',
                        'type'    => 'NUMERIC',
                    ],
                ],
            ],
        ]);

        return is_array($subscription_ids)
            ? array_values(array_unique(array_filter(array_map('absint', $subscription_ids))))
            : [];
    }

    /**
     * Assign subscriptions to a customer account.
     *
     * @param array<int> $subscription_ids Subscription IDs.
     * @param int        $customer_id      Customer user ID.
     * @param string     $email            Customer email.
     * @return void
     */
    private function assignSubscriptionsToCustomer(array $subscription_ids, int $customer_id, string $email): void
    {
        $customer_id = absint($customer_id);
        $email       = sanitize_email($email);

        if ($customer_id <= 0 || empty($email)) {
            return;
        }

        $subscription_ids = array_values(array_unique(array_filter(array_map('absint', $subscription_ids))));
        foreach ($subscription_ids as $subscription_id) {
            $subscription = get_post($subscription_id);
            if (!$subscription instanceof \WP_Post || $subscription->post_type !== 'arraysubs_data') {
                continue;
            }

            update_post_meta($subscription_id, '_customer_id', $customer_id);
            update_post_meta($subscription_id, '_customer_email', $email);

            if ((int) $subscription->post_author !== $customer_id) {
                wp_update_post([
                    'ID'          => $subscription_id,
                    'post_author' => $customer_id,
                ]);
            }
        }
    }
}
