<?php

namespace App\Services;

use App\Models\Contribution;
use App\Models\ContributionFee;
use App\Models\ContributionPenalty;
use App\Models\Member;
use App\Models\MembershipCategory;
use App\Models\MembershipTransaction;
use App\Models\MemberActivityLog;
use App\Services\PaymentService;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Carbon\Carbon;

class ContributionService
{
    protected PaymentService $paymentService;
    protected NotificationService $notificationService;

    public function __construct(PaymentService $paymentService, NotificationService $notificationService)
    {
        $this->paymentService = $paymentService;
        $this->notificationService = $notificationService;
    }

    /**
     * Calculate contribution for a member.
     */
    public function calculateContribution(Member $member, ?ContributionFee $fee = null, ?Carbon $dueDate = null): array
    {
        $category = $member->primaryCategory;
        
        if (!$category) {
            throw new \Exception('Member has no primary membership category');
        }

        if (!$fee) {
            $fee = $category->contributionFees()->active()->mandatory()->first();
        }

        if (!$fee) {
            throw new \Exception('No active contribution fee found for this category');
        }

        $dueDate = $dueDate ?? $fee->calculateNextDueDate();
        $period = $fee->getPeriodLabel($dueDate);

        return [
            'amount' => $fee->amount,
            'currency' => $fee->currency,
            'due_date' => $dueDate,
            'period' => $period,
            'contribution_fee_id' => $fee->id,
            'membership_category_id' => $category->id,
        ];
    }

    /**
     * Create a contribution for a member.
     */
    public function createContribution(Member $member, array $data, int $createdBy): Contribution
    {
        $data['reference_number'] = Contribution::generateReferenceNumber();
        $data['member_id'] = $member->id;
        $data['created_by'] = $createdBy;
        $data['total_amount'] = ($data['amount'] ?? 0) + ($data['penalty_amount'] ?? 0);

        $contribution = Contribution::create($data);

        MemberActivityLog::log(
            'contribution_created',
            "Contribution created: {$contribution->formatted_amount}",
            $member,
            \App\Models\User::find($createdBy),
            ['contribution_id' => $contribution->id]
        );

        return $contribution;
    }

    /**
     * Generate contributions for all active members.
     */
    public function generateBulkContributions(MembershipCategory $category, ?ContributionFee $fee = null): int
    {
        $fee = $fee ?? $category->contributionFees()->active()->mandatory()->first();
        
        if (!$fee) {
            return 0;
        }

        $count = 0;
        $dueDate = $fee->calculateNextDueDate();
        $period = $fee->getPeriodLabel($dueDate);

        $members = $category->members()->where('members.status', 'active')->get();

        foreach ($members as $member) {
            // Check if contribution already exists for this period
            $existingContribution = Contribution::where('member_id', $member->id)
                ->where('period', $period)
                ->where('contribution_fee_id', $fee->id)
                ->first();

            if (!$existingContribution) {
                Contribution::create([
                    'reference_number' => Contribution::generateReferenceNumber(),
                    'member_id' => $member->id,
                    'membership_category_id' => $category->id,
                    'contribution_fee_id' => $fee->id,
                    'amount' => $fee->amount,
                    'penalty_amount' => 0,
                    'total_amount' => $fee->amount,
                    'currency' => $fee->currency,
                    'due_date' => $dueDate,
                    'period' => $period,
                    'status' => 'pending',
                ]);
                $count++;
            }
        }

        return $count;
    }

    /**
     * Apply penalty to overdue contribution.
     */
    public function applyPenalty(Contribution $contribution): float
    {
        if ($contribution->is_penalty_applied || !$contribution->isOverdue()) {
            return 0;
        }

        $category = $contribution->membershipCategory;
        $penalty = $category->penalties()->active()->first();

        if (!$penalty) {
            return 0;
        }

        $daysOverdue = $contribution->getDaysOverdue();
        $penaltyAmount = $penalty->calculatePenalty($contribution->amount, $daysOverdue);

        $contribution->applyPenalty($penaltyAmount);

        MemberActivityLog::log(
            'penalty_applied',
            "Penalty applied: {$penaltyAmount} {$contribution->currency}",
            $contribution->member,
            null,
            ['contribution_id' => $contribution->id, 'penalty_amount' => $penaltyAmount]
        );

        return $penaltyAmount;
    }

