<?php

namespace App\Http\Controllers\Api;

use App\Http\Controllers\Controller;
use App\Models\ApiToken;
use App\Models\KycDocument;
use App\Models\Mission;
use App\Models\MissionRating;
use App\Models\User;
use App\Models\Wallet;
use App\Models\WalletTransaction;
use App\Services\InAppNotificationService;
use App\Services\Auth\FirebaseIdTokenVerifier;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Password;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
use Illuminate\Validation\Rule;

class AuthController extends Controller
{
    public function register(
        Request $request,
        InAppNotificationService $notificationService,
    ): JsonResponse
    {
        $data = $request->validate([
            'name' => ['required', 'string', 'max:255'],
            'email' => ['required', 'email', 'max:255', 'unique:users,email'],
            'phone' => ['nullable', 'string', 'max:50'],
            'address' => ['nullable', 'string', 'max:255'],
            'password' => ['required', 'string', 'min:8'],
            'role' => ['required', 'in:client,liner'],
            'document_type' => ['nullable', 'in:cni,passeport,titre_sejour'],
            'document_number' => ['nullable', 'string', 'max:120'],
            'document_url' => ['nullable', 'url'],
        ]);

        $isLiner = $data['role'] === 'liner';
        $hasDocumentType = filled($data['document_type'] ?? null);
        $hasDocumentNumber = filled($data['document_number'] ?? null);
        $hasDocumentUrl = filled($data['document_url'] ?? null);
        $hasCompleteKyc = $hasDocumentType && $hasDocumentNumber && $hasDocumentUrl;

        if ($isLiner && !$hasCompleteKyc && ($hasDocumentType || $hasDocumentNumber || $hasDocumentUrl)) {
            return response()->json([
                'message' => 'Pour soumettre le KYC, renseignez type, numero et URL de la piece d identite.',
            ], 422);
        }

        $user = User::create([
            'name' => $data['name'],
            'email' => strtolower($data['email']),
            'phone' => $data['phone'] ?? null,
            'address' => $data['address'] ?? null,
            'password' => $data['password'],
            'role' => $data['role'],
            'is_kyc_verified' => $data['role'] === 'client',
            'is_available' => !$isLiner || $hasCompleteKyc,
            'email_notifications_enabled' => true,
            'language' => 'fr',
            'region' => 'FR',
            'push_notifications_enabled' => true,
            'sms_notifications_enabled' => false,
            'email_marketing_enabled' => true,
        ]);

        Wallet::firstOrCreate(
            ['user_id' => $user->id],
            ['balance' => 0, 'currency' => 'EUR']
        );

        if ($isLiner) {
            if ($hasCompleteKyc) {
                KycDocument::create([
                    'user_id' => $user->id,
                    'document_type' => $data['document_type'],
                    'document_number' => $data['document_number'],
                    'document_url' => $data['document_url'],
                    'notes' => 'Soumis lors de l inscription.',
                    'status' => 'pending',
                ]);

                $notificationService->notifyUser(
                    $user->id,
                    'kyc.submitted',
                    'KYC recu',
                    'Votre piece d identite a ete recue. Verification en cours.',
                    ['source' => 'register'],
                );
            } else {
                $notificationService->notifyUser(
                    $user->id,
                    'kyc.required',
                    'Piece d identite requise',
                    'Ajoutez votre piece d identite dans le profil pour activer votre compte liner.',
                    ['source' => 'register', 'is_available' => false],
                );
            }
        }

        $tokenPayload = $this->issueToken($user, 'register');

        return response()->json([
            'token' => $tokenPayload['plain_text_token'],
            'expires_at' => $tokenPayload['expires_at'],
            ...$this->userResponsePayload($user),
        ], 201);
    }

