<?php

namespace App\Http\Controllers\Api;

use App\Events\MissionTrackingUpdated;
use App\Http\Controllers\Controller;
use App\Jobs\ProcessMissionSettlementPayment;
use App\Models\Mission;
use App\Models\MissionApplication;
use App\Models\MissionMessage;
use App\Models\Payment;
use App\Models\MissionProof;
use App\Models\MissionRating;
use App\Models\MissionStatusEvent;
use App\Models\UserPaymentMethod;
use App\Services\InAppNotificationService;
use App\Services\Payments\GatewayResult;
use App\Services\Payments\PaymentGatewayManager;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Storage;
use Illuminate\Validation\ValidationException;
use Symfony\Component\HttpFoundation\StreamedResponse;

class MissionController extends Controller
{
public function index(Request $request): JsonResponse
{
    $user = $request->user();

    $query = Mission::query()->with([
        'client:id,name,phone',
        'liner:id,name,phone',
    ]);

    if ($user->role === 'client') {
        $query->with([
            'applications' => fn ($q) => $q
                ->with('liner:id,name,phone,email,is_available')
                ->select([
                    'id',
                    'mission_id',
                    'liner_id',
                    'status',
                    'accepted_at',
                    'rejected_at',
                    'created_at',
                    'updated_at',
                ]),
        ]);

        $query->where('client_id', $user->id);
    } elseif ($user->role === 'liner') {
        $query->with([
            'applications' => fn ($q) => $q
                ->where('liner_id', $user->id)
                ->select([
                    'id',
                    'mission_id',
                    'liner_id',
                    'status',
                    'accepted_at',
                    'rejected_at',
                    'created_at',
                    'updated_at',
                ]),
        ]);

        $query->where(function ($q) use ($user): void {
            $q
                // Missions définitivement validées pour ce liner
                ->where(function ($q2) use ($user): void {
                    $q2->where('liner_id', $user->id)
                        ->whereNotNull('client_validated_at');
                })

                // Missions publiées encore ouvertes à tous
                ->orWhere(function ($q2): void {
                    $q2->where('status', 'published')
                        ->whereNull('liner_id')
                        ->whereNull('pending_liner_id');
                })

                // Missions publiées sur lesquelles ce liner a déjà candidaté
                ->orWhere(function ($q2) use ($user): void {
                    $q2->where('status', 'published')
                        ->whereNull('liner_id')
                        ->whereHas(
                            'applications',
                            fn ($q3) => $q3->where('liner_id', $user->id),
                        );
                })

                // Missions préselectionnées pour paiement pour ce liner
                ->orWhere(function ($q2) use ($user): void {
                    $q2->where('status', 'published')
                        ->whereNull('liner_id')
                        ->where('pending_liner_id', $user->id);
                });
        });
    }

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

    $missions = $query->latest()->paginate(20);

    if ($user->role === 'liner') {
        $missions->getCollection()->transform(function (Mission $mission) use ($user): Mission {
            $application = $mission->applications->first();

            $applicationStatus = 'none';

            $isFullyAccepted =
                (int) ($mission->liner_id ?? 0) === (int) $user->id
                && $mission->client_validated_at !== null;

            $isSelectedForPayment =
                (int) ($mission->pending_liner_id ?? 0) === (int) $user->id
                && $mission->client_validated_at === null
                && (int) ($mission->liner_id ?? 0) !== (int) $user->id;

            if ($isFullyAccepted) {
                $applicationStatus = 'accepted';
            } elseif ($isSelectedForPayment) {
                $applicationStatus = 'selected_for_payment';
            } elseif ($application) {
                $applicationStatus = match ((string) $application->status) {
                    'pending' => 'waiting',
                    'selected_for_payment' => 'selected_for_payment',
                    'accepted' => $mission->client_validated_at !== null
                        ? 'accepted'
                        : 'selected_for_payment',
                    'rejected' => 'rejected',
                    'withdrawn' => 'withdrawn',
                    default => (string) $application->status,
                };
            }

            $mission->setAttribute('application_status', $applicationStatus);
            $mission->unsetRelation('applications');

            return $mission;
        });
    }

    return response()->json([
        'data' => $missions,
    ]);
}

    public function store(Request $request, InAppNotificationService $notificationService): JsonResponse
    {
        $user = $request->user();
        abort_unless($user->role === 'client', 403);

        $data = $request->validate([
            'title' => ['required', 'string', 'max:255'],
            'description' => ['nullable', 'string'],
            'event_name' => ['nullable', 'string', 'max:255'],
            'location' => ['required', 'string', 'max:255'],
            'expected_start_at' => ['required', 'date'],
            'budget_amount' => ['required', 'numeric', 'min:1'],
            'currency' => ['nullable', 'string', 'size:3'],
        ]);

        $mission = Mission::create([
            ...$data,
            'client_id' => $user->id,
            'status' => 'published',
            'currency' => strtoupper($data['currency'] ?? 'EUR'),
        ]);

        $notificationService->notifyAllLiners(
            'mission.match',
            'Nouvelle mission disponible',
            sprintf('"%s" est disponible a %s.', $mission->title, $mission->location),
            [
                'mission_id' => $mission->id,
                'status' => $mission->status,
            ],
        );

        return response()->json(['data' => $mission], 201);
    }

