<?php

namespace App\Http\Controllers\Web\Bo;

use App\Http\Controllers\Controller;
use App\Models\Mission;
use App\Models\MissionApplication;
use App\Models\MissionRating;
use App\Models\Payment;
use App\Models\Payout;
use App\Models\User;
use App\Models\WalletTransaction;
use App\Services\AdminAuditLogger;
use App\Services\InAppNotificationService;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Validation\Rule;
use Illuminate\View\View;

class MissionController extends Controller
{
    private const STATUSES = [
        'published',
        'accepted',
        'en_route',
        'arrived',
        'in_queue',
        'client_arrived',
        'spot_transmitted',
        'completed',
        'failed',
        'cancelled',
    ];

    public function index(Request $request): View
    {
        $search = trim((string) $request->string('q'));
        $status = trim((string) $request->string('status'));
        $clientId = $request->integer('client_id');
        $linerId = $request->integer('liner_id');
        $dateFrom = trim((string) $request->string('date_from'));
        $dateTo = trim((string) $request->string('date_to'));
        $budgetMin = $request->string('budget_min')->toString();
        $budgetMax = $request->string('budget_max')->toString();

        $missions = Mission::query()
            ->with(['client:id,name,email', 'liner:id,name,email'])
            ->when($search !== '', function ($query) use ($search): void {
                $query->where(function ($subQuery) use ($search): void {
                    $subQuery
                        ->where('title', 'like', '%' . $search . '%')
                        ->orWhere('event_name', 'like', '%' . $search . '%')
                        ->orWhere('location', 'like', '%' . $search . '%');
                });
            })
            ->when($status !== '', fn ($query) => $query->where('status', $status))
            ->when($clientId > 0, fn ($query) => $query->where('client_id', $clientId))
            ->when($linerId > 0, fn ($query) => $query->where('liner_id', $linerId))
            ->when($dateFrom !== '', fn ($query) => $query->whereDate('expected_start_at', '>=', $dateFrom))
            ->when($dateTo !== '', fn ($query) => $query->whereDate('expected_start_at', '<=', $dateTo))
            ->when(is_numeric($budgetMin), fn ($query) => $query->where('budget_amount', '>=', (float) $budgetMin))
            ->when(is_numeric($budgetMax), fn ($query) => $query->where('budget_amount', '<=', (float) $budgetMax))
            ->latest()
            ->paginate(20)
            ->withQueryString();

        return view('bo.missions.index', [
            'missions' => $missions,
            'search' => $search,
            'status' => $status,
            'clientId' => $clientId,
            'linerId' => $linerId,
            'dateFrom' => $dateFrom,
            'dateTo' => $dateTo,
            'budgetMin' => $budgetMin,
            'budgetMax' => $budgetMax,
            'statuses' => self::STATUSES,
            'clients' => User::query()
                ->where('role', 'client')
                ->orderBy('name')
                ->limit(200)
                ->get(['id', 'name', 'email']),
            'liners' => User::query()
                ->where('role', 'liner')
                ->orderBy('name')
                ->limit(200)
                ->get(['id', 'name', 'email']),
        ]);
    }

    public function create(): View
    {
        return view('bo.missions.create', [
            'statuses' => self::STATUSES,
            'clients' => User::query()
                ->where('role', 'client')
                ->orderBy('name')
                ->get(['id', 'name', 'email']),
            'liners' => User::query()
                ->where('role', 'liner')
                ->orderBy('name')
                ->get(['id', 'name', 'email']),
        ]);
    }