    public function login(Request $request): JsonResponse
    {
        $data = $request->validate([
            'login' => ['required', 'string'],
            'password' => ['required', 'string'],
        ]);

        $user = User::query()
            ->where('email', strtolower($data['login']))
            ->orWhere('phone', $data['login'])
            ->first();

        if (!$user || !Hash::check($data['password'], $user->password)) {
            return response()->json(['message' => 'Identifiants invalides'], 422);
        }

        $tokenPayload = $this->issueToken($user, 'login');

        return response()->json([
            'token' => $tokenPayload['plain_text_token'],
            'expires_at' => $tokenPayload['expires_at'],
            ...$this->userResponsePayload($user),
        ]);
    }

   public function firebaseLogin(
    Request $request,
    FirebaseIdTokenVerifier $verifier,
    InAppNotificationService $notificationService,
): JsonResponse {
    $data = $request->validate([
        'id_token' => ['required', 'string'],
        'role' => ['required', 'in:client,liner'],
        'provider' => ['nullable', 'string', 'max:100'],
    ]);

    try {
        $claims = $verifier->verify($data['id_token']);
    } catch (\Throwable $e) {
        return response()->json([
            'message' => 'Jeton Firebase invalide',
            'debug' => $e->getMessage(),
        ], 422);
    }

    $firebaseUid = (string) ($claims['user_id'] ?? $claims['sub'] ?? '');
    if ($firebaseUid === '') {
        return response()->json([
            'message' => 'Jeton Firebase invalide',
            'debug' => 'firebase_uid manquant dans les claims.',
        ], 422);
    }

    $email = strtolower((string) ($claims['email'] ?? ''));
    $phone = trim((string) ($claims['phone_number'] ?? ''));
    $name = trim((string) ($claims['name'] ?? ''));
    $signInProvider = trim(
        (string) ($claims['firebase']['sign_in_provider'] ?? ($data['provider'] ?? 'firebase'))
    );

    if ($name === '') {
        $name = $email !== ''
            ? (string) Str::of($email)->before('@')
            : 'Utilisateur LineUp';
    }

    $user = User::query()
        ->where('firebase_uid', $firebaseUid)
        ->first();

    if (!$user && $email !== '') {
        $user = User::query()->where('email', $email)->first();
    }

    if (!$user && $phone !== '') {
        $user = User::query()->where('phone', $phone)->first();
    }

    if ($user) {
        if ($user->role !== $data['role']) {
            return response()->json([
                'message' => 'Ce compte est deja associe a un autre role.',
                'debug' => 'user_id=' . $user->id . ', role_db=' . $user->role . ', role_requested=' . $data['role'],
            ], 422);
        }

        $updatePayload = [
            'firebase_uid' => $firebaseUid,
            'firebase_provider' => $signInProvider,
        ];

        if ($phone !== '') {
            $updatePayload['phone'] = $phone;
            if (!$user->phone_verified_at || $user->phone !== $phone) {
                $updatePayload['phone_verified_at'] = now();
            }
        }

        if (($claims['email_verified'] ?? false) === true && !$user->email_verified_at) {
            $updatePayload['email_verified_at'] = now();
        }

        if (
            $user->name === '' ||
            $user->name === 'LineUp User' ||
            $user->name === 'Utilisateur LineUp'
        ) {
            $updatePayload['name'] = $name;
        }

        $user->update($updatePayload);
    } else {
        $isLiner = $data['role'] === 'liner';

        $user = User::create([
            'name' => $name,
            'email' => $email !== ''
                ? $email
                : $this->firebaseFallbackEmail($firebaseUid, $data['role']),
            'phone' => $phone !== '' ? $phone : null,
            'address' => null,
            'password' => Str::random(48),
            'role' => $data['role'],
            'firebase_uid' => $firebaseUid,
            'firebase_provider' => $signInProvider,
            'is_kyc_verified' => $data['role'] === 'client',
            'is_available' => !$isLiner,
            'email_notifications_enabled' => true,
            'language' => 'fr',
            'region' => 'FR',
            'push_notifications_enabled' => true,
            'sms_notifications_enabled' => false,
            'email_marketing_enabled' => true,
            'email_verified_at' => ($claims['email_verified'] ?? false) === true ? now() : null,
            'phone_verified_at' => $phone !== '' ? now() : null,
        ]);

        Wallet::firstOrCreate(
            ['user_id' => $user->id],
            ['balance' => 0, 'currency' => 'EUR']
        );

        if ($isLiner) {
            $notificationService->notifyUser(
                $user->id,
                'kyc.required',
                'Piece d identite requise',
                'Ajoutez votre piece d identite dans le profil pour activer votre compte liner.',
                ['source' => 'firebase_register', 'is_available' => false],
            );
        }
    }

    $tokenPayload = $this->issueToken($user->fresh(), 'firebase_login');

    return response()->json([
        'token' => $tokenPayload['plain_text_token'],
        'expires_at' => $tokenPayload['expires_at'],
        ...$this->userResponsePayload($user),
    ]);
}

