<?php

namespace Tests\Feature\Api\V1;

use App\Enums\UserRole;
use App\Enums\UserStatus;
use App\Models\Appointment;
use App\Models\DeviceToken;
use App\Models\NotificationLog;
use App\Models\NotificationPreference;
use App\Models\User;
use App\Services\NotificationService;
use Database\Seeders\ClinicDemoSeeder;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Http;
use Tests\TestCase;

class NotificationDeliveryTest extends TestCase
{
    use RefreshDatabase;

    public function test_whatsapp_without_recorded_consent_is_skipped_without_provider_call(): void
    {
        $this->seed(ClinicDemoSeeder::class);
        $appointment = Appointment::query()->with('patient')->whereHas('patient', fn ($q) => $q->where('has_whatsapp', true))->firstOrFail();
        config(['clinic.whatsapp.enabled' => true]);
        Http::fake();

        NotificationPreference::create([
            'organization_id' => $appointment->organization_id,
            'patient_id' => $appointment->patient_id,
            'whatsapp_enabled' => true,
            'push_enabled' => true,
            'email_enabled' => true,
            'in_app_enabled' => true,
            'consent_at' => null,
        ]);

        $log = NotificationLog::create([
            'organization_id' => $appointment->organization_id,
            'appointment_id' => $appointment->id,
            'patient_id' => $appointment->patient_id,
            'channel' => 'whatsapp',
            'type' => 'reminder_test',
            'idempotency_key' => 'whatsapp-consent-test',
            'status' => 'queued',
            'scheduled_at' => now(),
        ]);

        app(NotificationService::class)->send($log);

        $this->assertSame('skipped', $log->fresh()->status);
        $this->assertSame(1, $log->fresh()->attempts);
        Http::assertNothingSent();
    }

    public function test_failed_push_delivery_is_logged_and_can_be_retried_to_success(): void
    {
        $this->seed(ClinicDemoSeeder::class);
        $appointment = Appointment::query()->with('patient')->firstOrFail();
        $patient = $appointment->patient;

        $patientUser = User::create([
            'organization_id' => $appointment->organization_id,
            'name' => $patient->name,
            'email' => 'push.patient@smartclinic.test',
            'phone' => $patient->phone,
            'password' => Hash::make('password'),
            'role' => UserRole::PATIENT,
            'status' => UserStatus::ACTIVE,
            'email_verified_at' => now(),
        ]);
        $patient->update(['user_id' => $patientUser->id]);

        DeviceToken::create([
            'organization_id' => $appointment->organization_id,
            'user_id' => $patientUser->id,
            'device_id' => 'test-device',
            'device_name' => 'Test Phone',
            'platform' => 'ios',
            'provider' => 'expo',
            'token' => 'ExponentPushToken[test]',
            'is_active' => true,
        ]);

        NotificationPreference::create([
            'organization_id' => $appointment->organization_id,
            'user_id' => $patientUser->id,
            'patient_id' => $patient->id,
            'push_enabled' => true,
            'whatsapp_enabled' => false,
            'email_enabled' => false,
            'in_app_enabled' => true,
            'consent_at' => now(),
        ]);

        $log = NotificationLog::create([
            'organization_id' => $appointment->organization_id,
            'appointment_id' => $appointment->id,
            'patient_id' => $appointment->patient_id,
            'channel' => 'push',
            'type' => 'reminder_test',
            'idempotency_key' => 'push-retry-test',
            'status' => 'queued',
            'scheduled_at' => now(),
        ]);

        config(['clinic.expo.enabled' => true, 'clinic.expo.url' => 'https://expo.test/send']);
        Http::fake([
            'https://expo.test/send' => Http::sequence()
                ->push(['message' => 'temporary'], 503)
                ->push(['data' => ['status' => 'ok', 'id' => 'ticket-123']], 200),
        ]);

        try {
            app(NotificationService::class)->send($log);
            $this->fail('A temporary provider failure should throw so the queue can retry the job.');
        } catch (\Throwable) {
            // Expected: provider failures are persisted and re-thrown for Laravel queue retry handling.
        }

        $this->assertSame('failed', $log->fresh()->status);
        $this->assertSame(1, $log->fresh()->attempts);

        app(NotificationService::class)->send($log->fresh());

        $this->assertSame('sent', $log->fresh()->status);
        $this->assertSame(2, $log->fresh()->attempts);
        $this->assertSame('ticket-123', $log->fresh()->provider_message_id);
    }
}
