<?php

namespace Tests\Feature;

use App\Models\Business;
use App\Models\Customer;
use App\Models\Loan;
use App\Models\LoanProduct;
use App\Models\ReminderNotificationLog;
use App\Models\Setting;
use App\Models\User;
use App\Services\LoanReminderService;
use App\Services\ModuleManager;
use Database\Seeders\DatabaseSeeder;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Http;
use Illuminate\Validation\ValidationException;
use Tests\TestCase;

class MessagingModuleToggleTest extends TestCase
{
    use RefreshDatabase;

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

    public function test_sms_and_whatsapp_are_available_as_system_module_switches(): void
    {
        $admin=User::where('username','admin')->firstOrFail();
        $this->actingAs($admin)->get(route('admin.system-settings.edit'))
            ->assertOk()->assertSee('SMS Messaging')->assertSee('WhatsApp Messaging');

        $enabled=[];
        foreach(array_keys(ModuleManager::MODULES) as $key){
            if(!in_array($key,['sms_messaging','whatsapp_messaging'],true)) $enabled[$key]=1;
        }

        $this->actingAs($admin)->put(route('admin.system-settings.update'),[
            'timezone'=>'Africa/Dar_es_Salaam','date_format'=>'d/m/Y','session_timeout_minutes'=>120,
            'modules_present'=>1,'modules'=>$enabled,
        ])->assertRedirect()->assertSessionHasNoErrors();

        $business=Business::firstOrFail();
        $this->assertSame('0',Setting::where('business_id',$business->id)->where('group','modules')->where('key','sms_messaging')->value('value'));
        $this->assertSame('0',Setting::where('business_id',$business->id)->where('group','modules')->where('key','whatsapp_messaging')->value('value'));

        $this->actingAs($admin)->get(route('notification-settings.edit'))
            ->assertOk()
            ->assertSee('SMS Messaging is currently disabled')
            ->assertSee('WhatsApp Messaging is currently disabled')
            ->assertDontSee('name="reminder_channels[]" value="sms"',false)
            ->assertDontSee('name="reminder_channels[]" value="whatsapp"',false);
    }

    public function test_whatsapp_provider_configuration_is_encrypted_and_used_for_delivery(): void
    {
        $admin=User::where('username','admin')->firstOrFail();
        $business=Business::firstOrFail();
        $endpoint='https://whatsapp.example.test/send';
        $token='very-secret-whatsapp-token';

        $this->actingAs($admin)->put(route('notification-settings.update'),[
            'reminders_enabled'=>1,'reminder_pre_due_days'=>'1','reminder_overdue_frequency'=>'daily',
            'reminder_channels'=>['whatsapp'],'whatsapp_api_endpoint'=>$endpoint,'whatsapp_api_token'=>$token,
        ])->assertRedirect()->assertSessionHasNoErrors();

        $business->refresh();
        $this->assertSame($endpoint,$business->whatsapp_api_endpoint);
        $this->assertSame($token,$business->whatsapp_api_token);
        $raw=(string)DB::table('businesses')->where('id',$business->id)->value('whatsapp_api_token');
        $this->assertNotSame($token,$raw);
        $this->assertStringNotContainsString($token,$raw);

        $audit=DB::table('audit_logs')->where('action','notifications.settings.updated')->latest('id')->first();
        $this->assertNotNull($audit);
        $this->assertStringNotContainsString($token,(string)$audit->after);

        $loan=$this->loanFixture('2026-08-20','2026-08-21');
        Http::fake([$endpoint=>Http::response(['message_id'=>'wa-1001'],200)]);
        $this->artisan('notifications:dispatch-reminders',['--date'=>'2026-08-20'])->assertSuccessful();

        $log=ReminderNotificationLog::where('channel','whatsapp')->firstOrFail();
        $this->assertSame('delivered',$log->status);
        Http::assertSent(fn($request)=>$request->url()===$endpoint
            && $request->hasHeader('Authorization','Bearer '.$token)
            && $request['to']===$loan->customer->phone
            && $request['message']===$log->message
            && $request['loan_id']===$loan->id);
    }

