<?php

namespace App\Http\Controllers\Api;

use App\Models\User;
use App\Models\Association;
use App\Models\FinancialAssignment;
use App\Models\FinancialItem;
use App\Models\Payment;
use App\Models\PrepaidPeriod;
use App\Models\PolicyDocument;
use App\Services\AssociationPaystackService;
use App\Services\FinancialManagementService;
use Illuminate\Http\Request;
use Illuminate\Support\Str;

class MemberController extends BaseApiController
{
    /**
     * Get current user's profile
     */
    public function profile(Request $request)
    {
        $user = $request->user();
        $associationId = $user->last_association_context ?? $user->associations->first()?->id;

        $user->load([
            'associations',
            'workProfile',
            'profileFieldValues' => function ($query) use ($associationId) {
                $query->where('association_id', $associationId)
                      ->with('profileField');
            }
        ]);
        
        return $this->success($user, 'Profile retrieved successfully');
    }

    /**
     * Update user profile
     */
    public function updateProfile(Request $request)
    {
        $validated = $request->validate([
            'firstname' => 'sometimes|string|max:255',
            'surname' => 'sometimes|string|max:255',
            'middlename' => 'sometimes|nullable|string|max:255',
            'phone' => 'sometimes|string|max:20',
            'date_of_birth' => 'sometimes|nullable|date',
            'gender' => 'sometimes|nullable|in:male,female,other',
            'address' => 'sometimes|nullable|string|max:500',
            'city' => 'sometimes|nullable|string|max:100',
            'state' => 'sometimes|nullable|string|max:100',
            'country' => 'sometimes|nullable|string|max:100',
        ]);

        $user = $request->user();
        $user->update($validated);

        // Reload with association-filtered relationships
        $associationId = $user->last_association_context ?? $user->associations->first()?->id;
        
        $user->load([
            'associations',
            'workProfile',
            'profileFieldValues' => function ($query) use ($associationId) {
                $query->where('association_id', $associationId)
                      ->with('profileField');
            }
        ]);

        return $this->success($user, 'Profile updated successfully');
    }

    /**
     * Upload user cover photo
     */
    public function uploadCover(Request $request)
    {
        $request->validate([
            'cover' => 'required|image|mimes:jpeg,png,jpg,gif|max:2048',
        ]);

        $user = $request->user();
        
        if ($request->hasFile('cover')) {
            // Delete old cover if exists and is not default
            if ($user->cover_photo_path) {
                \Storage::disk('public')->delete($user->cover_photo_path);
            }

            // Store file
            $path = $request->file('cover')->store('covers', 'public');
            
            // Save directly to users table
            $user->update(['cover_photo_path' => $path]);
        }

        // Return the full URL for immediate frontend display
        return $this->success([
            'cover' => $user->cover_photo_path,
            'cover_url' => $user->cover_url, // Uses the accessor we just updated
        ], 'Cover photo uploaded successfully');
    }

    /**
     * Upload user avatar
     */
    public function uploadAvatar(Request $request)
    {
        $request->validate([
            'avatar' => 'required|image|mimes:jpeg,png,jpg,gif|max:2048',
        ]);

        $user = $request->user();
        
        if ($request->hasFile('avatar')) {
            // Delete old avatar if exists
            if ($user->avatar) {
                \Storage::disk('public')->delete($user->avatar);
            }

            $path = $request->file('avatar')->store('avatars', 'public');
            $user->update(['profile_photo_path' => $path]);
        }

        return $this->success([
            'avatar' => $user->avatar,
            'avatar_url' => $user->avatar, // Accessor already returns full URL
        ], 'Avatar uploaded successfully');
    }

    /**
     * Get dashboard statistics for current user
     */
    public function dashboardStats(Request $request)
    {
        $user = $request->user();

        // Ensure percentage is fresh
        $user->calculateProfileCompletion();

        $associationId = $user->last_association_context ?? $user->associations()->first()?->id;
        $association = $user->associations()->where('associations.id', $associationId)->first();

        // Single source of truth for all financial numbers
        $financialService = app(FinancialManagementService::class);
        $financialSummary = $financialService->getMemberFinancialSummary($user, $associationId);

        // Payment trend (last 6 months) for sparkline chart
        $paymentTrend = $user->payments()
            ->where('status', 'completed')
            ->where('association_id', $associationId)
            ->where('paid_at', '>=', now()->subMonths(6)->startOfMonth())
            ->selectRaw("DATE_FORMAT(paid_at, '%Y-%m') as month, SUM(amount) as total")
            ->groupBy('month')
            ->orderBy('month')
            ->get()
            ->map(fn ($row) => [
                'month' => $row->month,
                'total' => round((float) $row->total, 2),
            ]);

        $stats = [
            'events_registered' => $user->eventRegistrations()->count(),
            'events_attended' => $user->eventRegistrations()->where('attendance_status', 'attended')->count(),
            'payments_made' => $user->payments()->where('status', 'completed')->count(),
            'total_paid' => $financialSummary['total_paid'],
            'membership_status' => $user->membership_status ?? 'active',
            'profile_completion' => $user->profile_complete_percentage,
            'member_since' => $user->created_at->format('Y-m-d'),
            'association' => [
                'name' => $association?->name,
                'logo' => $association?->logo_path ? asset('storage/' . $association->logo_path) : null,
                'dashboard_message' => $association?->extra_settings ? ($association->extra_settings['dashboard_message'] ?? null) : null,
            ],
            'financial' => [
                'total_outstanding' => $financialSummary['total_due'],
                'total_paid' => $financialSummary['total_paid'],
                'overdue_count' => $financialSummary['overdue_count'],
                'pending_count' => $financialSummary['pending_count'],
                'next_due' => $financialSummary['upcoming'],
                'prepaid_periods_active' => $financialSummary['prepaid_active_count'],
                'by_category' => $financialSummary['by_category'] ?? [],
            ],
            'payment_trend' => $paymentTrend,
        ];

        return $this->success($stats, 'Dashboard statistics retrieved successfully');
    }

