<?php

namespace Tests\Feature;

use App\Models\Business;
use App\Models\Setting;
use App\Models\User;
use Database\Seeders\DatabaseSeeder;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Crypt;
use Illuminate\Support\Facades\Http;
use Tests\TestCase;

class SmsSettingsTest extends TestCase
{
    use RefreshDatabase;

    protected function setUp(): void
    {
        parent::setUp();
        $this->seed(DatabaseSeeder::class);
    }

    public function test_system_settings_exposes_sms_gateway_configuration_and_test_sender(): void
    {
        $admin = User::where('username', 'admin')->firstOrFail();

        $this->actingAs($admin)
            ->get(route('admin.system-settings.edit'))
            ->assertOk()
            ->assertSee('SMS Notifications')
            ->assertSee('API URL')
            ->assertSee('Sender ID')
            ->assertSee('API Key')
            ->assertSee('Enable SMS notifications')
            ->assertSee('Send Test SMS')
            ->assertSee('Test phone number')
            ->assertSee('Test message')
            ->assertSee('https://messaging-service.co.tz/api/sms/v1/text/single');
    }

    public function test_sms_api_key_is_encrypted_and_never_rendered_back_to_the_form(): void
    {
        $admin = User::where('username', 'admin')->firstOrFail();
        $business = Business::firstOrFail();
        $plainApiKey = 'sms-test-api-key-very-secret';

        $this->actingAs($admin)
            ->put(route('admin.system-settings.update'), [
                'timezone' => 'Africa/Dar_es_Salaam',
                'date_format' => 'd/m/Y',
                'session_timeout_minutes' => 120,
                'sms_settings_present' => 1,
                'sms_enabled' => 1,
                'sms_api_url' => 'https://sms.example.test/api/messages',
                'sms_sender_id' => 'MICROFIN',
                'sms_api_key' => $plainApiKey,
                'sms_timeout_seconds' => 15,
            ])
            ->assertSessionHasNoErrors()
            ->assertRedirect();

        $storedApiKey = Setting::query()
            ->where('business_id', $business->id)
            ->where('group', 'sms')
            ->where('key', 'sms_api_key')
            ->firstOrFail();

        $this->assertSame('encrypted', $storedApiKey->type);
        $this->assertNotSame($plainApiKey, $storedApiKey->value);
        $this->assertSame($plainApiKey, Crypt::decryptString($storedApiKey->value));

        $this->assertDatabaseHas('settings', [
            'business_id' => $business->id,
            'group' => 'sms',
            'key' => 'sms_enabled',
            'value' => '1',
        ]);
        $this->assertDatabaseHas('settings', [
            'business_id' => $business->id,
            'group' => 'sms',
            'key' => 'sms_api_url',
            'value' => 'https://sms.example.test/api/messages',
        ]);
        $this->assertDatabaseHas('settings', [
            'business_id' => $business->id,
            'group' => 'sms',
            'key' => 'sms_sender_id',
            'value' => 'MICROFIN',
        ]);

        $this->actingAs($admin)
            ->get(route('admin.system-settings.edit'))
            ->assertOk()
            ->assertSee('API Key saved.')
            ->assertDontSee($plainApiKey);
    }

    public function test_blank_api_key_keeps_the_existing_encrypted_credential(): void
    {
        $admin = User::where('username', 'admin')->firstOrFail();
        $business = Business::firstOrFail();
        $encryptedApiKey = Crypt::encryptString('existing-api-key');

        Setting::create([
            'business_id' => $business->id,
            'group' => 'sms',
            'key' => 'sms_api_key',
            'value' => $encryptedApiKey,
            'type' => 'encrypted',
        ]);

        $this->actingAs($admin)
            ->put(route('admin.system-settings.update'), [
                'timezone' => 'Africa/Dar_es_Salaam',
                'date_format' => 'd/m/Y',
                'session_timeout_minutes' => 120,
                'sms_settings_present' => 1,
                'sms_enabled' => 1,
                'sms_api_url' => 'https://sms.example.test/api/messages',
                'sms_sender_id' => 'MICROFIN',
                'sms_api_key' => '',
                'sms_timeout_seconds' => 20,
            ])
            ->assertSessionHasNoErrors();

        $this->assertSame(
            $encryptedApiKey,
            Setting::query()
                ->where('business_id', $business->id)
                ->where('group', 'sms')
                ->where('key', 'sms_api_key')
                ->value('value'),
        );
    }

