<?php

namespace App\Http\Controllers\Api;

use App\Exceptions\PaystackException;
use App\Models\FinancialAssignment;
use App\Models\FinancialItem;
use App\Models\Payment;
use App\Models\Project;
use App\Models\ProjectContribution;
use App\Services\AssociationPaystackService;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Str;

class ProjectController extends BaseApiController
{
    /**
     * List projects visible to the current member.
     *
     * Supports: category, status, search, sort (newest/popular/ending_soon), per_page.
     * Contribution summaries are batch-loaded to avoid N+1.
     */
    public function index(Request $request): JsonResponse
    {
        $user = Auth::user();
        $association = $user->associations()->first();

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

        $perPage = max(1, min((int) $request->input('per_page', 12), 100));

        $query = Project::where('association_id', $association->id)
            ->visible()
            ->with(['creator:id,firstname,surname']);

        // ── Filters ──
        if ($request->filled('category')) {
            $query->byCategory($request->input('category'));
        }

        if ($request->filled('status')) {
            $query->where('status', $request->input('status'));
        }

        if ($request->filled('search')) {
            $query->search($request->input('search'));
        }

        if ($request->boolean('featured')) {
            $query->featured();
        }

        // ── Sorting ──
        $sort = $request->input('sort', 'newest');
        switch ($sort) {
            case 'popular':
                $query->withCount(['contributions as completed_contributions_count' => function ($q) {
                    $q->where('status', 'completed');
                }])->orderByDesc('completed_contributions_count');
                break;
            case 'ending_soon':
                $query->whereNotNull('end_date')
                    ->where('end_date', '>=', now())
                    ->orderBy('end_date');
                break;
            case 'newest':
            default:
                $query->orderByDesc('created_at');
                break;
        }

        $paginated = $query->paginate($perPage);
        $projects = $paginated->items();

        // ── Batch-load contribution summaries ──
        $projectIds = collect($projects)->pluck('id')->toArray();

        $contributionSummaries = ProjectContribution::whereIn('project_id', $projectIds)
            ->where('status', 'completed')
            ->selectRaw('project_id, SUM(amount) as total_raised, COUNT(DISTINCT user_id) as contributor_count')
            ->groupBy('project_id')
            ->get()
            ->keyBy('project_id');

        $transformed = collect($projects)->map(function ($project) use ($contributionSummaries) {
            $summary = $contributionSummaries->get($project->id);
            $totalRaised = $summary ? round((float) $summary->total_raised, 2) : 0;
            $contributorCount = $summary ? (int) $summary->contributor_count : 0;
            $targetAmount = (float) $project->target_amount;
            $progressPercentage = $targetAmount > 0
                ? round(min(($totalRaised / $targetAmount) * 100, 100), 1)
                : 0;

            return [
                'id' => $project->id,
                'uuid' => $project->uuid,
                'title' => $project->title,
                'slug' => $project->slug,
                'description' => $project->description,
                'category' => $project->category,
                'category_label' => Project::CATEGORIES[$project->category] ?? ucfirst($project->category),
                'status' => $project->status,
                'status_label' => Project::STATUSES[$project->status] ?? ucfirst($project->status),
                'target_amount' => $targetAmount,
                'total_raised' => $totalRaised,
                'contributor_count' => $contributorCount,
                'progress_percentage' => $progressPercentage,
                'cover_image_url' => $project->cover_image_url,
                'is_featured' => $project->is_featured,
                'start_date' => $project->start_date?->toDateString(),
                'end_date' => $project->end_date?->toDateString(),
                'created_at' => $project->created_at?->toIso8601String(),
                'creator' => $project->creator ? [
                    'id' => $project->creator->id,
                    'name' => trim($project->creator->firstname . ' ' . $project->creator->surname),
                ] : null,
            ];
        })->toArray();

        return response()->json([
            'success' => true,
            'data' => $transformed,
            'pagination' => [
                'current_page' => $paginated->currentPage(),
                'last_page' => $paginated->lastPage(),
                'per_page' => $paginated->perPage(),
                'total' => $paginated->total(),
                'from' => $paginated->firstItem(),
                'to' => $paginated->lastItem(),
            ],
            'meta' => [
                'timestamp' => now()->toIso8601String(),
                'request_id' => request()->header('X-Request-ID', uniqid()),
                'categories' => Project::CATEGORIES,
            ],
        ]);
    }