    /**
     * Update profile field values
     */
    public function updateProfileFields(Request $request)
    {
        $validated = $request->validate([
            'fields' => 'required|array',
            'fields.*.field_id' => 'required|exists:profile_fields,id',
            'fields.*.value' => 'nullable',
        ]);

        $user = $request->user();
        $associationId = $user->last_association_context ?? $user->associations->first()?->id;

        foreach ($validated['fields'] as $fieldData) {
            $value = $fieldData['value'];
            
            $user->profileFieldValues()->updateOrCreate(
                [
                    'profile_field_id' => $fieldData['field_id'],
                    'association_id' => $associationId,
                ],
                [
                    'value' => is_array($value) ? null : (string) $value,
                    'value_json' => is_array($value) ? $value : null,
                ]
            );
        }

        // Recalculate profile completion
        $user->calculateProfileCompletion();

        $user->load([
            'profileFieldValues' => function ($query) use ($associationId) {
                $query->where('association_id', $associationId)
                      ->with('profileField');
            }
        ]);

        return $this->success([
            'profile_completion' => $user->profile_complete_percentage,
            'user' => $user,
        ], 'Profile fields updated successfully');
    }

    /**
     * Get a specific policy document
     */
    public function getPolicy(Request $request, string $type)
    {
        $user = $request->user();
        $associationId = null;

        if ($user) {
            $associationId = $user->last_association_context ?? $user->associations->first()?->id;
        } else {
            // If guest, try to get association from query param (e.g. for registration page)
            $associationId = $request->query('association_id');
        }

        // 1. Try to find association-specific policy
        $document = null;
        if ($associationId) {
            $document = PolicyDocument::where('association_id', $associationId)
                ->where('type', $type)
                ->where('is_active', true)
                ->first();
        }

        // 2. Fallback to global policy
        if (!$document) {
            $document = PolicyDocument::whereNull('association_id')
                ->where('type', $type)
                ->where('is_active', true)
                ->first();
        }

        if (!$document) {
            return $this->error('Policy document not found', null, 404);
        }

        return $this->success($document, 'Policy document retrieved successfully');
    }

    /**
     * Update user password
     */
    public function updatePassword(Request $request)
    {
        $validated = $request->validate([
            'current_password' => 'required|string',
            'password' => 'required|string|min:8|confirmed',
        ]);

        $user = $request->user();

        if (!\Hash::check($validated['current_password'], $user->password)) {
            return $this->error('The provided current password does not match our records.', null, 422);
        }

        $user->update([
            'password' => \Hash::make($validated['password']),
        ]);

        return $this->success(null, 'Password updated successfully');
    }

    /**
     * Get membership status for current user
     */
    public function membershipStatus(Request $request)
    {
        $user = $request->user();
        $associationId = $user->last_association_context ?? $user->associations->first()?->id;

        if (!$associationId) {
            return $this->error('No association context found', 404);
        }

        $association = \App\Models\Association::find($associationId);
        $status = $user->getMembershipStatus($associationId);
        $latestAssignment = $user->getLatestMembershipAssignment($associationId);

        return $this->success([
            'status' => $status,
            'has_active_subscription' => $user->hasActiveSubscription($associationId),
            'requires_payment' => $association->requiresPaidMembership(),
            'association_settings' => [
                'membership_mode' => $association->membership_mode,
                'standard_fee' => $association->getDefaultMembershipFee(),
                'billing_cycle' => $association->getDefaultBillingCycle(),
            ],
            'payment_configuration' => $association->getPaymentConfiguration(),
            'latest_assignment' => $latestAssignment ? [
                'id' => $latestAssignment->id,
                'amount' => $latestAssignment->amount,
                'amount_paid' => $latestAssignment->amount_paid,
                'due_date' => $latestAssignment->due_date,
                'status' => $latestAssignment->status,
                'financial_item' => [
                    'id' => $latestAssignment->financialItem->id,
                    'title' => $latestAssignment->financialItem->title,
                    'description' => $latestAssignment->financialItem->description,
                    'type' => $latestAssignment->financialItem->type,
                    'frequency' => $latestAssignment->financialItem->frequency,
                    'grace_period_days' => $latestAssignment->financialItem->grace_period_days,
                ]
            ] : null
        ]);
    }

    /**
     * Get financial assignments for current user
     */
    public function financialAssignments(Request $request)
    {
        $user = $request->user();
        $associationId = $user->last_association_context ?? $user->associations->first()?->id;

        if (!$associationId) {
            return $this->error('No association context found', 404);
        }

        $perPage = $request->input('per_page', 20);
        $status = $request->input('status');

        $query = \App\Models\FinancialAssignment::where('user_id', $user->id)
            ->where('association_id', $associationId)
            ->obligations()  // Only admin-assigned obligations — excludes on-demand purchases
            ->with(['financialItem'])
            ->when($status, function ($q) use ($status) {
                $q->where('status', $status);
            })
            ->orderBy('due_date', 'desc');

        return $this->paginated($query, $perPage);
    }

    /**
     * Get specific financial assignment
     */
    public function financialAssignment(Request $request, $id)
    {
        $user = $request->user();
        $associationId = $user->last_association_context ?? $user->associations->first()?->id;

        $assignment = \App\Models\FinancialAssignment::where('user_id', $user->id)
            ->where('association_id', $associationId)
            ->with(['financialItem'])
            ->find($id);

        if (!$assignment) {
            return $this->error('Financial assignment not found', 404);
        }

        return $this->success($assignment);
    }