    public function me(Request $request): JsonResponse
    {
        return response()->json($this->userResponsePayload($request->user()));
    }

    public function updateMe(Request $request): JsonResponse
    {
        $user = $request->user();

        $data = $request->validate([
            'name' => ['sometimes', 'string', 'max:255'],
            'email' => ['sometimes', 'email', 'max:255', Rule::unique('users', 'email')->ignore($user->id)],
            'phone' => ['sometimes', 'nullable', 'string', 'max:50'],
            'address' => ['sometimes', 'nullable', 'string', 'max:255'],
            'avatar_url' => ['sometimes', 'nullable', 'url', 'max:2048'],
            'avatar_file' => ['sometimes', 'nullable', 'file', 'mimes:jpg,jpeg,png,webp', 'max:5120'],
            'bio' => ['sometimes', 'nullable', 'string', 'max:1000'],
            'is_available' => ['sometimes', 'boolean'],
            'email_notifications_enabled' => ['sometimes', 'boolean'],
            'language' => ['sometimes', 'nullable', 'string', 'min:2', 'max:10'],
            'region' => ['sometimes', 'nullable', 'string', 'min:2', 'max:10'],
            'push_notifications_enabled' => ['sometimes', 'boolean'],
            'sms_notifications_enabled' => ['sometimes', 'boolean'],
            'email_marketing_enabled' => ['sometimes', 'boolean'],
            'password' => ['sometimes', 'nullable', 'string', 'min:8', 'confirmed'],
        ]);

        $payload = [];

        foreach (['name', 'phone', 'address', 'avatar_url', 'bio'] as $field) {
            if (array_key_exists($field, $data)) {
                $payload[$field] = $data[$field];
            }
        }

        if ($request->hasFile('avatar_file')) {
            $avatarFile = $request->file('avatar_file');
            $storedPath = $avatarFile->store('avatars/' . $user->id, 'public');
            $payload['avatar_url'] = Storage::disk('public')->url($storedPath);
        }

        if (array_key_exists('email', $data)) {
            $normalizedEmail = strtolower((string) $data['email']);
            $payload['email'] = $normalizedEmail;
            if ($normalizedEmail !== strtolower((string) $user->email)) {
                $payload['email_verified_at'] = null;
            }
        }

        if (array_key_exists('phone', $data)) {
            $normalizedPhone = $data['phone'] !== null ? trim((string) $data['phone']) : null;
            $payload['phone'] = $normalizedPhone !== '' ? $normalizedPhone : null;
            if (($normalizedPhone ?? '') !== (($user->phone ?? '') !== null ? trim((string) $user->phone) : '')) {
                $payload['phone_verified_at'] = null;
            }
        }

        if (array_key_exists('is_available', $data) && $user->role === 'liner') {
            $payload['is_available'] = (bool) $data['is_available'];
        }

        if (array_key_exists('email_notifications_enabled', $data)) {
            $payload['email_notifications_enabled'] = (bool) $data['email_notifications_enabled'];
        }

        if (array_key_exists('language', $data)) {
            $payload['language'] = strtolower((string) ($data['language'] ?? 'fr'));
        }

        if (array_key_exists('region', $data)) {
            $payload['region'] = strtoupper((string) ($data['region'] ?? 'FR'));
        }

        if (array_key_exists('push_notifications_enabled', $data)) {
            $payload['push_notifications_enabled'] = (bool) $data['push_notifications_enabled'];
        }

        if (array_key_exists('sms_notifications_enabled', $data)) {
            $payload['sms_notifications_enabled'] = (bool) $data['sms_notifications_enabled'];
        }

        if (array_key_exists('email_marketing_enabled', $data)) {
            $payload['email_marketing_enabled'] = (bool) $data['email_marketing_enabled'];
        }

        if (isset($data['password']) && $data['password'] !== '') {
            $payload['password'] = $data['password'];
        }

        if (!empty($payload)) {
            $user->update($payload);
        }

        return response()->json($this->userResponsePayload($user));
    }