    /**
     * Show project detail with updates, committee, top contributors,
     * linked financial items, and the user's own contributions.
     */
    public function show(Request $request, $id): JsonResponse
    {
        $user = Auth::user();
        $association = $user->associations()->first();

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

        $project = Project::where('association_id', $association->id)
            ->where('id', $id)
            ->with([
                'creator:id,firstname,surname',
                'updates' => function ($q) {
                    $q->with('author:id,firstname,surname')->orderByDesc('is_pinned')->orderByDesc('created_at')->limit(20);
                },
                'committee' => function ($q) {
                    $q->with(['activeMembers' => function ($q2) {
                        $q2->with('user:id,firstname,surname')->orderByRaw("FIELD(role, 'chairperson', 'vice_chairperson', 'secretary', 'treasurer', 'member')");
                    }]);
                },
            ])
            ->first();

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

        // ── Contribution stats (batch) ──
        $totalRaised = round((float) $project->contributions()->where('status', 'completed')->sum('amount'), 2);
        $contributorCount = $project->contributions()->where('status', 'completed')->distinct('user_id')->count('user_id');
        $targetAmount = (float) $project->target_amount;
        $progressPercentage = $targetAmount > 0
            ? round(min(($totalRaised / $targetAmount) * 100, 100), 1)
            : 0;

        // ── Top contributors ──
        $topContributors = ProjectContribution::where('project_id', $project->id)
            ->where('status', 'completed')
            ->selectRaw('user_id, SUM(amount) as total_contributed')
            ->groupBy('user_id')
            ->orderByDesc('total_contributed')
            ->limit(10)
            ->get();

        $topContributorUserIds = $topContributors->pluck('user_id')->toArray();
        $topContributorUsers = \App\Models\User::whereIn('id', $topContributorUserIds)
            ->get(['id', 'firstname', 'surname', 'profile_photo_path'])
            ->keyBy('id');

        $topContributorsData = $topContributors->map(function ($row) use ($topContributorUsers) {
            $u = $topContributorUsers->get($row->user_id);
            return [
                'user_id' => $row->user_id,
                'name' => $u ? trim($u->firstname . ' ' . $u->surname) : 'Unknown',
                'avatar' => $u?->profile_photo_path,
                'total_contributed' => round((float) $row->total_contributed, 2),
            ];
        })->toArray();

        // ── User's own contributions ──
        $myContributions = ProjectContribution::where('project_id', $project->id)
            ->where('user_id', $user->id)
            ->orderByDesc('created_at')
            ->get()
            ->map(fn($c) => [
                'id' => $c->id,
                'amount' => (float) $c->amount,
                'status' => $c->status,
                'contributed_at' => $c->contributed_at?->toIso8601String(),
                'created_at' => $c->created_at?->toIso8601String(),
            ]);

        // ── Linked financial items ──
        $linkedFinancialItems = FinancialItem::where('related_type', Project::class)
            ->where('related_id', $project->id)
            ->where('is_active', true)
            ->get()
            ->map(fn($fi) => [
                'id' => $fi->id,
                'title' => $fi->title,
                'category' => $fi->category,
                'amount' => (float) $fi->amount,
                'is_optional' => $fi->is_optional,
                'requires_manual_assignment' => $fi->requires_manual_assignment,
            ]);

        // ── Build response ──
        $data = [
            'id' => $project->id,
            'uuid' => $project->uuid,
            'title' => $project->title,
            'slug' => $project->slug,
            'description' => $project->description,
            'category' => $project->category,
            'category_label' => Project::CATEGORIES[$project->category] ?? ucfirst($project->category),
            'status' => $project->status,
            'status_label' => Project::STATUSES[$project->status] ?? ucfirst($project->status),
            'target_amount' => $targetAmount,
            'total_raised' => $totalRaised,
            'contributor_count' => $contributorCount,
            'progress_percentage' => $progressPercentage,
            'cover_image_url' => $project->cover_image_url,
            'is_featured' => $project->is_featured,
            'visibility' => $project->visibility,
            'start_date' => $project->start_date?->toDateString(),
            'end_date' => $project->end_date?->toDateString(),
            'created_at' => $project->created_at?->toIso8601String(),
            'creator' => $project->creator ? [
                'id' => $project->creator->id,
                'name' => trim($project->creator->firstname . ' ' . $project->creator->surname),
            ] : null,
            'updates' => $project->updates->map(fn($u) => [
                'id' => $u->id,
                'title' => $u->title,
                'body' => $u->body,
                'image_url' => $u->image_url,
                'is_pinned' => $u->is_pinned,
                'created_at' => $u->created_at?->toIso8601String(),
                'author' => $u->author ? [
                    'id' => $u->author->id,
                    'name' => trim($u->author->firstname . ' ' . $u->author->surname),
                ] : null,
            ]),
            'committee' => $project->committee ? [
                'id' => $project->committee->id,
                'name' => $project->committee->name,
                'mandate' => $project->committee->mandate,
                'status' => $project->committee->status,
                'members' => $project->committee->activeMembers->map(fn($m) => [
                    'id' => $m->id,
                    'user_id' => $m->user_id,
                    'name' => $m->user ? trim($m->user->firstname . ' ' . $m->user->surname) : 'Unknown',
                    'role' => $m->role,
                    'role_label' => $m->role_label,
                ]),
            ] : null,
            'top_contributors' => $topContributorsData,
            'my_contributions' => $myContributions,
            'linked_financial_items' => $linkedFinancialItems,
            'budget' => [
                'allocated' => (float) ($project->budget_allocated ?? 0),
                'spent' => (float) ($project->budget_spent ?? 0),
                'expense_total' => $project->expense_total,
            ],
        ];

        return $this->success($data, 'Project details retrieved');
    }