    /**
     * Initiate payment for financial assignment (multi-method support)
     */
    public function initiatePayment(Request $request)
    {
        $validated = $request->validate([
            'financial_assignment_id' => 'required|integer|exists:financial_assignments,id',
            'amount' => 'required|numeric|min:0',
            'payment_method' => 'required|in:paystack,bank_transfer,cash',
            'callback_url' => 'nullable|url',
        ]);

        $user = $request->user();
        $associationId = $user->last_association_context ?? $user->associations->first()?->id;
        $association = \App\Models\Association::find($associationId);

        // Get assignment
        $assignment = \App\Models\FinancialAssignment::where('user_id', $user->id)
            ->where('association_id', $associationId)
            ->with(['financialItem'])
            ->find($validated['financial_assignment_id']);

        if (!$assignment) {
            return $this->error('Financial assignment not found', 404);
        }

        // Check if already paid
        if ($assignment->status === 'paid') {
            return $this->error('This assignment has already been paid', 422);
        }

        // Calculate balance
        $balance = (float)$assignment->amount - (float)$assignment->amount_paid;

        if ($validated['amount'] > $balance) {
            return $this->error('Payment amount exceeds outstanding balance', 422);
        }

        // Policy enforcement: partial payment check (uses charge snapshot for temporal integrity)
        if ($validated['amount'] < $balance && !$assignment->canPartialPay()) {
            return $this->error(
                'Partial payment is not allowed for this item. Please pay the full balance of ₦' .
                number_format($balance, 2),
                422
            );
        }

        // PAYSTACK: Online payment via association's Paystack account
        if ($validated['payment_method'] === 'paystack') {
            if (!$association->hasPaystackConfigured()) {
                return $this->error('Paystack payment not configured for this association. Please use bank transfer or contact admin.', 422);
            }

            try {
                // Generate unique reference
                $reference = 'GLD-' . strtoupper(uniqid()) . '-' . $assignment->id;

                // Use association-specific Paystack service
                $paystackService = new \App\Services\AssociationPaystackService($association);
                $paystackData = $paystackService->initializeTransaction(
                    email: $user->email,
                    amountInKobo: (int)($validated['amount'] * 100),
                    reference: $reference,
                    metadata: [
                        'user_id' => $user->id,
                        'financial_assignment_id' => $assignment->id,
                        'financial_item_title' => $assignment->financialItem->title,
                        'type' => $assignment->financialItem->type,
                    ],
                    callbackUrl: $validated['callback_url'] ?? null,
                );

                // Create payment record
                $payment = \App\Models\Payment::create([
                    'uuid' => \Str::uuid(),
                    'user_id' => $user->id,
                    'association_id' => $associationId,
                    'payable_type' => \App\Models\FinancialAssignment::class,
                    'payable_id' => $assignment->id,
                    'amount' => $validated['amount'],
                    'gateway' => 'paystack',
                    'payment_method' => 'paystack',
                    'reference' => $reference,
                    'access_code' => $paystackData['access_code'],
                    'status' => 'pending',
                ]);

                return $this->success([
                    'payment_id' => $payment->id,
                    'payment_method' => 'paystack',
                    'authorization_url' => $paystackData['authorization_url'],
                    'access_code' => $paystackData['access_code'],
                    'reference' => $paystackData['reference'],
                ], 'Payment initiated successfully');

            } catch (\App\Exceptions\PaystackException $e) {
                \Log::error('Payment initiation failed', [
                    'association_id' => $associationId,
                    'assignment_id' => $assignment->id,
                    'error' => $e->getMessage(),
                ]);

                return $this->error('Failed to initiate payment: ' . $e->getMessage(), 500);
            }
        }

        // BANK TRANSFER: Manual payment - show bank details
        if ($validated['payment_method'] === 'bank_transfer') {
            if (!$association->hasBankTransferConfigured()) {
                return $this->error('Bank transfer details not configured for this association', 422);
            }

            $reference = 'BT-' . strtoupper(uniqid()) . '-' . $assignment->id;

            // Create payment record (pending admin approval)
            $payment = \App\Models\Payment::create([
                'uuid' => \Str::uuid(),
                'user_id' => $user->id,
                'association_id' => $associationId,
                'payable_type' => \App\Models\FinancialAssignment::class,
                'payable_id' => $assignment->id,
                'amount' => $validated['amount'],
                'gateway' => 'manual',
                'payment_method' => 'bank_transfer',
                'reference' => $reference,
                'status' => 'pending', // Awaiting admin approval
            ]);

            return $this->success([
                'payment_id' => $payment->id,
                'payment_method' => 'bank_transfer',
                'reference' => $reference,
                'bank_details' => [
                    'bank_name' => $association->bank_name,
                    'account_number' => $association->account_number,
                    'account_name' => $association->account_name,
                ],
                'instructions' => $association->payment_instructions,
                'note' => 'Please upload proof of payment after making the transfer',
            ], 'Bank transfer details retrieved successfully');
        }

        // CASH: Physical payment (admin only - members can't initiate cash payments)
        if ($validated['payment_method'] === 'cash') {
            return $this->error('Cash payments must be recorded by association admin', 422);
        }

        return $this->error('Invalid payment method', 422);
    }