    public function update(
        Request $request,
        Mission $mission,
        InAppNotificationService $notificationService,
    ): JsonResponse
    {
        $user = $request->user();
        abort_unless($user->id === $mission->client_id, 403);

        if (in_array($mission->status, ['completed', 'cancelled'], true)) {
            return response()->json(['message' => 'La mission ne peut pas etre modifiee'], 422);
        }

        $data = $request->validate([
            'title' => ['sometimes', 'string', 'max:255'],
            'description' => ['sometimes', 'nullable', 'string'],
            'event_name' => ['sometimes', 'nullable', 'string', 'max:255'],
            'location' => ['sometimes', 'string', 'max:255'],
            'expected_start_at' => ['sometimes', 'date'],
            'budget_amount' => ['sometimes', 'numeric', 'min:1'],
        ]);

        $mission->update($data);

        if ($mission->liner_id !== null) {
            $notificationService->notifyUser(
                $mission->liner_id,
                'mission.updated',
                'Mission modifiee',
                sprintf('Le client a modifie la mission "%s".', $mission->title),
                [
                    'mission_id' => $mission->id,
                    'status' => $mission->status,
                ],
            );
        }

        return response()->json(['data' => $mission->fresh()]);
    }

    public function destroy(
        Request $request,
        Mission $mission,
        InAppNotificationService $notificationService,
    ): JsonResponse
    {
        $user = $request->user();
        abort_unless($user->id === $mission->client_id, 403);

        if ($mission->status === 'completed') {
            return response()->json(['message' => 'Une mission terminee ne peut pas etre annulee'], 422);
        }

        $pendingApplicationLinerIds = MissionApplication::query()
            ->where('mission_id', $mission->id)
            ->where('status', 'pending')
            ->pluck('liner_id')
            ->unique()
            ->values()
            ->all();

        $mission->update([
            'status' => 'cancelled',
            'cancelled_at' => now(),
        ]);

        MissionApplication::query()
            ->where('mission_id', $mission->id)
            ->where('status', 'pending')
            ->update([
                'status' => 'rejected',
                'accepted_at' => null,
                'rejected_at' => now(),
            ]);

        if ($mission->liner_id !== null) {
            $notificationService->notifyUser(
                $mission->liner_id,
                'mission.cancelled',
                'Mission annulee',
                sprintf('La mission "%s" a ete annulee par le client.', $mission->title),
                [
                    'mission_id' => $mission->id,
                    'status' => 'cancelled',
                ],
            );
        }

        foreach ($pendingApplicationLinerIds as $linerId) {
            if ((int) $linerId === (int) $mission->liner_id) {
                continue;
            }

            $notificationService->notifyUser(
                (int) $linerId,
                'mission.cancelled',
                'Mission annulee',
                sprintf('La mission "%s" a ete annulee par le client.', $mission->title),
                [
                    'mission_id' => $mission->id,
                    'status' => 'cancelled',
                ],
            );
        }

        return response()->json([], 204);
    }

    public function accept(
        Request $request,
        Mission $mission,
        InAppNotificationService $notificationService,
    ): JsonResponse
    {
        $user = $request->user();
        abort_unless($user->role === 'liner', 403);

        if (!$user->is_kyc_verified) {
            return response()->json([
                'message' => 'KYC requis: validez votre CNI, passeport ou titre de sejour.',
            ], 422);
        }

        if (!$user->is_available) {
            return response()->json([
                'message' => 'Passez en disponible pour postuler a une mission.',
            ], 422);
        }

        if ($mission->status !== 'published' || $mission->liner_id !== null) {
            return response()->json(['message' => 'Mission indisponible'], 422);
        }

        $existingApplication = MissionApplication::query()
            ->where('mission_id', $mission->id)
            ->where('liner_id', $user->id)
            ->first();

        if ($existingApplication && $existingApplication->status === 'pending') {
            $missionData = $mission->fresh(['client:id,name,phone', 'liner:id,name,phone']);
            $missionData->setAttribute('application_status', 'waiting');

            return response()->json([
                'data' => $missionData,
                'message' => 'Candidature deja envoyee.',
            ]);
        }

        $application = MissionApplication::query()->updateOrCreate(
            [
                'mission_id' => $mission->id,
                'liner_id' => $user->id,
            ],
            [
                'status' => 'pending',
                'accepted_at' => null,
                'rejected_at' => null,
            ],
        );

        $this->recordStatusEvent(
            $mission,
            $user->id,
            'application_submitted',
            null,
            null,
            'Candidature envoyee',
        );

        $notificationService->notifyUser(
            $mission->client_id,
            'mission.application_submitted',
            'Nouvelle candidature',
            sprintf('%s a postule a votre mission "%s".', $user->name, $mission->title),
            [
                'mission_id' => $mission->id,
                'status' => $mission->status,
                'liner_id' => $user->id,
                'application_id' => $application->id,
            ],
        );

        $missionData = $mission->fresh(['client:id,name,phone', 'liner:id,name,phone']);
        $missionData->setAttribute('application_status', 'waiting');

        return response()->json(['data' => $missionData]);
    }