    public function test_disabled_messaging_modules_stop_new_sends_and_retries_without_deleting_history(): void
    {
        $business=Business::firstOrFail();
        $business->update([
            'reminders_enabled'=>true,'reminder_pre_due_days'=>[1],'reminder_due_date_enabled'=>false,
            'reminder_overdue_enabled'=>false,'reminder_channels'=>['sms','whatsapp'],
            'nextsms_api_key'=>'key','nextsms_sender_id'=>'TEST','whatsapp_api_endpoint'=>'https://whatsapp.example.test/send',
        ]);
        $loan=$this->loanFixture('2026-08-20','2026-08-21');

        Setting::updateOrCreate(['business_id'=>$business->id,'group'=>'modules','key'=>'sms_messaging'],['value'=>'0','type'=>'boolean']);
        Setting::updateOrCreate(['business_id'=>$business->id,'group'=>'modules','key'=>'whatsapp_messaging'],['value'=>'0','type'=>'boolean']);
        Http::fake();

        $this->artisan('notifications:dispatch-reminders',['--date'=>'2026-08-20'])->assertSuccessful();
        $this->assertSame(0,ReminderNotificationLog::whereIn('channel',['sms','whatsapp'])->count());
        Http::assertNothingSent();

        [$installmentId,$recipient]=$this->inBranch((int)$loan->branch_id, fn () => [
            $loan->installments()->firstOrFail()->id,
            $loan->customer()->firstOrFail()->phone,
        ]);
        $failed=ReminderNotificationLog::create([
            'business_id'=>$business->id,'branch_id'=>$loan->branch_id,'customer_id'=>$loan->customer_id,'loan_id'=>$loan->id,
            'loan_installment_id'=>$installmentId,'reminder_type'=>'pre_due','days_from_due'=>-1,'channel'=>'sms',
            'recipient'=>$recipient,'message'=>'Old failed SMS','scheduled_for'=>'2026-08-20','status'=>'failed','attempts'=>1,
            'idempotency_key'=>hash('sha256','disabled-sms-retry'),
        ]);

        try {
            $this->inBranch((int)$loan->branch_id, fn () => app(LoanReminderService::class)->retry($failed));
            $this->fail('Disabled SMS retry should be rejected.');
        } catch(ValidationException $e) {
            $this->assertSame('SMS Messaging is disabled in System Settings.',$e->errors()['status'][0] ?? null);
        }
        $this->assertSame(1,$failed->fresh()->attempts);
        $this->assertDatabaseHas('notification_logs',['id'=>$failed->id,'status'=>'failed']);
    }

    private function loanFixture(string $scheduleDate, string $dueDate): Loan
    {
        $business=Business::firstOrFail();
        $branch=$business->branches()->create(['name'=>'Messaging Branch '.uniqid(),'status'=>'active']);
        $customer=Customer::create([
            'branch_id'=>$branch->id,'customer_number'=>'CUS-MSG-'.strtoupper(substr(md5(uniqid()),0,8)),
            'identity_mode'=>'without_nin','first_name'=>'Neema','last_name'=>'Mushi','phone'=>'0756123456','status'=>'active',
        ]);
        $product=LoanProduct::create([
            'business_id'=>$business->id,'name'=>'Messaging Product '.uniqid(),'minimum_amount'=>'1000.00','maximum_amount'=>'500000.00',
            'duration'=>1,'duration_unit'=>'months','interest_rate'=>'0.000000','interest_calculation_method'=>'flat','repayment_frequency'=>'monthly',
            'grace_period_days'=>0,'late_payment_penalty_type'=>'fixed','late_payment_penalty_value'=>'0.000000','processing_fee_type'=>'fixed','processing_fee_value'=>'0.000000',
            'insurance_fee_type'=>'fixed','insurance_fee_value'=>'0.000000','other_charges'=>'0.00','collateral_required'=>false,'guarantor_required'=>false,'status'=>'active',
        ]);
        $loan=Loan::create([
            'branch_id'=>$branch->id,'customer_id'=>$customer->id,'loan_product_id'=>$product->id,'sequence_number'=>1,'sequence_year'=>2026,
            'loan_number'=>'LN-MSG-'.strtoupper(substr(md5(uniqid()),0,8)),'principal'=>'10000.00','interest'=>'0.00','processing_fee'=>'0.00',
            'insurance_fee'=>'0.00','other_charges'=>'0.00','fees'=>'0.00','total_repayment'=>'10000.00','principal_outstanding'=>'10000.00',
            'interest_outstanding'=>'0.00','fees_outstanding'=>'0.00','penalties_outstanding'=>'0.00','total_outstanding'=>'10000.00','total_paid'=>'0.00',
            'purpose'=>'Messaging testing','schedule_start_date'=>$scheduleDate,'status'=>'active','disbursed_at'=>$scheduleDate,'activated_at'=>$scheduleDate,'schedule_activated_at'=>$scheduleDate,
        ]);
        $loan->installments()->create([
            'installment_number'=>1,'principal'=>'10000.00','interest'=>'0.00','fees'=>'0.00','amount_due'=>'10000.00','due_date'=>$dueDate,
            'amount_paid'=>'0.00','remaining'=>'10000.00','status'=>'upcoming','principal_paid'=>'0.00','interest_paid'=>'0.00','fees_paid'=>'0.00','penalty_due'=>'0.00','penalty_paid'=>'0.00',
        ]);
        return $this->inBranch((int)$branch->id, fn () => $loan->fresh(['customer','installments']));
    }
}