    /**
     * Verify payment transaction (Paystack only)
     */
    public function verifyPayment(Request $request, $reference)
    {
        $user = $request->user();
        $associationId = $user->last_association_context ?? $user->associations->first()?->id;
        $association = \App\Models\Association::find($associationId);

        // Find payment by reference
        $payment = \App\Models\Payment::where('reference', $reference)
            ->where('user_id', $user->id)
            ->first();

        if (!$payment) {
            return $this->error('Payment not found', 404);
        }

        // Only Paystack payments need verification
        if ($payment->payment_method !== 'paystack') {
            return $this->error('Only Paystack payments can be verified via this endpoint', 422);
        }

        // If already verified, return success
        if ($payment->status === 'completed') {
            $assignment = $payment->payable;

            return $this->success([
                'reference' => $reference,
                'amount' => $payment->amount,
                'status' => 'success',
                'paid_at' => $payment->paid_at,
                'financial_assignment' => $assignment,
            ], 'Payment already verified');
        }

        try {
            // Verify with Paystack using association-specific service
            $paystackService = new \App\Services\AssociationPaystackService($association);
            $transactionData = $paystackService->verifyTransaction($reference);

            // Check if payment was successful
            if ($transactionData['status'] !== 'success') {
                $payment->update([
                    'status' => 'failed',
                    'failure_reason' => $transactionData['gateway_response'] ?? 'Payment failed',
                ]);

                return $this->error('Payment verification failed', 422);
            }

            // Update payment status
            $payment->update([
                'status' => 'completed',
                'paid_at' => now(),
                'authorization_code' => $transactionData['authorization']['authorization_code'] ?? null,
            ]);

            // Update financial assignment
            $assignment = $payment->payable;

            if ($assignment && $assignment instanceof \App\Models\FinancialAssignment) {
                $newAmountPaid = (float)$assignment->amount_paid + (float)$payment->amount;
                $totalAmount = (float)$assignment->amount;

                $assignment->update([
                    'amount_paid' => $newAmountPaid,
                    'status' => $newAmountPaid >= $totalAmount ? 'paid' : 'pending',
                ]);
            }

            return $this->success([
                'reference' => $reference,
                'amount' => $payment->amount,
                'status' => 'success',
                'paid_at' => $payment->paid_at,
                'financial_assignment' => $assignment,
            ], 'Payment verified successfully');

        } catch (\App\Exceptions\PaystackException $e) {
            \Log::error('Payment verification failed', [
                'association_id' => $associationId,
                'reference' => $reference,
                'error' => $e->getMessage(),
            ]);

            return $this->error('Failed to verify payment: ' . $e->getMessage(), 500);
        }
    }

    /**
     * Upload proof of payment (for bank transfer/cash payments)
     */
    public function uploadProofOfPayment(Request $request, $paymentId)
    {
        $request->validate([
            'proof' => 'required|image|mimes:jpeg,png,jpg,pdf|max:2048', // 2MB max
            'notes' => 'nullable|string|max:500',
        ]);

        $user = $request->user();

        $payment = \App\Models\Payment::where('id', $paymentId)
            ->where('user_id', $user->id)
            ->where('status', 'pending')
            ->whereIn('payment_method', ['bank_transfer', 'cash'])
            ->firstOrFail();

        // Store the proof
        $path = $request->file('proof')->store('payment-proofs', 'public');

        $payment->update([
            'proof_of_payment_path' => $path,
            'payment_notes' => $request->input('notes'),
        ]);

        // TODO: Notify association admins for approval

        return $this->success([
            'message' => 'Proof of payment uploaded successfully',
            'proof_url' => asset('storage/' . $path),
        ]);
    }

    /**
     * Get pending payments awaiting admin approval (Admin only)
     */
    public function getPendingPaymentsForApproval(Request $request)
    {
        $user = $request->user();
        $associationId = $user->last_association_context ?? $user->associations->first()?->id;

        // Check if user is admin
        // TODO: Add proper admin check via roles/permissions

        $payments = \App\Models\Payment::where('association_id', $associationId)
            ->where('status', 'pending')
            ->whereIn('payment_method', ['bank_transfer', 'cash'])
            ->whereNotNull('proof_of_payment_path')
            ->with(['user', 'payable'])
            ->orderBy('created_at', 'desc')
            ->paginate(20);

        return $this->success($payments);
    }

    /**
     * Approve payment (Admin only)
     */
    public function approvePayment(Request $request, $paymentId)
    {
        $request->validate([
            'admin_notes' => 'nullable|string|max:1000',
        ]);

        $user = $request->user();
        $associationId = $user->last_association_context ?? $user->associations->first()?->id;

        // Check if user is admin
        // TODO: Add proper admin check via roles/permissions

        $payment = \App\Models\Payment::where('id', $paymentId)
            ->where('association_id', $associationId)
            ->where('status', 'pending')
            ->firstOrFail();

        $payment->update([
            'status' => 'completed',
            'verified_by' => $user->id,
            'verified_at' => now(),
            'paid_at' => now(),
            'admin_notes' => $request->input('admin_notes'),
        ]);

        $metadata = $payment->metadata;
        $isAdvancePayment = isset($metadata['type']) && $metadata['type'] === 'advance_payment';

        if ($isAdvancePayment) {
            // Advance payment: create prepaid periods and consume matching pending assignments
            $item = FinancialItem::find($payment->payable_id);
            $periodsCreated = 0;

            if ($item && isset($metadata['periods'])) {
                $service = app(FinancialManagementService::class);
                $prepaidPeriods = $service->createPrepaidPeriods(
                    $payment->user,
                    $item,
                    $payment->id,
                    $metadata['periods'],
                    (float) $payment->amount
                );
                $periodsCreated = $prepaidPeriods->count();

                // Consume any existing pending/overdue assignments matching prepaid period billing periods.
                // This handles the case where a member already has a pending assignment for the current
                // month — it should be marked as paid immediately when admin approves the advance payment.
                foreach ($prepaidPeriods as $prepaid) {
                    $pendingAssignment = FinancialAssignment::where('user_id', $payment->user_id)
                        ->where('financial_item_id', $item->id)
                        ->where('association_id', $associationId)
                        ->whereIn('status', ['pending', 'overdue'])
                        ->where(function ($q) use ($prepaid) {
                            $q->where('billing_period', $prepaid->billing_period)
                              ->orWhereNull('billing_period');
                        })
                        ->first();

                    if ($pendingAssignment) {
                        $pendingAssignment->update([
                            'status' => 'paid',
                            'amount_paid' => $pendingAssignment->amount,
                            'billing_period' => $prepaid->billing_period,
                        ]);
                        $prepaid->markAsConsumed($pendingAssignment);
                    }
                }
            }

            return $this->success([
                'message' => 'Advance payment approved successfully',
                'payment' => $payment->fresh(['user', 'payable']),
                'periods_created' => $periodsCreated,
            ]);
        }

        // Regular payment: update financial assignment
        $assignment = $payment->payable;

        if ($assignment && $assignment instanceof \App\Models\FinancialAssignment) {
            $newAmountPaid = (float)$assignment->amount_paid + (float)$payment->amount;
            $totalAmount = (float)$assignment->amount;

            $assignment->update([
                'amount_paid' => $newAmountPaid,
                'status' => $newAmountPaid >= $totalAmount ? 'paid' : 'pending',
            ]);
        }

        return $this->success([
            'message' => 'Payment approved successfully',
            'payment' => $payment->fresh(['user', 'payable']),
        ]);
    }