    public function test_admin_can_send_a_real_test_message_with_saved_sms_credentials(): void
    {
        $admin = User::where('username', 'admin')->firstOrFail();
        $business = Business::firstOrFail();
        $this->configureSms($business, enabled: false);

        Http::fake([
            'https://sms.example.test/api/messages' => Http::response([
                'messages' => [[
                    'to' => '255756123456',
                    'status' => [
                        'groupName' => 'PENDING',
                        'name' => 'PENDING_ENROUTE',
                    ],
                    'messageId' => 347669944827117568,
                ]],
            ], 200),
        ]);

        $this->actingAs($admin)
            ->post(route('admin.system-settings.sms.test'), [
                'sms_test_phone' => '0756 123 456',
                'sms_test_message' => 'Microfinance SMS gateway test.',
            ])
            ->assertSessionHasNoErrors()
            ->assertSessionHas(
                'sms_test_success',
                'Your test message was sent successfully to 255756123456. The SMS provider accepted it for delivery.',
            );

        $flash = (string) session('sms_test_success');
        $this->assertStringNotContainsString('HTTP 200', $flash);
        $this->assertStringNotContainsString('PENDING_ENROUTE', $flash);
        $this->assertStringNotContainsString('messageId', $flash);

        Http::assertSent(function ($request): bool {
            return $request->method() === 'POST'
                && $request->url() === 'https://sms.example.test/api/messages'
                && $request->hasHeader('Authorization', 'Basic nextsms-api-key')
                && $request['from'] === 'MICROFIN'
                && $request['to'] === '255756123456'
                && $request['text'] === 'Microfinance SMS gateway test.';
        });

        $this->assertDatabaseHas('audit_logs', [
            'action' => 'sms.test.sent',
            'auditable_type' => 'business',
            'auditable_id' => $business->id,
        ]);
    }

    public function test_nextsms_base_url_is_resolved_to_the_single_message_endpoint(): void
    {
        $admin = User::where('username', 'admin')->firstOrFail();
        $business = Business::firstOrFail();
        $this->configureSms($business);

        Setting::query()
            ->where('business_id', $business->id)
            ->where('group', 'sms')
            ->where('key', 'sms_api_url')
            ->update(['value' => 'https://messaging-service.co.tz']);

        Http::fake([
            'https://messaging-service.co.tz/api/sms/v1/text/single' => Http::response([
                'success' => true,
                'message' => 'Message accepted',
            ], 200),
        ]);

        $this->actingAs($admin)
            ->post(route('admin.system-settings.sms.test'), [
                'sms_test_phone' => '0756123456',
                'sms_test_message' => 'NextSMS endpoint test.',
            ])
            ->assertSessionHasNoErrors()
            ->assertSessionHas('sms_test_success');

        Http::assertSent(function ($request): bool {
            return $request->method() === 'POST'
                && $request->url() === 'https://messaging-service.co.tz/api/sms/v1/text/single'
                && $request->hasHeader('Authorization', 'Basic nextsms-api-key')
                && $request['from'] === 'MICROFIN'
                && $request['to'] === '255756123456'
                && $request['text'] === 'NextSMS endpoint test.';
        });
    }

    public function test_failed_test_message_shows_provider_status_without_exposing_api_key(): void
    {
        $admin = User::where('username', 'admin')->firstOrFail();
        $business = Business::firstOrFail();
        $this->configureSms($business);

        Http::fake([
            'https://sms.example.test/api/messages' => Http::response([
                'message' => 'Unauthorized',
            ], 401),
        ]);

        $this->actingAs($admin)
            ->post(route('admin.system-settings.sms.test'), [
                'sms_test_phone' => '255756123456',
                'sms_test_message' => 'Credential test',
            ])
            ->assertSessionHasNoErrors()
            ->assertSessionHas('sms_test_error');

        $flash = (string) session('sms_test_error');
        $this->assertStringContainsString('HTTP 401', $flash);
        $this->assertStringContainsString('Unauthorized', $flash);
        $this->assertStringNotContainsString('nextsms-api-key', $flash);
    }

    public function test_test_message_requires_a_valid_tanzanian_phone_number(): void
    {
        $admin = User::where('username', 'admin')->firstOrFail();
        $business = Business::firstOrFail();
        $this->configureSms($business);

        Http::fake();

        $this->actingAs($admin)
            ->post(route('admin.system-settings.sms.test'), [
                'sms_test_phone' => '12345',
                'sms_test_message' => 'Credential test',
            ])
            ->assertSessionHas('sms_test_error');

        Http::assertNothingSent();
    }

    private function configureSms(Business $business, bool $enabled = true): void
    {
        foreach ([
            'sms_enabled' => [$enabled ? '1' : '0', 'boolean'],
            'sms_api_url' => ['https://sms.example.test/api/messages', 'string'],
            'sms_sender_id' => ['MICROFIN', 'string'],
            'sms_api_key' => [Crypt::encryptString('nextsms-api-key'), 'encrypted'],
            'sms_timeout_seconds' => ['15', 'integer'],
        ] as $key => [$value, $type]) {
            Setting::updateOrCreate(
                ['business_id' => $business->id, 'group' => 'sms', 'key' => $key],
                ['value' => $value, 'type' => $type],
            );
        }
    }
}
