<?php

namespace App\Modules\VehiclesInVideo\Services;

use App\Mail\VehiclesInVideoTokenExpiringMail;
use App\Models\ExternalApiToken;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Mail;

class TokenService
{
    public const PROVIDER = 'vehicles-in-video';

    public function __construct(private readonly VehiclesInVideoApiService $api)
    {
    }

    public function requestGrantUrl(): array
    {
        $callbackUrl = route('tenant.notifications.vehicles-in-video.store', tenant('id'));
        $result = $this->api->requestGrantUrl($callbackUrl);

        if (!empty($result['error'])) {
            Log::error('VehiclesInVideo: Grant URL request failed', $result);
            return [
                'success' => false,
                'message' => $result['error']['message'] ?? 'Failed to request grant URL.',
            ];
        }

        if (empty($result['token_grant_url'])) {
            Log::error('VehiclesInVideo: No grant URL in response', $result);
            return ['success' => false, 'message' => 'No grant URL received from API.'];
        }

        $this->providerQuery()->delete();

        $token = ExternalApiToken::create([
            'provider' => self::PROVIDER,
            'grant_url' => $result['token_grant_url'],
        ]);

        return [
            'success' => true,
            'message' => 'Grant URL generated.',
            'grant_url' => $token->grant_url,
            'token_id' => $token->id,
        ];
    }

    public function getValidAuthToken(): ?string
    {
        $token = $this->latestToken();

        if (!$token || !$token->refresh_token) {
            return null;
        }

        if ($this->needsRefresh($token)) {
            Log::info('VehiclesInVideo: Auth token expired or expiring soon, attempting refresh');
            $result = $this->refreshToken();

            if (!$result['success']) {
                Log::error('VehiclesInVideo: Auto-refresh failed', ['message' => $result['message']]);
                return null;
            }

            $token->refresh();
        }

        return $token->auth_token;
    }

    public function refreshToken(): array
    {
        $token = $this->latestToken();

        if (!$token || !$token->refresh_token) {
            return ['success' => false, 'message' => 'No connected token found to refresh.'];
        }

        $result = $this->api->refreshToken($token->refresh_token);

        if (!empty($result['error'])) {
            Log::error('VehiclesInVideo: Token refresh failed', $result);
            return [
                'success' => false,
                'message' => $result['error']['message'] ?? 'Failed to refresh token.',
            ];
        }

        if (empty($result['auth_token']) || empty($result['refresh_token'])) {
            Log::error('VehiclesInVideo: Incomplete refresh response', $result);
            return ['success' => false, 'message' => 'Incomplete response from API.'];
        }

        $token->update([
            'auth_token' => $result['auth_token'],
            'refresh_token' => $result['refresh_token'],
            'auth_expires_at' => $this->expiryFromSeconds($result['auth_token_expiry_secs'] ?? null, 86400),
            'refresh_expires_at' => $this->expiryFromSeconds(
                $result['refresh_token_expiry_secs'] ?? null,
                180 * 86400
            ),
        ]);

        return ['success' => true, 'message' => 'Token refreshed successfully.'];
    }

    public function checkTokenExpiry(): array
    {
        $token = $this->latestToken();

        if (!$token || !$token->refresh_token) {
            return ['status' => 'info', 'message' => 'No connected token found.'];
        }

        if (!$token->refresh_expires_at) {
            return ['status' => 'warn', 'message' => 'Connected token has no refresh expiry date set.'];
        }

        $daysLeft = (int) now()->diffInDays($token->refresh_expires_at, false);

        if ($daysLeft > 30) {
            return [
                'status' => 'info',
                'message' => "Refresh token expires in {$daysLeft} days. No action needed.",
            ];
        }

        $this->sendExpiryNotification($daysLeft);

        if ($daysLeft <= 0) {
            return [
                'status' => 'error',
                'message' => 'Refresh token has already expired. Notification sent.',
            ];
        }

        return [
            'status' => 'warn',
            'message' => "Refresh token expires in {$daysLeft} days. Notification sent.",
        ];
    }

    public function handleWebhook(array $payload): array
    {
        $authToken = $payload['auth_token'] ?? null;
        $refreshToken = $payload['refresh_token'] ?? null;

        if (empty($authToken) || empty($refreshToken)) {
            Log::warning('VehiclesInVideo: Webhook missing tokens', $payload);
            return ['success' => false, 'message' => 'Missing tokens', 'status_code' => 400];
        }

        $token = $this->latestToken() ?? new ExternalApiToken(['provider' => self::PROVIDER]);

        $token->fill([
            'provider' => self::PROVIDER,
            'auth_token' => $authToken,
            'refresh_token' => $refreshToken,
            'auth_expires_at' => $this->expiryFromSeconds($payload['auth_token_expiry_secs'] ?? null, 86400),
            'refresh_expires_at' => $this->expiryFromSeconds(
                $payload['refresh_token_expiry_secs'] ?? null,
                180 * 86400
            ),
        ]);

        $token->save();

        $this->providerQuery()->where('id', '!=', $token->id)->delete();

        Log::info('VehiclesInVideo: Tokens stored', ['token_id' => $token->id]);

        return ['success' => true, 'message' => 'Tokens received', 'status_code' => 200];
    }

    public function deleteAllTokens(): array
    {
        $count = $this->providerQuery()->count();
        if ($count === 0) {
            return ['success' => false, 'message' => 'No tokens to delete.'];
        }

        $this->providerQuery()->delete();

        return ['success' => true, 'message' => "All tokens deleted ({$count} removed)."];
    }

    public function latestToken(): ?ExternalApiToken
    {
        return $this->providerQuery()->latest()->first();
    }

    private function providerQuery()
    {
        return ExternalApiToken::query()->where('provider', self::PROVIDER);
    }

    private function needsRefresh(ExternalApiToken $token): bool
    {
        if (!$token->auth_expires_at) {
            return true;
        }

        return $token->auth_expires_at->isPast()
            || $token->auth_expires_at->diffInMinutes(now(), false) > -10;
    }

    private function expiryFromSeconds(?int $seconds, int $fallback): Carbon
    {
        return now()->addSeconds($seconds ?: $fallback);
    }

    private function sendExpiryNotification(int $daysLeft): void
    {
        $recipients = $this->notificationRecipients();
        if (empty($recipients)) {
            Log::warning('VehiclesInVideo: No notification recipients configured for token expiry alert');
            return;
        }

        try {
            Mail::to($recipients)->send(new VehiclesInVideoTokenExpiringMail($daysLeft));
        } catch (\Throwable $e) {
            Log::error('VehiclesInVideo: Failed to send token expiry notification', [
                'exception' => $e->getMessage(),
            ]);
        }
    }

    private function notificationRecipients(): array
    {
        $value = \App\Facades\Settings::get('vehicles-in-video-notification-recipients');
        if (empty($value)) {
            return [];
        }

        return array_values(array_filter(array_map('trim', explode(',', $value))));
    }
}