    /**
     * Reject payment (Admin only)
     */
    public function rejectPayment(Request $request, $paymentId)
    {
        $request->validate([
            'reason' => 'required|string|max:500',
            'admin_notes' => 'nullable|string|max:1000',
        ]);

        $user = $request->user();
        $associationId = $user->last_association_context ?? $user->associations->first()?->id;

        // Check if user is admin
        // TODO: Add proper admin check via roles/permissions

        $payment = \App\Models\Payment::where('id', $paymentId)
            ->where('association_id', $associationId)
            ->where('status', 'pending')
            ->firstOrFail();

        $adminNotes = $request->input('admin_notes');
        if ($adminNotes) {
            $adminNotes .= "\n\nRejection reason: " . $request->input('reason');
        } else {
            $adminNotes = "Rejection reason: " . $request->input('reason');
        }

        $payment->update([
            'status' => 'failed',
            'verified_by' => $user->id,
            'verified_at' => now(),
            'failure_reason' => $request->input('reason'),
            'admin_notes' => $adminNotes,
        ]);

        // TODO: Notify member about rejection

        return $this->success([
            'message' => 'Payment rejected',
            'payment' => $payment->fresh(['user', 'payable']),
        ]);
    }

    // =============================================
    // Financial Obligations & Advance Payment Endpoints
    // =============================================

    /**
     * Get all financial obligations grouped by category and status.
     *
     * Response includes:
     * - categories: { dues: { overdue: [...], pending: [...], ... }, levy: { ... }, ... }
     * - by_status: { overdue: [...], due_soon: [...], pending: [...], paid: [...], waived: [...] }
     * - summary: totals + per-category breakdown
     * - settings: association-level financial policies
     *
     * Query params:
     * - ?category=dues  — filter to a single category
     */
    public function getAllDues(Request $request)
    {
        $user = $request->user();
        $associationId = $user->last_association_context ?? $user->associations->first()?->id;

        if (!$associationId) {
            return $this->error('No association context found', null, 404);
        }

        $categoryFilter = $request->input('category');

        $query = FinancialAssignment::where('user_id', $user->id)
            ->where('association_id', $associationId)
            ->obligations()  // Only admin-assigned obligations — excludes on-demand purchases
            ->with(['financialItem'])
            ->orderBy('due_date', 'asc');

        // Filter by category if provided
        if ($categoryFilter) {
            $query->whereHas('financialItem', function ($q) use ($categoryFilter) {
                $q->where('category', $categoryFilter)
                    ->orWhere('type', $categoryFilter);
            });
        }

        $assignments = $query->get();

        $now = now();

        // Group by status (flat — backward compatible)
        $byStatus = [
            'overdue' => $assignments->where('status', 'overdue')->values(),
            'due_soon' => $assignments->filter(fn ($a) =>
                $a->status === 'pending' &&
                $a->due_date &&
                $a->due_date->lte($now->copy()->addDays(7)) &&
                $a->due_date->gte($now)
            )->values(),
            'pending' => $assignments->filter(fn ($a) =>
                $a->status === 'pending' &&
                (!$a->due_date || $a->due_date->gt($now->copy()->addDays(7)))
            )->values(),
            'paid' => $assignments->where('status', 'paid')->sortByDesc('updated_at')->take(10)->values(),
            'waived' => $assignments->where('status', 'waived')->values(),
        ];

        // Group by category (new — structured)
        $byCategory = [];
        $categorized = $assignments->groupBy(fn ($a) => $a->financialItem->category ?? $a->financialItem->type);

        foreach ($categorized as $category => $catAssignments) {
            $byCategory[$category] = [
                'label' => FinancialItem::CATEGORIES[$category] ?? ucfirst($category),
                'overdue' => $catAssignments->where('status', 'overdue')->values(),
                'due_soon' => $catAssignments->filter(fn ($a) =>
                    $a->status === 'pending' &&
                    $a->due_date &&
                    $a->due_date->lte($now->copy()->addDays(7)) &&
                    $a->due_date->gte($now)
                )->values(),
                'pending' => $catAssignments->filter(fn ($a) =>
                    $a->status === 'pending' &&
                    (!$a->due_date || $a->due_date->gt($now->copy()->addDays(7)))
                )->values(),
                'paid' => $catAssignments->where('status', 'paid')->sortByDesc('updated_at')->take(5)->values(),
                'waived' => $catAssignments->where('status', 'waived')->values(),
                'summary' => [
                    'total_outstanding' => round(
                        $catAssignments->whereIn('status', ['pending', 'overdue'])
                            ->sum(fn ($a) => (float) $a->amount - (float) $a->amount_paid),
                        2
                    ),
                    'overdue_count' => $catAssignments->where('status', 'overdue')->count(),
                    'pending_count' => $catAssignments->where('status', 'pending')->count(),
                    'paid_count' => $catAssignments->where('status', 'paid')->count(),
                ],
            ];
        }

        $service = app(FinancialManagementService::class);
        $summary = $service->getMemberFinancialSummary($user, $associationId);

        $association = Association::find($associationId);

        return $this->success([
            // Backward compatible — flat status grouping
            'assignments' => $byStatus,
            // New — category grouping with per-category status breakdown
            'categories' => $byCategory,
            // Summary with category breakdown
            'summary' => $summary,
            // Association-level settings
            'settings' => [
                'financial_policies' => $association->getFinancialPolicySettings(),
                'payment_configuration' => $association->getPaymentConfiguration(),
            ],
        ], 'Financial obligations retrieved successfully');
    }