    public function applications(Request $request, Mission $mission): JsonResponse
    {
        $user = $request->user();
        abort_unless(
            ($user->role === 'client' && $mission->client_id === $user->id) || $user->role === 'admin',
            403,
        );

        $applications = $mission->applications()
            ->with('liner:id,name,phone,email,is_available')
            ->orderByRaw("CASE WHEN status = 'pending' THEN 0 WHEN status = 'accepted' THEN 1 ELSE 2 END")
            ->latest()
            ->get();

        return response()->json(['data' => $applications]);
    }

public function chooseLiner(
    Request $request,
    Mission $mission,
    InAppNotificationService $notificationService,
): JsonResponse {
    $user = $request->user();
    abort_unless($user->role === 'client' && $mission->client_id === $user->id, 403);

    $data = $request->validate([
        'liner_id' => ['required', 'integer', 'exists:users,id'],
    ]);

    if ($mission->client_validated_at !== null) {
        return response()->json([
            'message' => 'Cette mission a deja ete validee.',
            'data' => $mission->fresh([
                'client:id,name,phone',
                'liner:id,name,phone',
            ]),
        ], 422);
    }

    $selectedLinerId = (int) $data['liner_id'];

    $application = MissionApplication::query()
        ->where('mission_id', $mission->id)
        ->where('liner_id', $selectedLinerId)
        ->first();

    if (!$application) {
        return response()->json([
            'message' => 'Ce liner n a pas candidate a cette mission.',
        ], 422);
    }

    $existingPaidPayment = Payment::query()
        ->where('mission_id', $mission->id)
        ->where('status', 'paid')
        ->latest()
        ->first();

    if ($existingPaidPayment) {
        return response()->json([
            'message' => 'Cette mission a deja un paiement confirme.',
            'data' => $mission->fresh([
                'client:id,name,phone',
                'liner:id,name,phone',
            ]),
        ], 422);
    }

    DB::transaction(function () use ($mission, $selectedLinerId) {
        $mission->update([
            'pending_liner_id' => $selectedLinerId,
            'client_validated_at' => null,
        ]);

        MissionApplication::query()
            ->where('mission_id', $mission->id)
            ->update([
                'status' => 'pending',
            ]);

        MissionApplication::query()
            ->where('mission_id', $mission->id)
            ->where('liner_id', $selectedLinerId)
            ->update([
                'status' => 'selected_for_payment',
            ]);
    });

    return response()->json([
        'data' => $mission->fresh([
            'client:id,name,phone',
            'liner:id,name,phone',
        ]),
        'message' => 'Liner preselectionne. Paiement requis pour valider definitivement la mission.',
    ]);
}

public function confirmLiner(
    Request $request,
    Mission $mission,
    PaymentGatewayManager $gatewayManager,
    InAppNotificationService $notificationService,
): JsonResponse {
    $user = $request->user();
    abort_unless($user->role === 'client' && $mission->client_id === $user->id, 403);

    $data = $request->validate([
        'payment_method_id' => ['nullable', 'integer', 'exists:user_payment_methods,id'],
        'liner_id' => ['nullable', 'integer', 'exists:users,id'],
    ]);

    $selectedLinerId = isset($data['liner_id'])
        ? (int) $data['liner_id']
        : (int) ($mission->pending_liner_id ?? 0);

    if ($selectedLinerId <= 0) {
        return response()->json([
            'message' => 'Aucun liner preselectionne pour cette mission.',
        ], 422);
    }

    $application = MissionApplication::query()
        ->where('mission_id', $mission->id)
        ->where('liner_id', $selectedLinerId)
        ->first();

    if (!$application) {
        return response()->json([
            'message' => 'Ce liner n a pas candidate a cette mission.',
        ], 422);
    }

    if ($mission->client_validated_at !== null && (int) ($mission->liner_id ?? 0) === $selectedLinerId) {
        $existingPaidPayment = Payment::query()
            ->where('mission_id', $mission->id)
            ->where('status', 'paid')
            ->latest()
            ->first();

        return response()->json([
            'data' => [
                'mission' => $mission->fresh(['client:id,name,phone', 'liner:id,name,phone']),
                'payment' => $existingPaidPayment,
            ],
            'message' => 'Liner deja valide et paiement deja confirme.',
        ]);
    }

    $existingPayment = Payment::query()
        ->where('mission_id', $mission->id)
        ->latest()
        ->first();

    if ($existingPayment?->status === 'paid') {
        if (
            !$mission->client_validated_at ||
            (int) ($mission->liner_id ?? 0) !== $selectedLinerId
        ) {
            DB::transaction(function () use (
                $mission,
                $selectedLinerId,
                $notificationService,
                $existingPayment
            ) {
                $mission->update([
                    'liner_id' => $selectedLinerId,
                    'status' => 'accepted',
                    'client_validated_at' => now(),
                    'pending_liner_id' => null,
                ]);

                MissionApplication::query()
                    ->where('mission_id', $mission->id)
                    ->where('liner_id', $selectedLinerId)
                    ->update([
                        'status' => 'accepted',
                    ]);

                MissionApplication::query()
                    ->where('mission_id', $mission->id)
                    ->where('liner_id', '!=', $selectedLinerId)
                    ->update([
                        'status' => 'rejected',
                    ]);

                $notificationService->notifyUser(
                    $selectedLinerId,
                    'mission.client_validated',
                    'Mission validee par le client',
                    sprintf(
                        'Le client a valide votre profil et le paiement est confirme pour "%s". Vous pouvez demarrer.',
                        $mission->title
                    ),
                    [
                        'mission_id' => $mission->id,
                        'payment_id' => $existingPayment->id,
                    ],
                );
            });
        }

        return response()->json([
            'data' => [
                'mission' => $mission->fresh(['client:id,name,phone', 'liner:id,name,phone']),
                'payment' => $existingPayment,
            ],
            'message' => 'Liner deja valide et paiement deja confirme.',
        ]);
    }

    if ($existingPayment?->status === 'pending') {
        $existingMetadata = is_array($existingPayment->metadata)
            ? $existingPayment->metadata
            : [];

        return response()->json([
            'data' => [
                'mission' => $mission->fresh(['client:id,name,phone', 'liner:id,name,phone']),
                'payment' => $existingPayment,
            ],
            'message' => 'Paiement deja en attente de confirmation. Le liner n est pas encore valide.',
            'meta' => [
                'pending_liner_id' => $existingMetadata['pending_liner_id'] ?? $mission->pending_liner_id,
            ],
        ], 202);
    }

    $paymentMethodQuery = UserPaymentMethod::query()
        ->where('user_id', $user->id)
        ->where('type', UserPaymentMethod::TYPE_PAY_IN)
        ->where('is_active', true);

    if (isset($data['payment_method_id'])) {
        $paymentMethodQuery->whereKey($data['payment_method_id']);
    } else {
        $paymentMethodQuery->where('is_default', true);
    }

    $paymentMethod = $paymentMethodQuery->first();

    if (!$paymentMethod) {
        return response()->json([
            'message' => 'Aucun moyen de paiement client valide pour confirmer la mission.',
        ], 422);
    }

    $amount = (float) $mission->budget_amount;
    $commissionRate = (float) config('lineup.payments.commission_rate', 0.025);
    $commissionAmount = round($amount * $commissionRate, 2);
    $linerNetAmount = max(0, round($amount - $commissionAmount, 2));

    if ($paymentMethod->provider === 'paypal') {
        $methodDetails = is_array($paymentMethod->details) ? $paymentMethod->details : [];
        $vaultId = $methodDetails['vault_id'] ?? $methodDetails['vault_token'] ?? $methodDetails['reference'] ?? null;

        if (!is_string($vaultId) || $vaultId === '') {
            return response()->json([
                'message' => 'Jeton PayPal Vault manquant. Lancez d abord la configuration PayPal Vault.',
            ], 422);
        }

        $payment = Payment::updateOrCreate(
            ['mission_id' => $mission->id],
            [
                'client_id' => $mission->client_id,
                'liner_id' => $selectedLinerId,
                'payment_method_id' => $paymentMethod->id,
                'amount' => $amount,
                'currency' => $mission->currency,
                'status' => 'pending',
                'provider' => $paymentMethod->provider,
                'provider_reference' => null,
                'failure_reason' => null,
                'paid_at' => null,
                'metadata' => [
                    'commission_rate' => $commissionRate,
                    'commission_amount' => $commissionAmount,
                    'liner_net_amount' => $linerNetAmount,
                    'charged_stage' => 'service_completion_auto_debit',
                    'charge_mode' => 'merchant_subsequent',
                    'paypal_vault_id' => $vaultId,
                    'pending_liner_id' => $selectedLinerId,
                ],
            ],
        );

        return response()->json([
            'data' => [
                'mission' => $mission->fresh(['client:id,name,phone', 'liner:id,name,phone']),
                'payment' => $payment,
            ],
            'message' => 'Paiement PayPal en attente. La mission ne sera validee qu apres confirmation effective du paiement.',
        ], 202);
    }

    $result = $gatewayManager->charge(
        method: $paymentMethod,
        amount: $amount,
        currency: $mission->currency,
        metadata: [
            'mission_id' => $mission->id,
            'client_id' => $mission->client_id,
            'liner_id' => $selectedLinerId,
            'commission_rate' => $commissionRate,
            'commission_amount' => $commissionAmount,
            'liner_net_amount' => $linerNetAmount,
            'charged_stage' => 'acceptance_confirmation',
        ],
    );

    $payment = Payment::updateOrCreate(
        ['mission_id' => $mission->id],
        [
            'client_id' => $mission->client_id,
            'liner_id' => $selectedLinerId,
            'payment_method_id' => $paymentMethod->id,
            'amount' => $amount,
            'currency' => $mission->currency,
            'status' => $result->status,
            'provider' => $paymentMethod->provider,
            'provider_reference' => $result->providerReference,
            'failure_reason' => $result->status === GatewayResult::STATUS_FAILED
                ? $result->message
                : null,
            'paid_at' => $result->status === GatewayResult::STATUS_PAID
                ? now()
                : null,
            'metadata' => array_merge(
                [
                    'commission_rate' => $commissionRate,
                    'commission_amount' => $commissionAmount,
                    'liner_net_amount' => $linerNetAmount,
                    'charged_stage' => 'acceptance_confirmation',
                    'pending_liner_id' => $selectedLinerId,
                ],
                $result->payload ?: [],
            ),
        ],
    );

    if ($result->status === GatewayResult::STATUS_PAID) {
        DB::transaction(function () use (
            $mission,
            $selectedLinerId,
            $notificationService,
            $payment
        ) {
            $mission->update([
                'liner_id' => $selectedLinerId,
                'status' => 'accepted',
                'client_validated_at' => now(),
                'pending_liner_id' => null,
            ]);

            MissionApplication::query()
                ->where('mission_id', $mission->id)
                ->where('liner_id', $selectedLinerId)
                ->update([
                    'status' => 'accepted',
                ]);

            MissionApplication::query()
                ->where('mission_id', $mission->id)
                ->where('liner_id', '!=', $selectedLinerId)
                ->update([
                    'status' => 'rejected',
                ]);

            $notificationService->notifyUser(
                $selectedLinerId,
                'mission.client_validated',
                'Mission validee par le client',
                sprintf(
                    'Le client a valide votre profil et le paiement est confirme pour "%s". Vous pouvez demarrer.',
                    $mission->title
                ),
                [
                    'mission_id' => $mission->id,
                    'payment_id' => $payment->id,
                ],
            );
        });

        return response()->json([
            'data' => [
                'mission' => $mission->fresh(['client:id,name,phone', 'liner:id,name,phone']),
                'payment' => $payment,
            ],
            'message' => 'Paiement confirme. Mission validee.',
        ]);
    }

    if ($result->status === GatewayResult::STATUS_PENDING) {
        return response()->json([
            'data' => [
                'mission' => $mission->fresh(['client:id,name,phone', 'liner:id,name,phone']),
                'payment' => $payment,
            ],
            'message' => 'Paiement en attente de confirmation. Le liner n est pas encore valide.',
        ], 202);
    }

    DB::transaction(function () use ($mission, $selectedLinerId) {
        $mission->update([
            'liner_id' => null,
            'client_validated_at' => null,
            'pending_liner_id' => $selectedLinerId,
        ]);

        MissionApplication::query()
            ->where('mission_id', $mission->id)
            ->where('liner_id', $selectedLinerId)
            ->update([
                'status' => 'selected_for_payment',
            ]);
    });

    return response()->json([
        'message' => $result->message ?: 'Le paiement a echoue.',
        'data' => [
            'mission' => $mission->fresh(['client:id,name,phone', 'liner:id,name,phone']),
            'payment' => $payment,
        ],
    ], 422);
}
public function decline(Request $request, Mission $mission): JsonResponse
    {
        $user = $request->user();
        abort_unless($user->role === 'liner', 403);

        if ($mission->status !== 'published' || $mission->liner_id !== null) {
            return response()->json(['message' => 'Mission indisponible'], 422);
        }

        MissionApplication::query()
            ->where('mission_id', $mission->id)
            ->where('liner_id', $user->id)
            ->whereIn('status', ['pending', 'rejected'])
            ->update([
                'status' => 'withdrawn',
                'rejected_at' => now(),
            ]);

        $this->recordStatusEvent(
            mission: $mission,
            userId: $user->id,
            status: 'declined',
            latitude: null,
            longitude: null,
            note: 'Mission refusee',
        );

        return response()->json([], 204);
    }