    /**
     * Voluntary donation contribution to a project.
     *
     * Creates a FinancialAssignment (purchase_type=on_demand) + Payment.
     * Supports Paystack and bank_transfer methods.
     */
    public function contribute(Request $request, $id): JsonResponse
    {
        $validated = $request->validate([
            'amount' => 'required|numeric|min:100',
            'payment_method' => 'required|in:paystack,bank_transfer',
            'callback_url' => 'nullable|url',
        ]);

        $user = Auth::user();
        $association = $user->associations()->first();

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

        $project = Project::where('association_id', $association->id)
            ->where('id', $id)
            ->active()
            ->first();

        if (!$project) {
            return $this->error('Project not found or not accepting contributions', null, 404);
        }

        $amount = (float) $validated['amount'];

        // Find a voluntary donation FinancialItem linked to this project
        $financialItem = FinancialItem::where('related_type', Project::class)
            ->where('related_id', $project->id)
            ->where('is_active', true)
            ->where('is_optional', true)
            ->first();

        if (!$financialItem) {
            return $this->error('Voluntary contributions are not configured for this project. Please contact your association admin.', null, 422);
        }

        // ── Fail-fast: check payment method availability BEFORE creating any DB records ──
        if ($validated['payment_method'] === 'paystack' && !$association->hasPaystackConfigured()) {
            return $this->error('Paystack payment not configured for this association. Please use bank transfer or contact admin.', null, 422);
        }

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

        // ── Handle existing pending assignment ──
        // If a previous attempt left an orphaned pending assignment (e.g. payment method failed),
        // reuse it instead of blocking the user.
        $existingPending = FinancialAssignment::where('user_id', $user->id)
            ->where('association_id', $association->id)
            ->where('status', 'pending')
            ->where('purchase_type', FinancialAssignment::PURCHASE_TYPE_ON_DEMAND)
            ->whereHas('financialItem', function ($q) use ($project) {
                $q->where('related_type', Project::class)
                  ->where('related_id', $project->id);
            })
            ->first();

        if ($existingPending) {
            // Check if it already has a payment record — if so, that's a genuine pending payment
            $hasPayment = Payment::where('payable_type', FinancialAssignment::class)
                ->where('payable_id', $existingPending->id)
                ->where('status', 'pending')
                ->exists();

            if ($hasPayment) {
                return $this->error('You already have a pending contribution for this project. Please complete or cancel the existing payment.', null, 422);
            }

            // No payment attached — this is an orphaned assignment from a failed attempt.
            // Update the amount and reuse it.
            $existingPending->update(['amount' => $amount]);
            $assignment = $existingPending;
        } else {
            // Create FinancialAssignment — on_demand so it does NOT appear as obligation
            $assignment = FinancialAssignment::create([
                'financial_item_id' => $financialItem->id,
                'user_id' => $user->id,
                'association_id' => $association->id,
                'status' => 'pending',
                'purchase_type' => FinancialAssignment::PURCHASE_TYPE_ON_DEMAND,
                'amount' => $amount,
                'amount_paid' => 0,
                'due_date' => now()->addDays(7)->toDateString(),
            ]);
        }

        // ── PAYSTACK ──
        if ($validated['payment_method'] === 'paystack') {
            try {
                $reference = 'PRJ-' . strtoupper(uniqid()) . '-' . $assignment->id;

                $paystackService = new AssociationPaystackService($association);
                $paystackData = $paystackService->initializeTransaction(
                    email: $user->email,
                    amountInKobo: (int) ($amount * 100),
                    reference: $reference,
                    metadata: [
                        'user_id' => $user->id,
                        'financial_assignment_id' => $assignment->id,
                        'project_id' => $project->id,
                        'project_title' => $project->title ?? 'Project Contribution',
                        'type' => 'project_contribution',
                    ],
                    callbackUrl: $validated['callback_url'] ?? null,
                );

                $payment = Payment::create([
                    'uuid' => Str::uuid(),
                    'user_id' => $user->id,
                    'association_id' => $association->id,
                    'payable_type' => FinancialAssignment::class,
                    'payable_id' => $assignment->id,
                    'amount' => $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'],
                ], 'Contribution payment initiated successfully');

            } catch (PaystackException $e) {
                Log::error('Project contribution initiation failed', [
                    'association_id' => $association->id,
                    'project_id' => $project->id,
                    'assignment_id' => $assignment->id,
                    'error' => $e->getMessage(),
                ]);

                // Clean up the orphaned assignment so user can retry
                if (!$existingPending) {
                    $assignment->delete();
                }

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

        // ── BANK TRANSFER ──
        if ($validated['payment_method'] === 'bank_transfer') {
            $reference = 'BT-PRJ-' . strtoupper(uniqid()) . '-' . $assignment->id;

            $payment = Payment::create([
                'uuid' => Str::uuid(),
                'user_id' => $user->id,
                'association_id' => $association->id,
                'payable_type' => FinancialAssignment::class,
                'payable_id' => $assignment->id,
                'amount' => $amount,
                'gateway' => 'manual',
                'payment_method' => 'bank_transfer',
                'reference' => $reference,
                'status' => 'pending',
            ]);

            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');
        }

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

    /**
     * Verify a Paystack contribution and record it on success.
     */
    public function verifyContribution(Request $request, $id, $reference): JsonResponse
    {
        $user = Auth::user();
        $association = $user->associations()->first();

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

        $project = Project::where('association_id', $association->id)
            ->where('id', $id)
            ->first();

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

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

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

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

        // Idempotent: if already verified, return success
        if ($payment->status === 'completed') {
            return $this->success([
                'project_id' => $project->id,
                'title' => $project->title,
                'contribution_recorded' => true,
            ], 'Payment already verified — contribution recorded');
        }

        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);
            }

            DB::transaction(function () use ($payment, $user, $project) {
                // Update payment
                $payment->update([
                    'status' => 'completed',
                    'paid_at' => now(),
                ]);

                // Update financial assignment with decimal-safe arithmetic
                $assignment = $payment->payable;
                if ($assignment && $assignment instanceof FinancialAssignment) {
                    $newAmountPaid = bcadd((string) $assignment->amount_paid, (string) $payment->amount, 2);
                    $totalAmount = (string) $assignment->amount;

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

                    // Create ProjectContribution record
                    ProjectContribution::firstOrCreate(
                        [
                            'project_id' => $project->id,
                            'user_id' => $user->id,
                            'financial_assignment_id' => $assignment->id,
                        ],
                        [
                            'amount' => $payment->amount,
                            'status' => 'completed',
                            'contributed_at' => now(),
                        ]
                    );
                } else {
                    Log::warning('Project contribution verification: assignment not found or wrong type', [
                        'payment_id' => $payment->id,
                        'payable_type' => $payment->payable_type,
                        'payable_id' => $payment->payable_id,
                    ]);
                }
            });

            return $this->success([
                'project_id' => $project->id,
                'title' => $project->title,
                'contribution_recorded' => true,
                'amount' => (float) $payment->amount,
            ], 'Payment verified — contribution recorded');

        } catch (PaystackException $e) {
            Log::error('Project contribution verification failed', [
                'association_id' => $association->id,
                'project_id' => $project->id,
                'reference' => $reference,
                'error' => $e->getMessage(),
            ]);

            return $this->error('Payment verification failed: ' . $e->getMessage(), null, 500);
        }
    }

    /**
     * Current user's contribution history for a specific project.
     */
    public function myContributions(Request $request, $id): JsonResponse
    {
        $user = Auth::user();
        $association = $user->associations()->first();

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

        $project = Project::where('association_id', $association->id)
            ->where('id', $id)
            ->first();

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

        $contributions = ProjectContribution::where('project_id', $project->id)
            ->where('user_id', $user->id)
            ->orderByDesc('created_at')
            ->get()
            ->map(fn($c) => [
                'id' => $c->id,
                'amount' => (float) $c->amount,
                'status' => $c->status,
                'contributed_at' => $c->contributed_at?->toIso8601String(),
                'created_at' => $c->created_at?->toIso8601String(),
            ]);

        $totalContributed = $contributions->where('status', 'completed')->sum('amount');

        return $this->success([
            'project_id' => $project->id,
            'project_title' => $project->title,
            'contributions' => $contributions,
            'total_contributed' => round((float) $totalContributed, 2),
        ], 'Your contributions retrieved');
    }
}
