<?php

namespace Tests\Feature;

use App\Jobs\SendDailyBranchReportDelivery;
use App\Mail\DailyBranchReportMail;
use App\Models\Branch;
use App\Models\Business;
use App\Models\DailyBranchReport;
use App\Models\DailyBranchReportDelivery;
use App\Models\DailyBranchReportRecipient;
use App\Models\DailyClosing;
use App\Models\Role;
use App\Models\Setting;
use App\Models\User;
use App\Services\DailyBranchReportService;
use App\Services\WhatsAppGatewayService;
use Database\Seeders\DatabaseSeeder;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Http\Client\Request as HttpRequest;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Facades\Queue;
use Tests\TestCase;

class DailyBranchReportDeliveryTest extends TestCase
{
    use RefreshDatabase;

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

    public function test_admin_can_configure_email_and_whatsapp_report_recipients(): void
    {
        $business = Business::firstOrFail();
        $branch = $this->branch($business, 'Report Settings Branch');
        $admin = $this->staff('super-administrator', $branch, 'daily_report_admin');

        $this->actingAs($admin)->put(route('admin.daily-reports.settings.update'), [
            'email_enabled' => '1',
            'whatsapp_enabled' => '1',
            'whatsapp_api_endpoint' => 'https://graph.facebook.com/v20.0/123/messages',
            'whatsapp_api_token' => 'secret-whatsapp-token',
        ])->assertRedirect();

        $this->assertDatabaseHas('settings', [
            'business_id' => $business->id,
            'group' => 'daily_reports',
            'key' => 'email_enabled',
            'value' => '1',
        ]);
        $this->assertDatabaseHas('settings', [
            'business_id' => $business->id,
            'group' => 'daily_reports',
            'key' => 'whatsapp_enabled',
            'value' => '1',
        ]);
        $this->assertNotSame('secret-whatsapp-token', DB::table('businesses')->where('id', $business->id)->value('whatsapp_api_token'));

        $this->actingAs($admin)->post(route('admin.daily-reports.recipients.store'), [
            'name' => 'Operations Manager',
            'channel' => 'email',
            'destination' => 'MANAGER@example.com',
            'is_active' => '1',
        ])->assertRedirect();

        $this->actingAs($admin)->post(route('admin.daily-reports.recipients.store'), [
            'name' => 'Branch Manager',
            'branch_id' => $branch->id,
            'channel' => 'whatsapp',
            'destination' => '0756123456',
            'is_active' => '1',
        ])->assertRedirect();

        $this->assertDatabaseHas('daily_branch_report_recipients', [
            'business_id' => $business->id,
            'branch_id' => null,
            'channel' => 'email',
            'destination' => 'manager@example.com',
            'is_active' => 1,
        ]);
        $this->assertDatabaseHas('daily_branch_report_recipients', [
            'business_id' => $business->id,
            'branch_id' => $branch->id,
            'channel' => 'whatsapp',
            'destination' => '255756123456',
            'is_active' => 1,
        ]);
    }

    public function test_confirmed_report_is_archived_once_and_only_matching_enabled_recipients_are_queued(): void
    {
        Queue::fake();
        $business = Business::firstOrFail();
        $branchA = $this->branch($business, 'Report Branch A');
        $branchB = $this->branch($business, 'Report Branch B');
        $manager = $this->staff('branch-manager', $branchA, 'report_manager_a');

        foreach (['email_enabled', 'whatsapp_enabled'] as $key) {
            Setting::create(['business_id' => $business->id, 'group' => 'daily_reports', 'key' => $key, 'value' => '1', 'type' => 'boolean']);
        }

        DailyBranchReportRecipient::create([
            'business_id' => $business->id,
            'name' => 'All Branches Manager',
            'channel' => 'email',
            'destination' => 'all@example.com',
            'is_active' => true,
        ]);
        DailyBranchReportRecipient::create([
            'business_id' => $business->id,
            'branch_id' => $branchA->id,
            'name' => 'Branch A Manager',
            'channel' => 'whatsapp',
            'destination' => '255756123456',
            'is_active' => true,
        ]);
        DailyBranchReportRecipient::create([
            'business_id' => $business->id,
            'branch_id' => $branchB->id,
            'name' => 'Wrong Branch',
            'channel' => 'email',
            'destination' => 'other@example.com',
            'is_active' => true,
        ]);

        $closing = $this->confirmedClosing($business, $branchA, $manager);
        $service = app(DailyBranchReportService::class);
        $report = $service->archiveAndDispatch($closing);

        $this->assertSame(1, DailyBranchReport::count());
        $this->assertSame(2, DailyBranchReportDelivery::where('daily_branch_report_id', $report->id)->count());
        $this->assertDatabaseMissing('daily_branch_report_deliveries', ['recipient' => 'other@example.com']);
        $this->assertSame('1500.00', $report->snapshot['closing_funds']);
        $this->assertNotEmpty($report->checksum);
        Queue::assertPushed(SendDailyBranchReportDelivery::class, 2);

        $sameReport = $service->archive($closing);
        $this->assertSame($report->id, $sameReport->id);
        $this->assertSame($report->checksum, $sameReport->checksum);
    }