    public function status(
        Request $request,
        Mission $mission,
        InAppNotificationService $notificationService,
    ): JsonResponse
    {
        $user = $request->user();
        abort_unless($user->role === 'liner' && $mission->liner_id === $user->id, 403);

        $data = $request->validate([
            'status' => ['required', 'in:EN_ROUTE,ARRIVE,DANS_LA_FILE,CLIENT_ARRIVE,PLACE_TRANSMISE,MISSION_TERMINEE,MISSION_IMPOSSIBLE'],
            'note' => ['nullable', 'string', 'max:500'],
            'latitude' => ['nullable', 'numeric', 'between:-90,90'],
            'longitude' => ['nullable', 'numeric', 'between:-180,180'],
        ]);

        $mapped = match ($data['status']) {
            'EN_ROUTE' => 'en_route',
            'ARRIVE' => 'arrived',
            'DANS_LA_FILE' => 'in_queue',
            'CLIENT_ARRIVE' => 'client_arrived',
            'PLACE_TRANSMISE' => 'spot_transmitted',
            'MISSION_TERMINEE' => 'completed',
            'MISSION_IMPOSSIBLE' => 'failed',
        };

        if (in_array($mapped, ['en_route', 'arrived', 'in_queue', 'client_arrived', 'spot_transmitted', 'completed'], true)) {
            $paidPayment = Payment::query()
                ->where('mission_id', $mission->id)
                ->where('status', 'paid')
                ->first();

            $latestPayment = Payment::query()
                ->where('mission_id', $mission->id)
                ->latest()
                ->first();

            $latestMetadata = is_array($latestPayment?->metadata) ? $latestPayment->metadata : [];
            $deferredAutoDebitReady = $latestPayment?->status === 'pending'
                && ($latestMetadata['charged_stage'] ?? null) === 'service_completion_auto_debit';

            if (!$mission->client_validated_at || (!$paidPayment && !$deferredAutoDebitReady)) {
                return response()->json([
                    'message' => 'Le client doit valider le liner et enregistrer un paiement avant le debut de mission.',
                ], 422);
            }
        }

        $updates = ['status' => $mapped];

        if (in_array($mapped, ['en_route', 'arrived', 'in_queue', 'client_arrived', 'spot_transmitted', 'completed'], true)) {
            $updates['started_at'] = $mission->started_at ?? now();
        }

        if (in_array($mapped, ['arrived', 'in_queue', 'client_arrived', 'spot_transmitted', 'completed'], true)) {
            $updates['arrived_at'] = $mission->arrived_at ?? now();
        }

        if ($mapped === 'completed') {
            $updates['completed_at'] = now();
        }

        DB::transaction(function () use ($mission, $updates, $user, $data, $mapped, $notificationService): void {
            $mission->update($updates);

            $this->recordStatusEvent(
                $mission,
                $user->id,
                $mapped,
                $data['latitude'] ?? null,
                $data['longitude'] ?? null,
                $data['note'] ?? null,
            );

            $notificationService->notifyUser(
                $mission->client_id,
                'mission.status_changed',
                'Mise a jour mission',
                sprintf(
                    'La mission "%s" est maintenant: %s.',
                    $mission->title,
                    $this->statusLabel($mapped),
                ),
                [
                    'mission_id' => $mission->id,
                    'status' => $mapped,
                    'liner_id' => $mission->liner_id,
                ],
            );

        });

        if ($mapped === 'completed') {
            ProcessMissionSettlementPayment::dispatch($mission->id);
        }

        return response()->json(['data' => $mission->fresh(['client:id,name,phone', 'liner:id,name,phone'])]);
    }

