<?php

/**
 * Date Calculation Helper Functions
 *
 * Functions for calculating subscription dates, intervals, and durations.
 *
 * @package ArraySubs
 * @since 1.0.0
 */

defined('ABSPATH') || exit;

/**
 * Get the default WordPress display format for date/time values.
 *
 * @param string $format Optional explicit format.
 * @return string
 */
function arraysubs_get_display_datetime_format(string $format = ''): string
{
    if (!empty($format)) {
        return $format;
    }

    return get_option('date_format') . ' ' . get_option('time_format');
}

/**
 * Convert a MySQL datetime string into a Unix timestamp using a known source timezone.
 *
 * @param string         $date     Date in MySQL format.
 * @param \DateTimeZone  $timezone Source timezone for the provided date.
 * @return int|null
 */
function arraysubs_get_datetime_timestamp_from_string(string $date, \DateTimeZone $timezone): ?int
{
    if (empty($date) || $date === '0000-00-00 00:00:00') {
        return null;
    }

    try {
        $date_time = new \DateTimeImmutable($date, $timezone);

        return $date_time->getTimestamp();
    } catch (\Exception $exception) {
        $timestamp = strtotime($date);

        return $timestamp ?: null;
    }
}

/**
 * Get the timezone label (e.g. "UTC+6", "UTC-5", "UTC+5:30") from WordPress settings.
 *
 * @return string Timezone offset label.
 */
function arraysubs_get_timezone_label(): string
{
    $offset_seconds = (int) wp_timezone()->getOffset(new \DateTimeImmutable('now', new \DateTimeZone('UTC')));

    if (0 === $offset_seconds) {
        return 'UTC';
    }

    $sign    = $offset_seconds >= 0 ? '+' : '-';
    $hours   = absint(intdiv($offset_seconds, 3600));
    $minutes = absint(($offset_seconds % 3600) / 60);

    if ($minutes > 0) {
        return sprintf('UTC%s%d:%02d', $sign, $hours, $minutes);
    }

    return sprintf('UTC%s%d', $sign, $hours);
}

/**
 * Format a timestamp using the current WordPress timezone and display settings.
 *
 * The formatted output includes a timezone offset suffix (e.g. "UTC+6") so
 * the displayed date is always unambiguous regardless of admin timezone changes.
 *
 * @param int    $timestamp Unix timestamp.
 * @param string $format    Optional explicit format.
 * @return string
 */
function arraysubs_format_timestamp_local(int $timestamp, string $format = ''): string
{
    if ($timestamp <= 0) {
        return '';
    }

    $formatted = wp_date(arraysubs_get_display_datetime_format($format), $timestamp, wp_timezone());

    return $formatted . ' (' . arraysubs_get_timezone_label() . ')';
}

/**
 * Format a site-local MySQL datetime string using the current WordPress timezone.
 *
 * @param string $local_date Date in MySQL format stored in site local time.
 * @param string $format     Optional explicit format.
 * @return string
 */
function arraysubs_format_site_local_date(string $local_date, string $format = ''): string
{
    $timestamp = arraysubs_get_datetime_timestamp_from_string($local_date, wp_timezone());

    if ($timestamp === null) {
        return '';
    }

    return arraysubs_format_timestamp_local($timestamp, $format);
}

/**
 * Calculate next payment date from a given date
 *
 * @param string $start_date Start date in MySQL format (UTC)
 * @param int    $interval Billing interval
 * @param string $period Billing period (day, week, month, year)
 * @return string Next payment date in MySQL format (UTC)
 */
function arraysubs_calculate_next_payment_from_date(string $start_date, int $interval, string $period): string
{
    // Lifetime subscriptions have no recurring payments
    if ($period === 'lifetime') {
        return '';
    }

    $start_timestamp = strtotime($start_date);

    switch ($period) {
        case 'day':
            $next = strtotime("+{$interval} days", $start_timestamp);
            break;
        case 'week':
            $next = strtotime("+{$interval} weeks", $start_timestamp);
            break;
        case 'month':
            $next = strtotime("+{$interval} months", $start_timestamp);
            break;
        case 'year':
            $next = strtotime("+{$interval} years", $start_timestamp);
            break;
        default:
            $next = strtotime('+1 month', $start_timestamp);
    }

    return gmdate('Y-m-d H:i:s', $next);
}

/**
 * Calculate subscription end date
 *
 * @param string $start_date Start date in MySQL format (UTC)
 * @param int    $interval Billing interval
 * @param string $period Billing period
 * @param int    $length Total number of billing cycles (0 = never expires)
 * @return string|null End date or null if never expires
 */
function arraysubs_calculate_end_date_from_length(string $start_date, int $interval, string $period, int $length): ?string
{
    if ($length === 0) {
        return null; // Never expires
    }

    // Lifetime subscriptions: if length is set, calculate end date from start using length as days
    // Otherwise they never expire
    if ($period === 'lifetime') {
        return null; // Lifetime deals don't expire via billing cycles
    }

    $start_timestamp = strtotime($start_date);
    $total_interval = $interval * $length;

    switch ($period) {
        case 'day':
            $end = strtotime("+{$total_interval} days", $start_timestamp);
            break;
        case 'week':
            $end = strtotime("+{$total_interval} weeks", $start_timestamp);
            break;
        case 'month':
            $end = strtotime("+{$total_interval} months", $start_timestamp);
            break;
        case 'year':
            $end = strtotime("+{$total_interval} years", $start_timestamp);
            break;
        default:
            $end = strtotime("+{$total_interval} months", $start_timestamp);
    }

    return gmdate('Y-m-d H:i:s', $end);
}