    public function store(Request $request, AdminAuditLogger $auditLogger): RedirectResponse
    {
        $data = $request->validate([
            'client_id' => ['required', 'integer', Rule::exists('users', 'id')->where(fn ($query) => $query->where('role', 'client'))],
            'liner_id' => ['nullable', 'integer', Rule::exists('users', 'id')->where(fn ($query) => $query->where('role', 'liner'))],
            'title' => ['required', 'string', 'max:180'],
            'description' => ['nullable', 'string', 'max:3000'],
            'event_name' => ['nullable', 'string', 'max:180'],
            'location' => ['required', 'string', 'max:255'],
            'expected_start_at' => ['required', 'date'],
            'status' => ['required', Rule::in(self::STATUSES)],
            'budget_amount' => ['required', 'numeric', 'min:0'],
            'currency' => ['required', 'string', 'size:3'],
        ]);

        $mission = Mission::create([
            'client_id' => (int) $data['client_id'],
            'liner_id' => isset($data['liner_id']) ? (int) $data['liner_id'] : null,
            'title' => $data['title'],
            'description' => $data['description'] ?? null,
            'event_name' => $data['event_name'] ?? null,
            'location' => $data['location'],
            'expected_start_at' => $data['expected_start_at'],
            'status' => $data['status'],
            'budget_amount' => (float) $data['budget_amount'],
            'currency' => strtoupper($data['currency']),
            'accepted_at' => in_array($data['status'], ['accepted', 'en_route', 'arrived', 'in_queue', 'client_arrived', 'spot_transmitted', 'completed', 'failed'], true) ? now() : null,
            'started_at' => in_array($data['status'], ['en_route', 'arrived', 'in_queue', 'client_arrived', 'spot_transmitted', 'completed', 'failed'], true) ? now() : null,
            'arrived_at' => in_array($data['status'], ['arrived', 'in_queue', 'client_arrived', 'spot_transmitted', 'completed'], true) ? now() : null,
            'completed_at' => $data['status'] === 'completed' ? now() : null,
            'cancelled_at' => $data['status'] === 'cancelled' ? now() : null,
        ]);

        $auditLogger->log(
            admin: $request->user(),
            action: 'bo.missions.create',
            auditable: $mission,
            metadata: [
                'status' => $mission->status,
                'budget_amount' => (float) $mission->budget_amount,
            ],
        );

        return redirect()
            ->route('bo.missions.index')
            ->with('status', sprintf('Mission #%d creee.', $mission->id));
    }

    public function show(Mission $mission): View
    {
        $mission->load([
            'client:id,name,email,phone',
            'liner:id,name,email,phone',
            'statusEvents' => fn ($query) => $query->latest(),
            'proofs' => fn ($query) => $query->with(['liner:id,name', 'reviewer:id,name'])->latest(),
            'disputes' => fn ($query) => $query->with(['reporter:id,name,email'])->latest(),
            'messages' => fn ($query) => $query->latest()->limit(20),
            'applications' => fn ($query) => $query->with(['liner:id,name,email,phone'])->latest(),
        ]);

        $latestPayment = Payment::query()
            ->with(['paymentMethod:id,label,provider,type'])
            ->where('mission_id', $mission->id)
            ->latest('id')
            ->first();

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

        $linerPayouts = $mission->liner_id
            ? Payout::query()
                ->with(['paymentMethod:id,label,provider,type'])
                ->where('liner_id', $mission->liner_id)
                ->latest('id')
                ->limit(5)
                ->get()
            : collect();

        $linerWalletTransactions = $mission->liner_id
            ? WalletTransaction::query()
                ->where('user_id', $mission->liner_id)
                ->latest('id')
                ->limit(5)
                ->get()
            : collect();

        return view('bo.missions.show', [
            'mission' => $mission,
            'latestPayment' => $latestPayment,
            'missionRating' => $missionRating,
            'linerPayouts' => $linerPayouts,
            'linerWalletTransactions' => $linerWalletTransactions,
        ]);
    }