    public function proof(
        Request $request,
        Mission $mission,
        InAppNotificationService $notificationService,
    ): JsonResponse
    {
        $user = $request->user();
        abort_unless($user->role === 'liner' && $mission->liner_id === $user->id, 403);

        $rawPayload = $request->input('payload');
        if (is_string($rawPayload) && $rawPayload !== '') {
            $decodedPayload = json_decode($rawPayload, true);
            if (is_array($decodedPayload)) {
                $request->merge(['payload' => $decodedPayload]);
            }
        }

        $data = $request->validate([
            'type' => ['required', 'in:photo,qr,geoloc'],
            'proof_url' => ['nullable', 'url'],
            'proof_file' => ['nullable', 'file', 'mimes:jpg,jpeg,png,webp,pdf', 'max:10240'],
            'payload' => ['nullable', 'array'],
        ]);

        $proofUrl = $data['proof_url'] ?? null;
        $payload = is_array($data['payload'] ?? null) ? $data['payload'] : [];

        if (in_array($data['type'], ['photo', 'qr'], true) && !$request->hasFile('proof_file') && !$proofUrl) {
            throw ValidationException::withMessages([
                'proof_file' => 'Ajoutez un fichier ou une URL de preuve pour continuer.',
            ]);
        }

        if ($request->hasFile('proof_file')) {
            $file = $request->file('proof_file');
            $storedPath = $file->store('mission-proofs/' . $mission->id, 'public');
            $proofUrl = Storage::disk('public')->url($storedPath);
            $payload = array_merge($payload, [
                'file_name' => $file->getClientOriginalName(),
                'file_size' => $file->getSize(),
                'mime_type' => $file->getClientMimeType(),
                'stored_path' => $storedPath,
                'upload_source' => 'file',
            ]);
        }

        $proof = MissionProof::create([
            'mission_id' => $mission->id,
            'liner_id' => $user->id,
            'type' => $data['type'],
            'proof_url' => $proofUrl,
            'payload' => $payload !== [] ? $payload : null,
            'status' => 'pending',
        ]);

        $notificationService->notifyUser(
            $mission->client_id,
            'mission.proof_submitted',
            'Preuve envoyee',
            sprintf('Une preuve "%s" a ete envoyee pour "%s".', $proof->type, $mission->title),
            [
                'mission_id' => $mission->id,
                'proof_id' => $proof->id,
                'type' => $proof->type,
            ],
        );

        return response()->json(['data' => $proof], 201);
    }