/**
 * Get days until next payment
 *
 * @param int $subscription_id Subscription ID
 * @return int|false Number of days or false if no next payment
 */
function arraysubs_get_days_until_next_payment(int $subscription_id)
{
    $next_payment = get_post_meta($subscription_id, '_next_payment_date', true);

    if (empty($next_payment)) {
        return false;
    }

    $next_payment_time = strtotime($next_payment);
    $now = time();

    $diff = $next_payment_time - $now;

    return (int) ceil($diff / DAY_IN_SECONDS);
}

/**
 * Get days until expiration
 *
 * @param int $subscription_id Subscription ID
 * @return int|false Number of days or false if never expires
 */
function arraysubs_get_days_until_expiration(int $subscription_id)
{
    $end_date = get_post_meta($subscription_id, '_end_date', true);

    if (empty($end_date) || $end_date === '0000-00-00 00:00:00') {
        return false; // Never expires
    }

    $end_time = strtotime($end_date);
    $now = time();

    $diff = $end_time - $now;

    return (int) ceil($diff / DAY_IN_SECONDS);
}

/**
 * Format date interval for display
 *
 * @param int    $interval Interval value
 * @param string $period Period (day, week, month, year)
 * @return string Formatted interval string
 */
function arraysubs_format_date_interval(int $interval, string $period): string
{
    // Lifetime subscriptions have no interval-based display
    if ($period === 'lifetime') {
        return __('Lifetime Deal', 'arraysubs');
    }

    $labels = [
        'day' => _n('day', 'days', $interval, 'arraysubs'),
        'week' => _n('week', 'weeks', $interval, 'arraysubs'),
        'month' => _n('month', 'months', $interval, 'arraysubs'),
        'year' => _n('year', 'years', $interval, 'arraysubs'),
    ];

    $label = $labels[$period] ?? $labels['month'];

    /* translators: 1: duration number, 2: period label such as "day" or "weeks". */
    return sprintf(__('%1$d %2$s', 'arraysubs'), $interval, $label);
}

/**
 * Check if date is in the past
 *
 * @param string $date Date in MySQL format (UTC)
 * @return bool True if date is in the past
 */
function arraysubs_is_date_past(string $date): bool
{
    $date_time = strtotime($date);
    $now = time();

    return $date_time < $now;
}

/**
 * Check if date is today
 *
 * @param string $date Date in MySQL format (UTC)
 * @return bool True if date is today
 */
function arraysubs_is_date_today(string $date): bool
{
    $date_time = strtotime($date);
    $now_local = new \DateTimeImmutable('now', wp_timezone());
    $today_start = (int) $now_local->setTime(0, 0)->getTimestamp();
    $today_end = (int) $now_local->modify('+1 day')->setTime(0, 0)->getTimestamp() - 1;

    return $date_time >= $today_start && $date_time <= $today_end;
}

/**
 * Get human-readable time difference
 *
 * @param string $date Date in MySQL format (UTC)
 * @return string Human-readable difference
 */
function arraysubs_get_time_diff_human(string $date): string
{
    $date_time = strtotime($date);
    $now = time();

    $diff = $date_time - $now;

    if ($diff < 0) {
        // Past date
        return sprintf(
            /* translators: %s: Human-readable time difference */
            __('%s ago', 'arraysubs'),
            human_time_diff($date_time, $now)
        );
    } else {
        // Future date
        return sprintf(
            /* translators: %s: Human-readable time difference */
            __('in %s', 'arraysubs'),
            human_time_diff($now, $date_time)
        );
    }
}

/**
 * Format billing period for display
 *
 * Creates a human-readable string like "Every month", "Every 2 weeks", etc.
 *
 * @param int|string $interval Billing interval
 * @param string     $period   Billing period (day, week, month, year)
 * @return string Formatted billing period
 */
function arraysubs_format_billing_period($interval, string $period): string
{
    // Lifetime subscriptions have no recurring billing
    if ($period === 'lifetime') {
        return __('Lifetime Deal', 'arraysubs');
    }

    $interval = (int) $interval;

    if ($interval <= 0) {
        $interval = 1;
    }

    $period_labels = [
        'day'   => _n('day', 'days', $interval, 'arraysubs'),
        'week'  => _n('week', 'weeks', $interval, 'arraysubs'),
        'month' => _n('month', 'months', $interval, 'arraysubs'),
        'year'  => _n('year', 'years', $interval, 'arraysubs'),
    ];

    $period_label = $period_labels[$period] ?? $period_labels['month'];

    if ($interval === 1) {
        /* translators: %s: period label (e.g., "week", "month") */
        return sprintf(__('Every %s', 'arraysubs'), $period_label);
    }

    /* translators: 1: billing interval number, 2: period name in plural (e.g., "weeks", "months") */
    return sprintf(__('Every %1$d %2$s', 'arraysubs'), $interval, $period_label);
}

/**
 * Convert date to local timezone
 *
 * @param string $utc_date Date in MySQL format (UTC)
 * @param string $format Date format (default: WordPress date format)
 * @return string Formatted date in local timezone
 */
function arraysubs_format_date_local(string $utc_date, string $format = ''): string
{
    $timestamp = arraysubs_get_datetime_timestamp_from_string(
        $utc_date,
        new \DateTimeZone('UTC')
    );

    if ($timestamp === null) {
        return '';
    }

    return arraysubs_format_timestamp_local($timestamp, $format);
}
