<?php

namespace App\Http\Controllers\Api;

use App\Enums\ResponseStatus;
use App\Http\Controllers\Controller;
use App\Http\Requests\VehicleEnquiryRequest;
use App\Models\ApiRequest;
use App\Models\VehicleDataSource;
use App\Models\VehicleRecord;
use App\Models\Website;
use App\Services\DvlaService;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\DB;

class VehicleEnquiryController extends Controller
{
    public function __construct(
        private readonly DvlaService $dvlaService
    ) {
    }

    public function __invoke(VehicleEnquiryRequest $request): JsonResponse
    {
        $startTime = microtime(true);
        $website = $request->user();
        $registrationNumber = strtoupper(str_replace(' ', '', $request->validated('registration_number')));
        $contactData = $request->validated('contact_data');

        $vehicleRecord = VehicleRecord::where('registration_number', $registrationNumber)->first();

        if ($vehicleRecord) {
            $this->logRequest(
                $website,
                $registrationNumber,
                $request->ip(),
                $contactData,
                ResponseStatus::CacheHit,
                $this->buildMetadata($request, $startTime)
            );
            $this->incrementCacheHitRateLimit($request);

            return response()->json([
                'success' => true,
                'data' => $this->filterByTier($vehicleRecord->data, $website),
            ]);
        }

        if (!$this->checkExternalApiRateLimit($website)) {
            return response()->json([
                'message' => 'External API rate limit exceeded',
                'limit' => $website->external_api_rate_limit,
                'reset_at' => now()->addHour()->startOfHour()->timestamp,
            ], 429);
        }

        try {
            $dvlaData = $this->dvlaService->getVehicleDetails($registrationNumber);

            if (!$dvlaData) {
                $this->logRequest(
                    $website,
                    $registrationNumber,
                    $request->ip(),
                    $contactData,
                    ResponseStatus::NotFound,
                    $this->buildMetadata($request, $startTime)
                );
                $this->incrementExternalApiRateLimit($website);

                return response()->json([
                    'message' => 'Vehicle not found',
                ], 404);
            }

            $vehicleRecord = DB::transaction(function () use ($registrationNumber, $dvlaData) {
                $vehicle = VehicleRecord::create([
                    'registration_number' => $registrationNumber,
                    'data' => $dvlaData,
                ]);

                VehicleDataSource::create([
                    'vehicle_record_id' => $vehicle->id,
                    'source_name' => 'dvla',
                    'source_url' => DvlaService::class.'::API_URL',
                    'last_fetched_at' => now(),
                    'cache_expires_at' => now()->addMonths(6),
                ]);

                return $vehicle;
            });

            $this->logRequest(
                $website,
                $registrationNumber,
                $request->ip(),
                $contactData,
                ResponseStatus::ApiFetched,
                $this->buildMetadata($request, $startTime)
            );
            $this->incrementExternalApiRateLimit($website);

            return response()->json([
                'success' => true,
                'data' => $this->filterByTier($vehicleRecord->data, $website),
            ]);
        } catch (\Exception $e) {
            $this->logRequest(
                $website,
                $registrationNumber,
                $request->ip(),
                $contactData,
                ResponseStatus::Error,
                $this->buildMetadata($request, $startTime, $e->getMessage())
            );

            return response()->json([
                'message' => 'Failed to fetch vehicle details',
            ], 500);
        }
    }

    private function logRequest(Website $website, string $registrationNumber, string $ipAddress, ?array $contactData, ResponseStatus $status, array $metadata = []): void
    {
        ApiRequest::create([
            'website_id' => $website->id,
            'registration_number' => $registrationNumber,
            'ip_address' => $ipAddress,
            'contact_data' => $contactData,
            'response_status' => $status,
            'metadata' => $metadata,
            'created_at' => now(),
        ]);
    }

    private function buildMetadata($request, float $startTime, ?string $errorMessage = null): array
    {
        $responseTime = round((microtime(true) - $startTime) * 1000, 2);

        $metadata = [
            'user_agent' => $request->userAgent(),
            'response_time_ms' => $responseTime,
            'referer' => $request->header('referer'),
            'accept' => $request->header('accept'),
        ];

        if ($errorMessage) {
            $metadata['error_message'] = $errorMessage;
        }

        return $metadata;
    }

    private function incrementCacheHitRateLimit($request): void
    {
        $cacheKey = $request->attributes->get('rate_limit_key');

        if ($cacheKey) {
            $value = Cache::increment($cacheKey);
            Cache::put($cacheKey, $value, 3600);
        }
    }

    private function checkExternalApiRateLimit(Website $website): bool
    {
        if (app()->environment('local') || $website->bypass_rate_limit) {
            return true;
        }

        $hour = now()->format('Y-m-d-H');
        $cacheKey = "rate_limit:website:{$website->id}:external_api:{$hour}";
        $attempts = Cache::get($cacheKey, 0);

        return $attempts < $website->external_api_rate_limit;
    }

    private function incrementExternalApiRateLimit(Website $website): void
    {
        if (app()->environment('local') || $website->bypass_rate_limit) {
            return;
        }

        $hour = now()->format('Y-m-d-H');
        $cacheKey = "rate_limit:website:{$website->id}:external_api:{$hour}";

        $value = Cache::increment($cacheKey);
        Cache::put($cacheKey, $value, 3600);
    }

    private function filterByTier(mixed $data, Website $website): array
    {
        return $website->tier->filterFields((array) $data);
    }
}