    public function track(Request $request, Mission $mission): JsonResponse
    {
        $user = $request->user();

        $this->authorizeMissionAccess($mission, $user->id, $user->role);

        return response()->json([
            'data' => [
                'mission' => $mission->load([
                    'client:id,name,phone',
                    'liner:id,name,phone,is_available,current_latitude,current_longitude,current_location_updated_at',
                    'latestLocationEvent',
                ]),
                'timeline' => $mission->statusEvents()->latest()->get(),
                'proofs' => $mission->proofs()->latest()->get(),
                'payment' => Payment::query()
                    ->where('mission_id', $mission->id)
                    ->latest()
                    ->first(),
                'messages' => $mission->messages()
                    ->with('sender:id,name,phone,role')
                    ->latest()
                    ->limit(50)
                    ->get()
                    ->reverse()
                    ->values(),
            ],
        ]);
    }

    public function messages(Request $request, Mission $mission): JsonResponse
    {
        $user = $request->user();
        $this->authorizeMissionAccess($mission, $user->id, $user->role);

        return response()->json([
            'data' => $mission->messages()
                ->with('sender:id,name,phone,role')
                ->latest()
                ->limit(50)
                ->get()
                ->reverse()
                ->values(),
        ]);
    }

    public function sendMessage(
        Request $request,
        Mission $mission,
        InAppNotificationService $notificationService,
    ): JsonResponse
    {
        $user = $request->user();
        $this->authorizeMissionAccess($mission, $user->id, $user->role);

        if ($mission->liner_id === null) {
            return response()->json(['message' => 'Aucun liner assigne pour le moment'], 422);
        }

        $data = $request->validate([
            'message' => ['required', 'string', 'max:2000'],
        ]);

        $message = MissionMessage::create([
            'mission_id' => $mission->id,
            'sender_id' => $user->id,
            'message' => trim($data['message']),
        ]);

        $recipientId = $user->id === $mission->client_id
            ? $mission->liner_id
            : $mission->client_id;

        $notificationService->notifyUser(
            $recipientId,
            'mission.chat_message',
            'Nouveau message',
            sprintf('Nouveau message sur "%s".', $mission->title),
            [
                'mission_id' => $mission->id,
                'message_id' => $message->id,
                'sender_id' => $user->id,
            ],
        );

        return response()->json([
            'data' => $message->load('sender:id,name,phone,role'),
        ], 201);
    }