    public function logout(Request $request): JsonResponse
    {
        /** @var ApiToken|null $token */
        $token = $request->attributes->get('api_token');

        if ($token) {
            $token->delete();
        }

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

    public function deleteMe(Request $request): JsonResponse
    {
        $user = $request->user();

        if ($user->role === 'admin') {
            return response()->json([
                'message' => 'La suppression du compte admin via API n est pas autorisee.',
            ], 403);
        }

        DB::transaction(function () use ($user): void {
            $user->apiTokens()->delete();
            $user->delete();
        });

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

    public function updateLiveLocation(Request $request): JsonResponse
    {
        $user = $request->user();
        abort_unless($user->role === 'liner', 403);

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

        $user->update([
            'current_latitude' => (float) $data['latitude'],
            'current_longitude' => (float) $data['longitude'],
            'current_location_updated_at' => now(),
            'last_seen_at' => now(),
        ]);

        return response()->json([
            'data' => [
                'ok' => true,
                'current_latitude' => (float) $user->fresh()->current_latitude,
                'current_longitude' => (float) $user->fresh()->current_longitude,
            ],
        ]);
    }

    public function forgotPassword(Request $request): JsonResponse
    {
        $data = $request->validate([
            'email' => ['required', 'email'],
        ]);

        Password::broker()->sendResetLink([
            'email' => strtolower((string) $data['email']),
        ]);

        return response()->json([
            'message' => 'Si cet e-mail existe, un lien de reinitialisation a ete envoye.',
        ], 202);
    }

    public function resetPassword(Request $request): JsonResponse
    {
        $data = $request->validate([
            'email' => ['required', 'email'],
            'token' => ['required', 'string'],
            'password' => ['required', 'string', 'min:8', 'confirmed'],
        ]);

        $status = Password::broker()->reset(
            [
                'email' => strtolower((string) $data['email']),
                'token' => $data['token'],
                'password' => $data['password'],
                'password_confirmation' => (string) $request->input('password_confirmation'),
            ],
            function (User $user, string $password): void {
                $user->forceFill([
                    'password' => $password,
                    'remember_token' => Str::random(60),
                ])->save();

                $user->apiTokens()->delete();
            }
        );

        if ($status !== Password::PASSWORD_RESET) {
            return response()->json([
                'message' => 'Jeton ou e-mail invalide pour la reinitialisation du mot de passe.',
            ], 422);
        }

        return response()->json([
            'message' => 'Le mot de passe a ete reinitialise avec succes.',
        ]);
    }

    private function issueToken(User $user, string $name): array
    {
        $plainTextToken = Str::random(64);
        $expiresAt = now()->addDays(30);

        $user->apiTokens()->create([
            'name' => $name,
            'token_hash' => hash('sha256', $plainTextToken),
            'expires_at' => $expiresAt,
        ]);

        return [
            'plain_text_token' => $plainTextToken,
            'expires_at' => $expiresAt,
        ];
    }

    private function firebaseFallbackEmail(string $firebaseUid, string $role): string
    {
        $base = Str::lower($firebaseUid) . '+' . $role . '@firebase.lineupfrance.local';
        $candidate = $base;
        $suffix = 1;

        while (User::query()->where('email', $candidate)->exists()) {
            $candidate = Str::lower($firebaseUid) . '+' . $role . '+' . $suffix . '@firebase.lineupfrance.local';
            $suffix++;
        }

        return $candidate;
    }

    private function userResponsePayload(User $user): array
    {
        $loadedUser = $this->loadUserContext($user);
        $loadedUser->setAttribute('verification', $this->verificationPayload($loadedUser));

        if ($loadedUser->role === 'liner') {
            $loadedUser->setAttribute('liner_stats', $this->linerStatsPayload($loadedUser));
        }

        return ['user' => $loadedUser];
    }

    private function loadUserContext(User $user): User
    {
        return $user->fresh()->load([
            'wallet',
            'paymentMethods' => fn ($query) => $query
                ->where('is_active', true)
                ->orderByDesc('is_default')
                ->latest(),
            'kycDocuments' => fn ($query) => $query
                ->latest('id')
                ->limit(5),
        ]);
    }

    private function verificationPayload(User $user): array
    {
        $latestKyc = $user->kycDocuments->sortByDesc('id')->first();
        $identityStatus = $user->is_kyc_verified
            ? 'approved'
            : (($latestKyc->status ?? null) ?: 'missing');

        return [
            'email' => [
                'value' => $user->email,
                'verified' => $user->email_verified_at !== null,
                'verified_at' => $user->email_verified_at?->toIso8601String(),
            ],
            'phone' => [
                'value' => $user->phone,
                'verified' => $user->phone_verified_at !== null,
                'verified_at' => $user->phone_verified_at?->toIso8601String(),
            ],
            'identity' => [
                'verified' => (bool) $user->is_kyc_verified,
                'status' => $identityStatus,
                'document_type' => $latestKyc->document_type ?? null,
                'notes' => $latestKyc->notes ?? null,
            ],
        ];
    }

    private function linerStatsPayload(User $user): array
    {
        $activeStatuses = ['accepted', 'en_route', 'arrived', 'in_queue', 'client_arrived', 'spot_transmitted'];
        $ratingsQuery = MissionRating::query()->where('liner_id', $user->id);
        $ratingsCount = (clone $ratingsQuery)->count();
        $averageRating = $ratingsCount > 0
            ? round((float) ((clone $ratingsQuery)->avg('stars') ?? 0), 1)
            : null;
        $recentReviews = MissionRating::query()
            ->where('liner_id', $user->id)
            ->whereNotNull('comment')
            ->where('comment', '!=', '')
            ->with('mission:id,title')
            ->latest('id')
            ->limit(3)
            ->get()
            ->map(static function (MissionRating $rating): array {
                return [
                    'id' => $rating->id,
                    'stars' => (int) $rating->stars,
                    'comment' => (string) ($rating->comment ?? ''),
                    'mission_id' => $rating->mission_id,
                    'mission_title' => $rating->mission?->title,
                    'created_at' => $rating->created_at?->toIso8601String(),
                ];
            })
            ->values()
            ->all();
        $totalEarned = round((float) WalletTransaction::query()
            ->where('user_id', $user->id)
            ->where('type', 'credit')
            ->sum('amount'), 2);

        return [
            'completed_missions' => Mission::query()
                ->where('liner_id', $user->id)
                ->where('status', 'completed')
                ->count(),
            'active_missions' => Mission::query()
                ->where('liner_id', $user->id)
                ->whereIn('status', $activeStatuses)
                ->count(),
            'average_rating' => $averageRating,
            'ratings_count' => $ratingsCount,
            'total_earned' => $totalEarned,
            'recent_reviews' => $recentReviews,
        ];
    }
}
