<?php

namespace Tests\Unit\Services;

use App\Models\NotificationLog;
use App\Models\Setting;
use App\Services\NotificationService;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use Tests\Feature\Concerns\BuildsFeatureData;
use Tests\TestCase;

class NotificationServiceTest extends TestCase
{
    use BuildsFeatureData;
    use DatabaseTransactions;

    public function test_get_message_for_stage_passed_returns_expected_structure(): void
    {
        $service = app(NotificationService::class);

        $applicant = $this->createApplicant();
        $job = $this->createJob([
            'title' => 'Backend Engineer',
        ]);
        $this->createStage($job, [
            'stage_number' => 1,
            'name' => 'Tes Administrasi',
            'sort_order' => 1,
        ]);

        $application = $this->createJobApplication($applicant, $job, [
            'status' => 'applied',
        ]);

        $result = $service->getMessageForStatus('stage1_passed', $application);

        $this->assertSame('stage1_passed', $result['type']);
        $this->assertArrayHasKey('subject', $result);
        $this->assertArrayHasKey('message', $result);
        $this->assertArrayHasKey('wa_message', $result);
        $this->assertStringContainsString('Backend Engineer', $result['subject'] . ' ' . $result['message']);
        $this->assertStringContainsString('Tes Administrasi', $result['subject'] . ' ' . $result['message'] . ' ' . $result['wa_message']);
    }

    public function test_schedule_next_stage_notification_from_applied_creates_stage1_logs(): void
    {
        $service = app(NotificationService::class);

        Setting::set('stage_advance_days', '2', 'notification', 'Interval Antar Tahap');

        $applicant = $this->createApplicant();
        $job = $this->createJob();
        $this->createStage($job, [
            'stage_number' => 1,
            'name' => 'Tahap 1',
            'sort_order' => 1,
        ]);
        $this->createStage($job, [
            'stage_number' => 2,
            'name' => 'Tahap 2',
            'sort_order' => 2,
        ]);

        $application = $this->createJobApplication($applicant, $job, [
            'status' => 'applied',
        ]);

        $service->scheduleNextStageNotification($application);

        $logs = NotificationLog::where('application_id', $application->id)
            ->where('type', 'stage1_passed')
            ->where('status', 'scheduled')
            ->get();

        $this->assertCount(2, $logs);
        $this->assertTrue($logs->pluck('channel')->contains('email'));
        $this->assertTrue($logs->pluck('channel')->contains('whatsapp'));
    }

    public function test_schedule_next_stage_notification_after_last_form_filled_uses_final_decision_setting(): void
    {
        $service = app(NotificationService::class);

        Setting::set('final_auto_decision', 'accepted', 'notification', 'Keputusan Akhir Otomatis');

        $applicant = $this->createApplicant();
        $job = $this->createJob();
        $this->createStage($job, [
            'stage_number' => 1,
            'name' => 'Tahap Final',
            'sort_order' => 1,
        ]);

        $application = $this->createJobApplication($applicant, $job, [
            'status' => 'stage1_form_filled',
            'current_stage' => 1,
        ]);

        $service->scheduleNextStageNotification($application);

        $this->assertCount(2, NotificationLog::where('application_id', $application->id)
            ->where('type', 'accepted')
            ->where('status', 'scheduled')
            ->get());
    }

    public function test_advance_status_for_stage_and_final_notification_updates_application(): void
    {
        $service = app(NotificationService::class);

        $applicant = $this->createApplicant();
        $job = $this->createJob();

        $application = $this->createJobApplication($applicant, $job, [
            'status' => 'applied',
            'current_stage' => 0,
        ]);

        $stageLog = $this->createNotificationLog($application, [
            'type' => 'stage1_passed',
            'channel' => 'email',
            'status' => 'sent',
            'scheduled_at' => now()->subMinute(),
        ]);

        $service->advanceStatusForNotification($stageLog);

        $application->refresh();
        $this->assertSame('stage1_passed', $application->status);
        $this->assertSame(1, $application->current_stage);

        $finalLog = $this->createNotificationLog($application, [
            'type' => 'rejected',
            'channel' => 'email',
            'status' => 'sent',
            'scheduled_at' => now()->subMinute(),
        ]);

        $service->advanceStatusForNotification($finalLog);

        $application->refresh();
        $this->assertSame('rejected', $application->status);
        $this->assertNotNull($application->final_decision_at);
    }

    public function test_resync_notifications_for_job_cancels_old_entries_and_schedules_next_step(): void
    {
        $service = app(NotificationService::class);

        $applicant = $this->createApplicant();
        $job = $this->createJob();
        $this->createStage($job, [
            'stage_number' => 1,
            'name' => 'Tahap 1',
            'sort_order' => 1,
        ]);
        $this->createStage($job, [
            'stage_number' => 2,
            'name' => 'Tahap 2',
            'sort_order' => 2,
        ]);

        $application = $this->createJobApplication($applicant, $job, [
            'status' => 'applied',
        ]);

        $oldLog = $this->createNotificationLog($application, [
            'type' => 'stage2_passed',
            'status' => 'scheduled',
            'scheduled_at' => now()->addHour(),
        ]);

        $synced = $service->resyncNotificationsForJob($job);

        $this->assertSame(1, $synced);

        $oldLog->refresh();
        $this->assertSame('cancelled', $oldLog->status);

        $newLogs = NotificationLog::where('application_id', $application->id)
            ->where('type', 'stage1_passed')
            ->where('status', 'scheduled')
            ->get();

        $this->assertCount(2, $newLogs);
    }
}