    /**
     * Get member's on-demand purchases (library content, store items, event tickets, etc.).
     *
     * These are member-initiated purchases that do NOT appear in the financial
     * obligations page. They have their own dedicated view.
     */
    public function getPurchases(Request $request)
    {
        $user = $request->user();
        $associationId = $user->last_association_context ?? $user->associations->first()?->id;

        if (!$associationId) {
            return $this->error('No association context found', null, 404);
        }

        $statusFilter = $request->input('status');

        $query = FinancialAssignment::where('user_id', $user->id)
            ->where('association_id', $associationId)
            ->onDemandPurchases()
            ->with(['financialItem'])
            ->orderByDesc('created_at');

        if ($statusFilter && in_array($statusFilter, ['pending', 'paid', 'overdue'])) {
            $query->where('status', $statusFilter);
        }

        $purchases = $query->get();

        // Resolve purchase context (what was purchased)
        $purchaseData = $purchases->map(function ($assignment) {
            $item = $assignment->financialItem;
            $relatedType = $item?->related_type;
            $relatedId = $item?->related_id;

            // Determine the purchase source (library, store, event, etc.)
            $source = 'unknown';
            $sourceLabel = 'Purchase';
            $sourceUrl = null;

            if ($relatedType === \App\Models\LibraryItem::class) {
                $source = 'library';
                $sourceLabel = 'Library Content';
                $libraryItem = \App\Models\LibraryItem::find($relatedId);
                if ($libraryItem) {
                    $sourceUrl = "/learning-center/{$relatedId}";
                }
            }
            // Future: Add more source types here (store, events, etc.)

            return [
                'id' => $assignment->id,
                'title' => $item?->title ?? 'Unknown Item',
                'description' => $item?->description,
                'amount' => $assignment->amount,
                'amount_paid' => $assignment->amount_paid,
                'status' => $assignment->status,
                'source' => $source,
                'source_label' => $sourceLabel,
                'source_url' => $sourceUrl,
                'related_id' => $relatedId,
                'created_at' => $assignment->created_at?->toIso8601String(),
                'due_date' => $assignment->due_date?->toDateString(),
            ];
        })->values();

        $summary = [
            'total_purchases' => $purchases->count(),
            'total_spent' => round((float) $purchases->where('status', 'paid')->sum('amount'), 2),
            'pending_count' => $purchases->where('status', 'pending')->count(),
            'pending_total' => round(
                (float) $purchases->where('status', 'pending')
                    ->sum(fn ($a) => (float) $a->amount - (float) $a->amount_paid),
                2
            ),
        ];

        return $this->success([
            'purchases' => $purchaseData,
            'summary' => $summary,
        ], 'Purchases retrieved successfully');
    }

    /**
     * Get advance payment options for a recurring financial item.
     * NOW: Uses item-level policy flag (canAdvancePay) + per-item max periods.
     */
    public function getAdvancePaymentOptions(Request $request, $itemId)
    {
        $user = $request->user();
        $associationId = $user->last_association_context ?? $user->associations->first()?->id;

        if (!$associationId) {
            return $this->error('No association context found', null, 404);
        }

        $association = Association::find($associationId);

        $item = FinancialItem::where('association_id', $associationId)
            ->where('is_active', true)
            ->find($itemId);

        if (!$item) {
            return $this->error('Financial item not found', null, 404);
        }

        // Policy check is now inside the service — it checks item-level + association-level
        $service = app(FinancialManagementService::class);
        $eligibility = $service->getAdvancePaymentEligibility($user, $item);

        if (!$eligibility['allowed']) {
            return $this->success([
                'allowed' => false,
                'message' => $eligibility['reason'] ?? 'Advance payment is not available.',
                'current_prepaid' => $eligibility['current_prepaid'],
                'max_periods' => $eligibility['max_periods'],
            ]);
        }

        // Calculate options for 1 to max available periods (capped at 12 for UI)
        $maxOptions = min($eligibility['available'], 12);
        $options = [];
        for ($i = 1; $i <= $maxOptions; $i++) {
            $options[] = $service->calculateAdvancePayment($item, $i, $association);
        }

        return $this->success([
            'allowed' => true,
            'item' => [
                'id' => $item->id,
                'title' => $item->title,
                'category' => $item->category,
                'type' => $item->type,
                'amount' => $item->amount,
                'frequency' => $item->frequency,
            ],
            'max_periods' => $eligibility['available'],
            'current_prepaid' => $eligibility['current_prepaid'],
            'discount_percentage' => (float) ($association->advance_payment_discount_percentage ?? 0),
            'options' => $options,
        ], 'Advance payment options retrieved');
    }

