<?php
declare(strict_types=1);

namespace App\Modules\ActivitySubscriptions\Services;

use App\Core\App;
use App\Core\EventBus;
use App\Modules\ActivitySubscriptions\Models\ActivitySubscription;
use App\Modules\ActivitySubscriptions\Models\ActivityPricing;

class ActivitySubscriptionService
{
    /**
     * Generate monthly subscriptions for all active enrollments in the given month.
     *
     * For each active enrollment, creates a subscription record. If the enrollment
     * started after the 15th of its first month, marks it as half-month with half rate.
     *
     * @param string $month Format YYYY-MM
     * @return int Number of subscriptions generated
     */
    public static function generateMonthlySubscriptions(string $month): int
    {
        $db = App::getInstance()->db();
        $count = 0;

        // Get all active enrollments
        $enrollments = $db->select(
            "SELECT e.*, p.player_type, a.discipline_id
             FROM `enrollments` e
             JOIN `players` p ON p.id = e.player_id
             JOIN `academy_levels` al ON al.id = e.level_id
             JOIN `academies` a ON a.id = al.academy_id
             WHERE e.`status` = 'active'
               AND (e.`end_date` IS NULL OR e.`end_date` >= ?)
               AND e.`start_date` <= ?",
            [$month . '-01', $month . '-28']
        );

        foreach ($enrollments as $enrollment) {
            $playerId     = (int) $enrollment['player_id'];
            $enrollmentId = (int) $enrollment['id'];
            $disciplineId = (int) ($enrollment['discipline_id'] ?? 0);

            // Skip if subscription already exists for this enrollment + month
            $existing = ActivitySubscription::query()
                ->where('enrollment_id', '=', $enrollmentId)
                ->where('subscription_month', '=', $month)
                ->first();

            if ($existing) {
                continue;
            }

            // Check if this is the first month and enrollment started after the 15th
            $enrollmentDay = (int) date('d', strtotime($enrollment['start_date'] ?? ''));
            $enrollmentMonth = date('Y-m', strtotime($enrollment['start_date'] ?? ''));
            $isHalfMonth = ($enrollmentMonth === $month && $enrollmentDay > 15);

            // Calculate rate
            $rate = self::calculateRate($enrollmentId, $isHalfMonth);
            $baseRate = $rate['base'];
            $appliedRate = $rate['applied'];

            // Due date is the 7th of the subscription month
            $dueDate = $month . '-07';

            $sub = ActivitySubscription::create([
                'player_id'          => $playerId,
                'enrollment_id'      => $enrollmentId,
                'discipline_id'      => $disciplineId,
                'subscription_month' => $month,
                'player_type'        => $enrollment['player_type'] ?? 'member',
                'base_rate'          => $baseRate,
                'is_half_month'      => $isHalfMonth ? 1 : 0,
                'applied_rate'       => $appliedRate,
                'discount'           => bcsub((string) $baseRate, (string) $appliedRate, 2),
                'total_amount'       => $appliedRate,
                'status'             => 'pending',
                'due_date'           => $dueDate,
            ]);

            EventBus::dispatch('activity_sub.generated', [
                'subscription_id' => (int) $sub->id,
                'player_id'       => $playerId,
                'month'           => $month,
            ]);

            $count++;
        }

        return $count;
    }

    /**
     * Calculate the rate for an enrollment.
     *
     * Looks up the enrollment's academy pricing and returns base and applied rates.
     *
     * @return array{base: string, applied: string}
     */
    public static function calculateRate(int $enrollmentId, bool $isHalfMonth = false): array
    {
        $db = App::getInstance()->db();

        $enrollment = $db->selectOne(
            "SELECT e.*, p.player_type, a.id as academy_id, a.discipline_id
             FROM `enrollments` e
             JOIN `players` p ON p.id = e.player_id
             JOIN `academy_levels` al ON al.id = e.level_id
             JOIN `academies` a ON a.id = al.academy_id
             WHERE e.`id` = ?",
            [$enrollmentId]
        );

        if (!$enrollment) {
            return ['base' => '0.00', 'applied' => '0.00'];
        }

        $isMember = ($enrollment['player_type'] ?? '') === 'member';
        $academyId = (int) ($enrollment['academy_id'] ?? 0);

        // Try academy pricing first, then discipline pricing
        $rate = ActivityPricing::getEffectiveRate('academy', $academyId, $isMember);
        if ($rate <= 0 && !empty($enrollment['discipline_id'])) {
            $rate = ActivityPricing::getEffectiveRate('discipline', (int) $enrollment['discipline_id'], $isMember);
        }

        $baseRate = number_format($rate, 2, '.', '');
        $appliedRate = $isHalfMonth
            ? number_format($rate / 2, 2, '.', '')
            : $baseRate;

        return ['base' => $baseRate, 'applied' => $appliedRate];
    }

    /**
     * Mark a subscription as paid.
     */
    public static function paySubscription(int $subscriptionId, int $paymentId): bool
    {
        $sub = ActivitySubscription::find($subscriptionId);
        if (!$sub) {
            return false;
        }

        $sub->update([
            'status'     => 'paid',
            'paid_at'    => date('Y-m-d H:i:s'),
            'payment_id' => $paymentId,
        ]);

        EventBus::dispatch('activity_sub.paid', [
            'subscription_id' => $subscriptionId,
            'player_id'       => (int) $sub->player_id,
            'payment_id'      => $paymentId,
        ]);

        return true;
    }

    /**
     * Revoke overdue subscriptions for a given month.
     *
     * Finds subscriptions where status is 'pending' or 'overdue' and
     * due_date + 7 days < today, then sets them to 'revoked'.
     */
    public static function revokeOverdue(string $month): int
    {
        $db = App::getInstance()->db();
        $today = date('Y-m-d');
        $count = 0;

        $rows = ActivitySubscription::query()
            ->where('subscription_month', '=', $month)
            ->whereRaw("`status` IN ('pending', 'overdue')")
            ->whereRaw('DATE_ADD(`due_date`, INTERVAL 7 DAY) < ?', [$today])
            ->get();

        foreach ($rows as $row) {
            $sub = ActivitySubscription::find((int) $row['id']);
            if (!$sub) {
                continue;
            }

            $sub->update([
                'status'     => 'revoked',
                'revoked_at' => date('Y-m-d H:i:s'),
            ]);

            EventBus::dispatch('activity_sub.revoked', [
                'subscription_id' => (int) $row['id'],
                'player_id'       => (int) $row['player_id'],
                'month'           => $month,
            ]);

            $count++;
        }

        return $count;
    }

    /**
     * Grant an exemption on a subscription.
     */
    public static function grantExemption(int $subscriptionId, int $exemptedBy, string $reason): bool
    {
        $sub = ActivitySubscription::find($subscriptionId);
        if (!$sub) {
            return false;
        }

        $sub->update([
            'status'           => 'exempted',
            'exempted_by'      => $exemptedBy,
            'exemption_reason' => $reason,
        ]);

        EventBus::dispatch('activity_sub.exempted', [
            'subscription_id' => $subscriptionId,
            'player_id'       => (int) $sub->player_id,
            'exempted_by'     => $exemptedBy,
        ]);

        return true;
    }
}