    public function acceptApplication(
        Request $request,
        Mission $mission,
        MissionApplication $application,
        AdminAuditLogger $auditLogger,
        InAppNotificationService $notificationService,
    ): RedirectResponse {
        abort_unless($application->mission_id === $mission->id, 404);

        $paymentLocked = Payment::query()
            ->where('mission_id', $mission->id)
            ->whereIn('status', ['pending', 'paid'])
            ->exists();

        if (!in_array($mission->status, ['published', 'accepted'], true) || $paymentLocked) {
            return back()->with('status', 'Le BO peut changer de liner uniquement avant tout paiement en attente ou confirme.');
        }

        DB::transaction(function () use ($mission, $application, $request, $auditLogger, $notificationService): void {
            $now = now();

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

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

            $mission->update([
                'liner_id' => $application->liner_id,
                'status' => 'accepted',
                'accepted_at' => $mission->accepted_at ?? $now,
            ]);

            $auditLogger->log(
                admin: $request->user(),
                action: 'bo.missions.application.accept',
                auditable: $mission,
                metadata: [
                    'application_id' => $application->id,
                    'liner_id' => $application->liner_id,
                ],
            );

            $notificationService->notifyUser(
                $application->liner_id,
                'mission.application.accepted',
                'Mission retenue',
                sprintf('Votre candidature a ete retenue pour "%s".', $mission->title),
                [
                    'mission_id' => $mission->id,
                    'application_id' => $application->id,
                    'status' => 'accepted',
                ],
            );

            $rejectedLinerIds = MissionApplication::query()
                ->where('mission_id', $mission->id)
                ->where('id', '!=', $application->id)
                ->pluck('liner_id')
                ->filter()
                ->unique()
                ->all();

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

        return back()->with('status', 'Candidature acceptee et liner selectionne.');
    }

    public function rejectApplication(
        Request $request,
        Mission $mission,
        MissionApplication $application,
        AdminAuditLogger $auditLogger,
        InAppNotificationService $notificationService,
    ): RedirectResponse {
        abort_unless($application->mission_id === $mission->id, 404);

        if ($application->status === 'accepted' || $mission->liner_id === $application->liner_id) {
            return back()->with('status', 'Le liner retenu ne peut pas etre rejete depuis cette action rapide.');
        }

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

        $auditLogger->log(
            admin: $request->user(),
            action: 'bo.missions.application.reject',
            auditable: $mission,
            metadata: [
                'application_id' => $application->id,
                'liner_id' => $application->liner_id,
            ],
        );

        $notificationService->notifyUser(
            $application->liner_id,
            'mission.application.rejected',
            'Candidature non retenue',
            sprintf('Votre candidature n a pas ete retenue pour "%s".', $mission->title),
            [
                'mission_id' => $mission->id,
                'application_id' => $application->id,
                'status' => 'rejected',
            ],
        );

        return back()->with('status', 'Candidature rejetee.');
    }

    public function edit(Mission $mission): View
    {
        return view('bo.missions.edit', [
            'mission' => $mission,
            'statuses' => self::STATUSES,
            'clients' => User::query()
                ->where('role', 'client')
                ->orderBy('name')
                ->get(['id', 'name', 'email']),
            'liners' => User::query()
                ->where('role', 'liner')
                ->orderBy('name')
                ->get(['id', 'name', 'email']),
        ]);
    }

    public function update(Request $request, Mission $mission, AdminAuditLogger $auditLogger): RedirectResponse
    {
        $data = $request->validate([
            'client_id' => ['required', 'integer', Rule::exists('users', 'id')->where(fn ($query) => $query->where('role', 'client'))],
            'liner_id' => ['nullable', 'integer', Rule::exists('users', 'id')->where(fn ($query) => $query->where('role', 'liner'))],
            'title' => ['required', 'string', 'max:180'],
            'description' => ['nullable', 'string', 'max:3000'],
            'event_name' => ['nullable', 'string', 'max:180'],
            'location' => ['required', 'string', 'max:255'],
            'expected_start_at' => ['required', 'date'],
            'status' => ['required', Rule::in(self::STATUSES)],
            'budget_amount' => ['required', 'numeric', 'min:0'],
            'currency' => ['required', 'string', 'size:3'],
        ]);

        $status = $data['status'];

        $mission->update([
            'client_id' => (int) $data['client_id'],
            'liner_id' => isset($data['liner_id']) ? (int) $data['liner_id'] : null,
            'title' => $data['title'],
            'description' => $data['description'] ?? null,
            'event_name' => $data['event_name'] ?? null,
            'location' => $data['location'],
            'expected_start_at' => $data['expected_start_at'],
            'status' => $status,
            'budget_amount' => (float) $data['budget_amount'],
            'currency' => strtoupper($data['currency']),
            'accepted_at' => in_array($status, ['accepted', 'en_route', 'arrived', 'in_queue', 'client_arrived', 'spot_transmitted', 'completed', 'failed'], true)
                ? ($mission->accepted_at ?? now())
                : null,
            'started_at' => in_array($status, ['en_route', 'arrived', 'in_queue', 'client_arrived', 'spot_transmitted', 'completed', 'failed'], true)
                ? ($mission->started_at ?? now())
                : null,
            'arrived_at' => in_array($status, ['arrived', 'in_queue', 'client_arrived', 'spot_transmitted', 'completed'], true)
                ? ($mission->arrived_at ?? now())
                : null,
            'completed_at' => $status === 'completed' ? ($mission->completed_at ?? now()) : null,
            'cancelled_at' => $status === 'cancelled' ? ($mission->cancelled_at ?? now()) : null,
        ]);

        $auditLogger->log(
            admin: $request->user(),
            action: 'bo.missions.update',
            auditable: $mission,
            metadata: [
                'status' => $mission->status,
                'budget_amount' => (float) $mission->budget_amount,
            ],
        );

        return redirect()
            ->route('bo.missions.index')
            ->with('status', sprintf('Mission #%d mise a jour.', $mission->id));
    }

    public function destroy(Request $request, Mission $mission, AdminAuditLogger $auditLogger): RedirectResponse
    {
        $auditLogger->log(
            admin: $request->user(),
            action: 'bo.missions.delete',
            auditable: $mission,
            metadata: [
                'title' => $mission->title,
            ],
        );

        $mission->delete();

        return redirect()
            ->route('bo.missions.index')
            ->with('status', 'Mission supprimee.');
    }
}