    /**
     * Initiate advance payment for multiple periods
     */
    public function initiateAdvancePayment(Request $request)
    {
        $validated = $request->validate([
            'financial_item_id' => 'required|integer|exists:financial_items,id',
            'periods' => 'required|integer|min:1|max:24',
            'payment_method' => 'required|in:paystack,bank_transfer',
            'callback_url' => 'nullable|url',
        ]);

        $user = $request->user();
        $associationId = $user->last_association_context ?? $user->associations->first()?->id;
        $association = Association::find($associationId);

        $item = FinancialItem::where('association_id', $associationId)
            ->where('is_active', true)
            ->find($validated['financial_item_id']);

        if (!$item) {
            return $this->error('Financial item not found', null, 404);
        }

        // Policy enforcement: item-level + association-level checks happen inside the service
        $service = app(FinancialManagementService::class);

        // Verify eligibility (includes item-level and association-level policy checks)
        $eligibility = $service->getAdvancePaymentEligibility($user, $item);
        if (!$eligibility['allowed'] || $validated['periods'] > $eligibility['available']) {
            return $this->error('Requested periods exceed available limit', null, 422);
        }

        $calculation = $service->calculateAdvancePayment($item, $validated['periods'], $association);
        $reference = 'ADV-' . strtoupper(Str::random(8)) . '-' . $item->id;

        // Create payment record
        $payment = Payment::create([
            'user_id' => $user->id,
            'association_id' => $associationId,
            'payable_type' => FinancialItem::class,
            'payable_id' => $item->id,
            'amount' => $calculation['final_amount'],
            'currency' => 'NGN',
            'gateway' => $validated['payment_method'] === 'paystack' ? 'paystack' : 'manual',
            'payment_method' => $validated['payment_method'],
            'reference' => $reference,
            'status' => 'pending',
            'metadata' => [
                'type' => 'advance_payment',
                'periods' => $validated['periods'],
                'base_amount' => $calculation['base_amount'],
                'discount' => $calculation['discount'],
                'discount_percentage' => $calculation['discount_percentage'],
                'period_details' => $calculation['period_details'],
            ],
        ]);

        // Paystack payment
        if ($validated['payment_method'] === 'paystack') {
            if (!$association->hasPaystackConfigured()) {
                return $this->error('Paystack not configured for this association', null, 422);
            }

            try {
                $paystackService = new AssociationPaystackService($association);
                $paystackData = $paystackService->initializeTransaction(
                    email: $user->email,
                    amountInKobo: (int) ($calculation['final_amount'] * 100),
                    reference: $reference,
                    metadata: [
                        'user_id' => $user->id,
                        'type' => 'advance_payment',
                        'financial_item_id' => $item->id,
                        'periods' => $validated['periods'],
                        'association_id' => $associationId,
                    ],
                    callbackUrl: $validated['callback_url'] ?? null,
                );

                $payment->update(['access_code' => $paystackData['access_code']]);

                return $this->success([
                    'payment_id' => $payment->id,
                    'payment_method' => 'paystack',
                    'reference' => $reference,
                    'authorization_url' => $paystackData['authorization_url'],
                    'access_code' => $paystackData['access_code'],
                    'calculation' => $calculation,
                ], 'Advance payment initiated');

            } catch (\Throwable $e) {
                \Log::error('Advance payment initiation failed', [
                    'error' => $e->getMessage(),
                    'user_id' => $user->id,
                    'item_id' => $item->id,
                ]);
                return $this->error('Failed to initiate payment: ' . $e->getMessage(), null, 500);
            }
        }

        // Bank transfer
        if ($validated['payment_method'] === 'bank_transfer') {
            if (!$association->hasBankTransferConfigured()) {
                return $this->error('Bank transfer not configured', null, 422);
            }

            return $this->success([
                'payment_id' => $payment->id,
                'payment_method' => 'bank_transfer',
                'reference' => $reference,
                'bank_details' => [
                    'bank_name' => $association->bank_name,
                    'account_number' => $association->account_number,
                    'account_name' => $association->account_name,
                ],
                'instructions' => $association->payment_instructions,
                'calculation' => $calculation,
                'note' => 'Please upload proof of payment after making the transfer.',
            ], 'Bank transfer details for advance payment');
        }

        return $this->error('Invalid payment method', null, 422);
    }

    /**
     * Verify advance payment and create prepaid periods
     */
    public function verifyAdvancePayment(Request $request, $reference)
    {
        $user = $request->user();
        $associationId = $user->last_association_context ?? $user->associations->first()?->id;
        $association = Association::find($associationId);

        $payment = Payment::where('reference', $reference)
            ->where('user_id', $user->id)
            ->first();

        if (!$payment) {
            return $this->error('Payment not found', null, 404);
        }

        // Already completed - return success with existing data
        if ($payment->status === 'completed') {
            return $this->success([
                'reference' => $reference,
                'status' => 'success',
                'amount' => $payment->amount,
                'paid_at' => $payment->paid_at,
            ], 'Payment already verified');
        }

        if ($payment->payment_method !== 'paystack') {
            return $this->error('Only Paystack payments can be verified via this endpoint', null, 422);
        }

        try {
            $paystackService = new AssociationPaystackService($association);
            $transactionData = $paystackService->verifyTransaction($reference);

            if ($transactionData['status'] !== 'success') {
                $payment->update([
                    'status' => 'failed',
                    'failure_reason' => $transactionData['gateway_response'] ?? 'Payment failed',
                ]);
                return $this->error('Payment verification failed', null, 422);
            }

            // Mark payment as completed
            $payment->update([
                'status' => 'completed',
                'paid_at' => now(),
                'authorization_code' => $transactionData['authorization']['authorization_code'] ?? null,
            ]);

            // Create prepaid periods from metadata
            $metadata = $payment->metadata;
            $periodsCreated = 0;
            if (isset($metadata['type']) && $metadata['type'] === 'advance_payment') {
                $item = FinancialItem::find($payment->payable_id);
                if ($item) {
                    $service = app(FinancialManagementService::class);
                    $prepaidPeriods = $service->createPrepaidPeriods(
                        $user,
                        $item,
                        $payment->id,
                        $metadata['periods'],
                        (float) $payment->amount
                    );
                    $periodsCreated = $prepaidPeriods->count();

                    // Consume any existing pending assignments that match prepaid period billing periods.
                    // This handles the case where a member pays ahead but already has a pending
                    // assignment for the current month — it should be marked as paid immediately.
                    foreach ($prepaidPeriods as $prepaid) {
                        $pendingAssignment = FinancialAssignment::where('user_id', $user->id)
                            ->where('financial_item_id', $item->id)
                            ->where('association_id', $associationId)
                            ->whereIn('status', ['pending', 'overdue'])
                            ->where(function ($q) use ($prepaid) {
                                $q->where('billing_period', $prepaid->billing_period)
                                  ->orWhereNull('billing_period');
                            })
                            ->first();

                        if ($pendingAssignment) {
                            $pendingAssignment->update([
                                'status' => 'paid',
                                'amount_paid' => $pendingAssignment->amount,
                                'billing_period' => $prepaid->billing_period,
                            ]);
                            $prepaid->markAsConsumed($pendingAssignment);
                        }
                    }
                }
            }

            return $this->success([
                'reference' => $reference,
                'status' => 'success',
                'amount' => $payment->amount,
                'paid_at' => $payment->paid_at,
                'periods_created' => $periodsCreated,
            ], 'Advance payment verified and prepaid periods created');

        } catch (\Throwable $e) {
            \Log::error('Advance payment verification failed', [
                'reference' => $reference,
                'error' => $e->getMessage(),
            ]);
            return $this->error('Verification failed: ' . $e->getMessage(), null, 500);
        }
    }