    /**
     * Process contribution payment via MTN MoMo (mobile or card).
     */
    public function processPayment(
        Contribution $contribution,
        string $phoneNumber,
        string $paymentMethod = 'mobile',
        ?string $cardNumber = null,
        ?string $cardExpiry = null,
        ?string $cardCvv = null
    ): array {
        return DB::transaction(function () use ($contribution, $phoneNumber, $paymentMethod, $cardNumber, $cardExpiry, $cardCvv) {
            $storedMethod = $paymentMethod === 'card' ? 'mtn_momo_card' : 'mtn_momo_mobile';

            // Create transaction record
            $transaction = MembershipTransaction::create([
                'transaction_reference' => 'CON-PAY-' . strtoupper(uniqid()),
                'type'                  => 'contribution',
                'member_id'             => $contribution->member_id,
                'contribution_id'       => $contribution->id,
                'amount'                => $contribution->total_amount,
                'currency'              => $contribution->currency,
                'fee_amount'            => 0,
                'net_amount'            => $contribution->total_amount,
                'payment_method'        => $storedMethod,
                'payment_phone'         => $phoneNumber ?: null,
                'status'                => 'pending',
                'initiated_at'          => now(),
                'initiated_by'          => $contribution->member->user_id,
            ]);

            try {
                // Initiate payment via PaymentService (mobile or card)
                if ($paymentMethod === 'card') {
                    $result = $this->paymentService->initiateCardCollection(
                        cardNumber: $cardNumber ?? '',
                        expiry: $cardExpiry ?? '',
                        cvv: $cardCvv ?? '',
                        amount: $contribution->total_amount,
                        reference: $transaction->transaction_reference,
                        payerMessage: "Contribution payment - {$contribution->period}",
                        payeeNote: "Contribution payment for {$contribution->member->full_name}"
                    );
                } else {
                    $result = $this->paymentService->initiateCollection(
                        phoneNumber: $phoneNumber,
                        amount: $contribution->total_amount,
                        reference: $transaction->transaction_reference,
                        payerMessage: "Contribution payment - {$contribution->period}",
                        payeeNote: "Contribution payment for {$contribution->member->full_name}"
                    );
                }

                if ($result['success']) {
                    $transaction->update([
                        'external_reference' => $result['transaction_id'],
                        'status'             => 'processing',
                    ]);

                    return [
                        'success'               => true,
                        'message'               => 'Payment initiated. Please check your phone for the payment prompt.',
                        'transaction_reference' => $transaction->transaction_reference,
                    ];
                }

                $transaction->markAsFailed($result['message'] ?? 'Payment initiation failed');

                return [
                    'success' => false,
                    'message' => $result['message'] ?? 'Payment initiation failed',
                ];
            } catch (\Exception $e) {
                $transaction->markAsFailed($e->getMessage());

                Log::error('Contribution payment failed', [
                    'contribution_id' => $contribution->id,
                    'error'           => $e->getMessage(),
                ]);

                return [
                    'success' => false,
                    'message' => 'An error occurred while processing the payment.',
                ];
            }
        });
    }

    /**
     * Handle payment callback.
     */
    public function handlePaymentCallback(string $transactionReference, string $status, array $data = []): void
    {
        $transaction = MembershipTransaction::where('transaction_reference', $transactionReference)->first();

        if (!$transaction) {
            Log::warning('Payment callback for unknown transaction', ['reference' => $transactionReference]);
            return;
        }

        if ($status === 'SUCCESSFUL') {
            $transaction->markAsCompleted($data['transaction_id'] ?? null, $data);

            // Update contribution status
            if ($transaction->contribution_id) {
                $contribution = Contribution::find($transaction->contribution_id);
                $contribution->markAsPaid([
                    'paid_date' => now(),
                    'payment_method' => 'mtn_momo',
                    'payment_reference' => $transactionReference,
                    'transaction_id' => $data['transaction_id'] ?? null,
                    'payment_details' => $data,
                ]);

                // Send confirmation notification
                $this->notificationService->sendContributionConfirmation($contribution);
            }
        } else {
            $transaction->markAsFailed($data['reason'] ?? 'Payment failed', $status);

            // Send failure notification
            if ($transaction->contribution_id) {
                $contribution = Contribution::find($transaction->contribution_id);
                if ($contribution) {
                    try {
                        $this->notificationService->sendContributionPaymentFailed(
                            $contribution,
                            $data['reason'] ?? 'Payment was not completed.'
                        );
                    } catch (\Throwable $e) {
                        Log::error('Failed to send contribution payment failure notification', [
                            'contribution_id' => $contribution->id,
                            'error'           => $e->getMessage(),
                        ]);
                    }
                }
            }
        }
    }

    /**
     * Generate contribution history for a member.
     */
    public function generateHistory(Member $member, array $filters = []): array
    {
        $query = $member->contributions()->with(['membershipCategory', 'contributionFee']);

        if (!empty($filters['status'])) {
            $query->where('status', $filters['status']);
        }

        if (!empty($filters['category_id'])) {
            $query->where('membership_category_id', $filters['category_id']);
        }

        if (!empty($filters['from_date'])) {
            $query->where('due_date', '>=', $filters['from_date']);
        }

        if (!empty($filters['to_date'])) {
            $query->where('due_date', '<=', $filters['to_date']);
        }

        $contributions = $query->orderBy('due_date', 'desc')->paginate($filters['per_page'] ?? 15);

        return [
            'contributions' => $contributions,
            'total_paid' => $member->contributions()->paid()->sum('amount'),
            'pending_count' => $member->contributions()->pending()->count(),
            'overdue_count' => $member->contributions()->overdue()->count(),
            'total_outstanding' => $member->contributions()->whereIn('status', ['pending', 'overdue'])->sum('amount'),
            'total_pending' => $member->contributions()->pending()->sum('amount'),
            'total_overdue' => $member->contributions()->overdue()->sum('amount'),
            'total_penalty' => $member->contributions()->overdue()->sum('penalty_amount'),
        ];
    }

    /**
     * Get contribution statistics.
     */
    public function getStatistics(array $filters = []): array
    {
        $query = Contribution::query();

        if (!empty($filters['category_id'])) {
            $query->where('membership_category_id', $filters['category_id']);
        }

        if (!empty($filters['from_date'])) {
            $query->where('due_date', '>=', $filters['from_date']);
        }

        if (!empty($filters['to_date'])) {
            $query->where('due_date', '<=', $filters['to_date']);
        }

        return [
            'total' => $query->count(),
            'total_amount' => $query->sum('amount'),
            'paid' => (clone $query)->paid()->count(),
            'paid_amount' => (clone $query)->paid()->sum('amount'),
            'pending' => (clone $query)->pending()->count(),
            'pending_amount' => (clone $query)->pending()->sum('amount'),
            'overdue' => (clone $query)->overdue()->count(),
            'overdue_amount' => (clone $query)->overdue()->sum('amount'),
            'penalty_amount' => (clone $query)->overdue()->sum('penalty_amount'),
        ];
    }
}