    public function location(Request $request, Mission $mission): JsonResponse
    {
        $user = $request->user();
        abort_unless($user->role === 'liner' && $mission->liner_id === $user->id, 403);

        if (in_array($mission->status, ['cancelled', 'completed', 'failed'], true)) {
            return response()->json(['message' => 'La mission n est pas active'], 422);
        }

        $data = $request->validate([
            'latitude' => ['required', 'numeric', 'between:-90,90'],
            'longitude' => ['required', 'numeric', 'between:-180,180'],
            'note' => ['nullable', 'string', 'max:500'],
        ]);

        $this->recordStatusEvent(
            mission: $mission,
            userId: $user->id,
            status: 'live_location',
            latitude: (float) $data['latitude'],
            longitude: (float) $data['longitude'],
            note: $data['note'] ?? null,
        );

        return response()->json(['data' => ['ok' => true]]);
    }

    public function stream(Request $request, Mission $mission): StreamedResponse
    {
        $user = $request->user();
        $this->authorizeMissionAccess($mission, $user->id, $user->role);

        $sinceId = max(0, (int) $request->integer('since_id', 0));
        $timeout = min(30, max(1, (int) $request->integer('timeout', 20)));

        return response()->stream(function () use ($mission, $sinceId, $timeout): void {
            $startedAt = microtime(true);
            $lastId = $sinceId;

            while ((microtime(true) - $startedAt) < $timeout) {
                if (connection_aborted()) {
                    break;
                }

                $events = $mission->statusEvents()
                    ->where('id', '>', $lastId)
                    ->orderBy('id')
                    ->get();

                foreach ($events as $event) {
                    $lastId = $event->id;

                    echo 'id: ' . $event->id . "\n";
                    echo "event: mission.status\n";
                    echo 'data: ' . json_encode([
                        'mission_id' => $mission->id,
                        'status_event_id' => $event->id,
                        'status' => $event->status,
                        'latitude' => $event->latitude,
                        'longitude' => $event->longitude,
                        'note' => $event->note,
                        'occurred_at' => $event->created_at?->toIso8601String(),
                    ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) . "\n\n";
                }

                @ob_flush();
                @flush();
                usleep(1_000_000);
            }

            echo "event: keepalive\n";
            echo "data: {}\n\n";
            @ob_flush();
            @flush();
        }, 200, [
            'Content-Type' => 'text/event-stream',
            'Cache-Control' => 'no-cache',
            'Connection' => 'keep-alive',
            'X-Accel-Buffering' => 'no',
        ]);
    }

    public function rating(Request $request, Mission $mission): JsonResponse
    {
        $user = $request->user();
        abort_unless($mission->client_id === $user->id, 403);

        if ($mission->status !== 'completed' || !$mission->liner_id) {
            return response()->json(['message' => 'La mission ne peut pas encore etre evaluee'], 422);
        }

        $data = $request->validate([
            'stars' => ['required', 'integer', 'between:1,5'],
            'comment' => ['nullable', 'string', 'max:1000'],
        ]);

        $rating = MissionRating::updateOrCreate(
            ['mission_id' => $mission->id],
            [
                'client_id' => $user->id,
                'liner_id' => $mission->liner_id,
                'stars' => $data['stars'],
                'comment' => $data['comment'] ?? null,
            ],
        );

        return response()->json(['data' => $rating], 201);
    }

    private function ensureMissionHasSelectedLiner(
        Mission $mission,
        InAppNotificationService $notificationService,
        ?int $selectedLinerId = null,
    ): Mission {
        if ($mission->liner_id !== null && $mission->status === 'accepted') {
            if ($selectedLinerId !== null && $mission->liner_id !== $selectedLinerId) {
                $this->throwMissionSelectionValidation('Un autre liner a deja ete accepte pour cette mission.');
            }

            return $mission;
        }

        if ($mission->status !== 'published') {
            $this->throwMissionSelectionValidation(
                'Le client ne peut plus choisir de liner sur cette mission.',
            );
        }

        $selectionChanged = false;
        $selectedLinerName = '';
        $selectedLinerIdForNotification = null;
        $rejectedLinerIds = [];

        $mission = DB::transaction(function () use (
            $mission,
            $selectedLinerId,
            &$selectionChanged,
            &$selectedLinerName,
            &$selectedLinerIdForNotification,
            &$rejectedLinerIds,
        ): Mission {
            $lockedMission = Mission::query()
                ->with('liner:id,name')
                ->whereKey($mission->id)
                ->lockForUpdate()
                ->firstOrFail();

            if ($lockedMission->liner_id !== null && $lockedMission->status === 'accepted') {
                if ($selectedLinerId !== null && $lockedMission->liner_id !== $selectedLinerId) {
                    $this->throwMissionSelectionValidation('Un autre liner a deja ete accepte pour cette mission.');
                }

                return $lockedMission->fresh([
                    'client:id,name,phone',
                    'liner:id,name,phone',
                ]);
            }

            if ($lockedMission->status !== 'published') {
                $this->throwMissionSelectionValidation(
                    'Le client ne peut plus choisir de liner sur cette mission.',
                );
            }

            $pendingApplications = MissionApplication::query()
                ->with('liner:id,name')
                ->where('mission_id', $lockedMission->id)
                ->where('status', 'pending')
                ->lockForUpdate()
                ->get();

            if ($pendingApplications->isEmpty()) {
                $this->throwMissionSelectionValidation('Aucune candidature en attente sur cette mission.');
            }

            if ($selectedLinerId !== null) {
                $selectedApplication = $pendingApplications->firstWhere('liner_id', $selectedLinerId);
                if (!$selectedApplication) {
                    $this->throwMissionSelectionValidation('Ce liner n a pas de candidature en attente.');
                }
            } else {
                if ($pendingApplications->count() > 1) {
                    $this->throwMissionSelectionValidation(
                        'Plusieurs candidatures sont en attente. Selectionnez un liner explicitement.',
                    );
                }

                $selectedApplication = $pendingApplications->first();
            }

            $now = now();
            $selectedLinerIdValue = (int) $selectedApplication->liner_id;

            MissionApplication::query()
                ->where('mission_id', $lockedMission->id)
                ->where('status', 'pending')
                ->where('liner_id', '!=', $selectedLinerIdValue)
                ->update([
                    'status' => 'rejected',
                    'accepted_at' => null,
                    'rejected_at' => $now,
                ]);

            $selectedApplication->update([
                'status' => 'accepted',
                'accepted_at' => $now,
                'rejected_at' => null,
            ]);

            $lockedMission->update([
                'liner_id' => $selectedLinerIdValue,
                'status' => 'accepted',
                'accepted_at' => $now,
                'client_validated_at' => null,
            ]);

            $selectionChanged = true;
            $selectedLinerIdForNotification = $selectedLinerIdValue;
            $selectedLinerName = $selectedApplication->liner?->name ?? 'Le liner';
            $rejectedLinerIds = $pendingApplications
                ->where('liner_id', '!=', $selectedLinerIdValue)
                ->pluck('liner_id')
                ->unique()
                ->values()
                ->all();

            $this->recordStatusEvent(
                mission: $lockedMission,
                userId: $selectedLinerIdValue,
                status: 'accepted',
                latitude: null,
                longitude: null,
                note: 'Mission acceptee par le client',
            );

            return $lockedMission->fresh([
                'client:id,name,phone',
                'liner:id,name,phone',
            ]);
        });

        if (!$selectionChanged || $selectedLinerIdForNotification === null) {
            return $mission;
        }

        $notificationService->notifyUser(
            $selectedLinerIdForNotification,
            'mission.accepted',
            'Mission acceptee',
            sprintf('Le client a retenu votre candidature pour "%s".', $mission->title),
            [
                'mission_id' => $mission->id,
                'status' => 'accepted',
                'liner_id' => $selectedLinerIdForNotification,
            ],
        );

        foreach ($rejectedLinerIds as $rejectedLinerId) {
            $notificationService->notifyUser(
                (int) $rejectedLinerId,
                'mission.application_rejected',
                'Candidature non retenue',
                sprintf(
                    'Votre candidature pour "%s" n a pas ete retenue. %s a ete selectionne.',
                    $mission->title,
                    $selectedLinerName,
                ),
                [
                    'mission_id' => $mission->id,
                    'status' => 'rejected',
                    'selected_liner_id' => $selectedLinerIdForNotification,
                ],
            );
        }

        return $mission;
    }

    private function throwMissionSelectionValidation(string $message): never
    {
        throw ValidationException::withMessages([
            'mission' => [$message],
        ]);
    }

    private function recordStatusEvent(
        Mission $mission,
        int $userId,
        string $status,
        ?float $latitude,
        ?float $longitude,
        ?string $note,
    ): void {
        $event = MissionStatusEvent::create([
            'mission_id' => $mission->id,
            'user_id' => $userId,
            'status' => $status,
            'latitude' => $latitude,
            'longitude' => $longitude,
            'note' => $note,
        ]);

        event(new MissionTrackingUpdated(
            missionId: $mission->id,
            statusEventId: $event->id,
            status: $event->status,
            latitude: $event->latitude !== null ? (float) $event->latitude : null,
            longitude: $event->longitude !== null ? (float) $event->longitude : null,
            note: $event->note,
            occurredAt: $event->created_at?->toIso8601String() ?? now()->toIso8601String(),
        ));
    }

    private function authorizeMissionAccess(Mission $mission, int $userId, string $userRole): void
    {
        $isAuthorized = $mission->client_id === $userId
            || $mission->liner_id === $userId
            || $userRole === 'admin';

        abort_unless($isAuthorized, 403);
    }

    private function statusLabel(string $status): string
    {
        return match ($status) {
            'accepted' => 'acceptee',
            'en_route' => 'en route',
            'arrived' => 'arrivee sur place',
            'in_queue' => 'dans la file',
            'client_arrived' => 'client arrive',
            'spot_transmitted' => 'place transmise',
            'completed' => 'terminee',
            'failed' => 'mission impossible',
            'cancelled' => 'annulee',
            default => $status,
        };
    }

}
