<?php

namespace Tests\Feature\Api\V1;

use App\Enums\TransactionStatus;
use App\Enums\TransactionType;
use App\Enums\UserRole;
use App\Models\Appointment;
use App\Models\Assistant;
use App\Models\AssistantAssignment;
use App\Models\DeviceToken;
use App\Models\Doctor;
use App\Models\DoctorLocationAssignment;
use App\Models\DoctorScheduleException;
use App\Models\DoctorWorkingHour;
use App\Models\Location;
use App\Models\Patient;
use App\Models\PaymentTransaction;
use App\Models\QueueEntry;
use App\Models\User;
use Database\Seeders\ClinicDemoSeeder;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Route;
use Laravel\Sanctum\Sanctum;
use Tests\TestCase;

class RouteCoverageTest extends TestCase
{
    use RefreshDatabase;

    public function test_all_api_routes_respond_without_500(): void
    {
        $this->seed(ClinicDemoSeeder::class);

        // Fetch seeded records or create basic fallback ones
        $admin = User::where('role', UserRole::ADMIN)->firstOrFail();
        $doctorUser = User::where('role', UserRole::DOCTOR)->firstOrFail();
        $assistantUser = User::where('role', UserRole::ASSISTANT)->firstOrFail();

        $patient = Patient::firstOrFail();
        $appointment = Appointment::firstOrFail();
        $entry = QueueEntry::firstOrFail();

        $transaction = PaymentTransaction::first();
        if (! $transaction) {
            $transaction = PaymentTransaction::create([
                'organization_id' => $appointment->organization_id,
                'appointment_id' => $appointment->id,
                'patient_id' => $appointment->patient_id,
                'location_id' => $appointment->location_id,
                'created_by_user_id' => $admin->id,
                'type' => TransactionType::PAYMENT,
                'amount' => 1000,
                'method' => 'cash',
                'status' => TransactionStatus::SUCCEEDED,
                'idempotency_key' => 'coverage-idemp-1',
            ]);
        }

        $device = DeviceToken::first();
        if (! $device) {
            $device = DeviceToken::create([
                'organization_id' => $admin->organization_id,
                'user_id' => $admin->id,
                'device_id' => 'coverage-device',
                'device_name' => 'Coverage iPhone',
                'platform' => 'ios',
                'provider' => 'expo',
                'token' => 'ExponentPushToken[coverage]',
                'is_active' => true,
            ]);
        }

        $assignment = DoctorLocationAssignment::firstOrFail();
        $assistantAssignment = AssistantAssignment::firstOrFail();
        $workingHour = DoctorWorkingHour::firstOrFail();

        $exception = DoctorScheduleException::first();
        if (! $exception) {
            $exception = DoctorScheduleException::create([
                'organization_id' => $assignment->organization_id,
                'doctor_location_assignment_id' => $assignment->id,
                'type' => 'holiday',
                'starts_at' => now()->addDays(5),
                'ends_at' => now()->addDays(5)->addHours(4),
                'is_active' => true,
            ]);
        }

        $doctor = Doctor::firstOrFail();
        $assistant = Assistant::firstOrFail();
        $location = Location::firstOrFail();
        $user = User::firstOrFail();

        // Map parameter keys to values
        $parameterMap = [
            'patient' => $patient->id,
            'appointment' => $appointment->id,
            'entry' => $entry->id,
            'transaction' => $transaction->id,
            'device' => $device->id,
            'assignment' => $assignment->id,
            'doctor' => $doctor->id,
            'assistant' => $assistant->id,
            'location' => $location->id,
            'workingHour' => $workingHour->id,
            'exception' => $exception->id,
            'user' => $user->id,
        ];

        // Gather all API routes
        $routes = Route::getRoutes();
        $testedCount = 0;

        foreach ($routes as $route) {
            $uri = $route->uri();
            if (! str_starts_with($uri, 'api/v1')) {
                continue;
            }

            // Exclude verify email verification link route since it is signed and hard to mock dynamically
            if (str_contains($uri, 'auth/email/verify')) {
                continue;
            }

            $method = collect($route->methods())->first(fn ($m) => $m !== 'HEAD' && $m !== 'OPTIONS');
            if (! $method) {
                continue;
            }

            // Build request URL
            $url = '/'.$uri;
            foreach ($parameterMap as $param => $val) {
                $url = str_replace('{'.$param.'}', $val, $url);
            }

            // Clean up any remaining braces just in case
            if (preg_match('/\{[^}]+\}/', $url)) {
                $url = preg_replace('/\{[^}]+\}/', '1', $url);
            }

            // Set up acting user based on path/role requirements
            if (str_contains($url, '/admin/')) {
                Sanctum::actingAs($admin);
            } else {
                Sanctum::actingAs($assistantUser);
            }

            $data = [];
            if ($method === 'POST' || $method === 'PATCH' || $method === 'PUT') {
                if (str_contains($url, '/auth/login')) {
                    $data = ['email' => 'admin@smartclinic.test', 'password' => 'password'];
                } elseif (str_contains($url, '/auth/forgot-password')) {
                    $data = ['email' => 'admin@smartclinic.test'];
                } elseif (str_contains($url, '/auth/reset-password')) {
                    $data = ['token' => 'dummy', 'email' => 'admin@smartclinic.test', 'password' => 'new-password-123', 'password_confirmation' => 'new-password-123'];
                } elseif (str_contains($url, '/patients') && $method === 'POST') {
                    $data = ['name' => 'New Patient', 'phone' => '+923001234567', 'gender' => 'male', 'date_of_birth' => '1990-01-01'];
                } elseif (str_contains($url, '/appointments') && $method === 'POST') {
                    $data = [
                        'patient_id' => $patient->id,
                        'doctor_id' => $doctor->id,
                        'location_id' => $location->id,
                        'type' => 'consultation',
                        'scheduled_start_at' => now()->addDays(2)->setTime(10, 0)->toIso8601String(),
                        'base_fee' => 2000,
                    ];
                } elseif (str_contains($url, '/queue/check-in')) {
                    $data = ['appointment_id' => $appointment->id];
                } elseif (str_contains($url, '/queue/call-next')) {
                    $data = ['location_id' => $location->id, 'doctor_id' => $doctor->id];
                } elseif (str_contains($url, '/payments')) {
                    $data = ['amount' => 500, 'method' => 'cash', 'idempotency_key' => uniqid('pt-')];
                }
            }

            $response = $this->json($method, $url, $data);

            echo sprintf(
                "[%s] %s -> Status: %d\n",
                $method,
                $url,
                $response->status()
            );

            $this->assertNotEquals(
                500,
                $response->status(),
                "Route [{$method}] {$url} returned a 500 error: ".$response->getContent()
            );

            $testedCount++;
        }

        echo "Successfully tested {$testedCount} API endpoints one by one!\n";
    }
}