    public function test_email_delivery_uses_archived_copy_and_records_success(): void
    {
        Mail::fake();
        $business = Business::firstOrFail();
        $branch = $this->branch($business, 'Email Report Branch');
        $manager = $this->staff('branch-manager', $branch, 'email_report_manager');
        $report = app(DailyBranchReportService::class)->archive($this->confirmedClosing($business, $branch, $manager));
        $delivery = DailyBranchReportDelivery::create([
            'daily_branch_report_id' => $report->id,
            'channel' => 'email',
            'recipient_name' => 'Email Manager',
            'recipient' => 'manager@example.com',
            'status' => 'pending',
        ]);

        (new SendDailyBranchReportDelivery($delivery->id))->handle(app(WhatsAppGatewayService::class));

        Mail::assertSent(DailyBranchReportMail::class, fn (DailyBranchReportMail $mail): bool => $mail->hasTo('manager@example.com'));
        $delivery->refresh();
        $this->assertSame('sent', $delivery->status);
        $this->assertSame(1, $delivery->attempts);
        $this->assertNotNull($delivery->sent_at);
    }

    public function test_whatsapp_delivery_posts_meta_compatible_payload_and_records_provider_id(): void
    {
        Http::fake([
            'https://graph.facebook.com/*' => Http::response(['messages' => [['id' => 'wamid.report.123']]], 200),
        ]);
        $business = Business::firstOrFail();
        $business->update([
            'phone_country_code' => '255',
            'whatsapp_api_endpoint' => 'https://graph.facebook.com/v20.0/123/messages',
            'whatsapp_api_token' => 'whatsapp-test-token',
        ]);
        $branch = $this->branch($business, 'WhatsApp Report Branch');
        $manager = $this->staff('branch-manager', $branch, 'whatsapp_report_manager');
        $report = app(DailyBranchReportService::class)->archive($this->confirmedClosing($business->fresh(), $branch, $manager));
        $delivery = DailyBranchReportDelivery::create([
            'daily_branch_report_id' => $report->id,
            'channel' => 'whatsapp',
            'recipient_name' => 'WhatsApp Manager',
            'recipient' => '0756123456',
            'status' => 'pending',
        ]);

        (new SendDailyBranchReportDelivery($delivery->id))->handle(app(WhatsAppGatewayService::class));

        Http::assertSent(function (HttpRequest $request): bool {
            return $request->url() === 'https://graph.facebook.com/v20.0/123/messages'
                && $request->hasHeader('Authorization', 'Bearer whatsapp-test-token')
                && $request['messaging_product'] === 'whatsapp'
                && $request['to'] === '255756123456'
                && $request['type'] === 'text';
        });
        $delivery->refresh();
        $this->assertSame('sent', $delivery->status);
        $this->assertSame('255756123456', $delivery->recipient);
        $this->assertSame('wamid.report.123', $delivery->provider_message_id);
        $this->assertSame(1, $delivery->attempts);
    }

    private function confirmedClosing(Business $business, Branch $branch, User $manager): DailyClosing
    {
        return DailyClosing::withoutGlobalScopes()->create([
            'closing_number' => 'CLS-REPORT-'.$branch->id.'-'.str_pad((string) (DailyClosing::withoutGlobalScopes()->count() + 1), 3, '0', STR_PAD_LEFT),
            'business_id' => $business->id,
            'branch_id' => $branch->id,
            'closing_date' => '2026-08-17',
            'status' => 'confirmed',
            'opening_funds' => '1000.00',
            'collections' => '800.00',
            'cash_collections' => '300.00',
            'mobile_money_collections' => '250.00',
            'bank_collections' => '250.00',
            'other_collections' => '0.00',
            'disbursements' => '200.00',
            'expenses' => '100.00',
            'transfers' => '0.00',
            'capital_inflows' => '0.00',
            'other_inflows' => '0.00',
            'other_outflows' => '0.00',
            'closing_funds' => '1500.00',
            'expected_cash' => '1500.00',
            'actual_cash' => '1500.00',
            'variance' => '0.00',
            'snapshot_fingerprint' => hash('sha256', 'closing-'.$branch->id),
            'snapshot' => ['transaction_count' => 5, 'reconciliations' => []],
            'prepared_by' => $manager->id,
            'prepared_at' => now()->subMinute(),
            'manager_confirmed_by' => $manager->id,
            'manager_confirmed_at' => now(),
        ]);
    }

    private function branch(Business $business, string $name): Branch
    {
        return Branch::create([
            'business_id' => $business->id,
            'name' => $name,
            'status' => 'active',
        ]);
    }

    private function staff(string $role, Branch $branch, string $username): User
    {
        $user = User::factory()->create([
            'name' => str($username)->replace('_', ' ')->title()->toString(),
            'username' => $username,
            'status' => 'active',
            'primary_branch_id' => $branch->id,
        ]);
        $user->roles()->attach(Role::where('name', $role)->firstOrFail());
        $user->branches()->attach($branch->id, ['is_primary' => true]);

        return $user;
    }
}