    /**
     * Initiate payment for multiple assignments at once
     */
    public function initiateMultiplePayment(Request $request)
    {
        $validated = $request->validate([
            'assignment_ids' => 'required|array|min:1|max:20',
            'assignment_ids.*' => 'integer|exists:financial_assignments,id',
            'payment_method' => 'required|in:paystack,bank_transfer',
            'callback_url' => 'nullable|url',
        ]);

        $user = $request->user();
        $associationId = $user->last_association_context ?? $user->associations->first()?->id;
        $association = Association::find($associationId);

        // Fetch and validate all assignments belong to user and are payable
        $assignments = FinancialAssignment::whereIn('id', $validated['assignment_ids'])
            ->where('user_id', $user->id)
            ->where('association_id', $associationId)
            ->whereIn('status', ['pending', 'overdue'])
            ->with('financialItem')
            ->get();

        if ($assignments->isEmpty()) {
            return $this->error('No valid payable assignments found', null, 404);
        }

        // Calculate total outstanding balance
        $totalAmount = $assignments->sum(fn ($a) => (float) $a->amount - (float) $a->amount_paid);

        if ($totalAmount <= 0) {
            return $this->error('No outstanding balance to pay', null, 422);
        }

        $reference = 'MULTI-' . strtoupper(Str::random(8)) . '-' . $assignments->count();

        // Create a single payment record for the batch
        $payment = Payment::create([
            'user_id' => $user->id,
            'association_id' => $associationId,
            'payable_type' => FinancialAssignment::class,
            'payable_id' => $assignments->first()->id, // Primary reference
            'amount' => $totalAmount,
            'currency' => 'NGN',
            'gateway' => $validated['payment_method'] === 'paystack' ? 'paystack' : 'manual',
            'payment_method' => $validated['payment_method'],
            'reference' => $reference,
            'status' => 'pending',
            'metadata' => [
                'type' => 'multiple_payment',
                'assignment_ids' => $assignments->pluck('id')->toArray(),
                'assignment_amounts' => $assignments->mapWithKeys(fn ($a) => [
                    $a->id => (float) $a->amount - (float) $a->amount_paid
                ])->toArray(),
            ],
        ]);

        // Paystack
        if ($validated['payment_method'] === 'paystack') {
            if (!$association->hasPaystackConfigured()) {
                return $this->error('Paystack not configured', null, 422);
            }

            try {
                $titles = $assignments->map(fn ($a) => $a->financialItem->title)->join(', ');

                $paystackService = new AssociationPaystackService($association);
                $paystackData = $paystackService->initializeTransaction(
                    email: $user->email,
                    amountInKobo: (int) ($totalAmount * 100),
                    reference: $reference,
                    metadata: [
                        'user_id' => $user->id,
                        'type' => 'multiple_payment',
                        'assignment_ids' => $assignments->pluck('id')->toArray(),
                        'association_id' => $associationId,
                        'description' => "Payment for: {$titles}",
                    ],
                    callbackUrl: $validated['callback_url'] ?? null,
                );

                $payment->update(['access_code' => $paystackData['access_code']]);

                return $this->success([
                    'payment_id' => $payment->id,
                    'payment_method' => 'paystack',
                    'reference' => $reference,
                    'authorization_url' => $paystackData['authorization_url'],
                    'access_code' => $paystackData['access_code'],
                    'total_amount' => $totalAmount,
                    'assignment_count' => $assignments->count(),
                ], 'Multiple payment initiated');

            } catch (\Throwable $e) {
                \Log::error('Multiple payment initiation failed', [
                    'error' => $e->getMessage(),
                    'user_id' => $user->id,
                ]);
                return $this->error('Failed to initiate payment: ' . $e->getMessage(), null, 500);
            }
        }

        // Bank transfer
        if ($validated['payment_method'] === 'bank_transfer') {
            if (!$association->hasBankTransferConfigured()) {
                return $this->error('Bank transfer not configured', null, 422);
            }

            return $this->success([
                'payment_id' => $payment->id,
                'payment_method' => 'bank_transfer',
                'reference' => $reference,
                'total_amount' => $totalAmount,
                'assignment_count' => $assignments->count(),
                'bank_details' => [
                    'bank_name' => $association->bank_name,
                    'account_number' => $association->account_number,
                    'account_name' => $association->account_name,
                ],
                'instructions' => $association->payment_instructions,
            ], 'Bank transfer details for multiple payment');
        }

        return $this->error('Invalid payment method', null, 422);
    }

    /**
     * Get prepaid periods for current user
     */
    public function getPrepaidPeriods(Request $request)
    {
        $user = $request->user();
        $associationId = $user->last_association_context ?? $user->associations->first()?->id;

        if (!$associationId) {
            return $this->error('No association context found', null, 404);
        }

        $prepaidPeriods = PrepaidPeriod::where('user_id', $user->id)
            ->where('association_id', $associationId)
            ->with(['financialItem:id,title,type,frequency,amount'])
            ->orderBy('period_start_date', 'asc')
            ->get();

        return $this->success([
            'active' => $prepaidPeriods->where('status', 'active')->values(),
            'consumed' => $prepaidPeriods->where('status', 'consumed')->sortByDesc('consumed_at')->take(10)->values(),
        ], 'Prepaid periods retrieved');
    }
}
