<?php

namespace Tests\Feature;

use App\Models\AccountingEntry;
use App\Models\Branch;
use App\Models\Business;
use App\Models\Collateral;
use App\Models\CollateralCategory;
use App\Models\CollateralMovement;
use App\Models\Customer;
use App\Models\Expense;
use App\Models\ExpenseCategory;
use App\Models\FinancialAccount;
use App\Models\FinancialAccountTransaction;
use App\Models\Loan;
use App\Models\LoanDisbursement;
use App\Models\LoanPayment;
use App\Models\LoanProduct;
use App\Models\Role;
use App\Models\User;
use Database\Seeders\DatabaseSeeder;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Storage;
use Tests\TestCase;

class FinalDocumentsExportsTest extends TestCase
{
    use RefreshDatabase;

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

    public function test_required_document_pdf_endpoints_use_configured_business_identity_and_are_read_only(): void
    {
        $fixture = $this->fixture('pdf');
        $user = $fixture['manager'];
        $payment = $fixture['payment'];
        $disbursement = $fixture['disbursement'];
        $expense = $fixture['expense'];
        $loan = $fixture['loan'];
        $collateral = $fixture['collateral'];
        $transactionCount = $this->inBranch($fixture['branch']->id, fn () => FinancialAccountTransaction::count());
        $paymentSnapshot = $payment->only(['amount','principal_amount','interest_amount','fees_amount','penalties_amount','reference']);
        $expenseSnapshot = $expense->only(['status','amount','financial_account_transaction_id','accounting_journal_number']);

        $routes = [
            route('documents.payment-receipt.pdf', $payment),
            route('documents.loan-statement.pdf', $loan),
            route('documents.collateral-receipt.pdf', $collateral),
            route('documents.disbursement-receipt.pdf', $disbursement),
            route('documents.expense-voucher.pdf', $expense),
        ];

        foreach ($routes as $url) {
            $response = $this->actingAs($user)->get($url)->assertOk();
            $response->assertHeader('content-type', 'application/pdf');
            $this->assertStringStartsWith('%PDF-1.4', $response->getContent());
            $this->assertStringContainsString('Step 24 Finance Ltd', $response->getContent());
        }

        $this->assertSame($paymentSnapshot, $payment->fresh()->only(array_keys($paymentSnapshot)));
        $this->assertSame($expenseSnapshot, $expense->fresh()->only(array_keys($expenseSnapshot)));
        $this->assertSame($transactionCount, FinancialAccountTransaction::count());
    }

    public function test_print_documents_use_private_logo_and_stamp_and_document_access_is_branch_scoped(): void
    {
        Storage::fake('local');
        $fixture = $this->fixture('branding');
        $business = $fixture['business'];
        $business->update(['logo_path'=>'business/1/branding/logo.png','stamp_path'=>'business/1/branding/stamp.png']);
        Storage::disk('local')->put($business->logo_path, 'private-logo-bytes');
        Storage::disk('local')->put($business->stamp_path, 'private-stamp-bytes');

        $this->actingAs($fixture['manager'])->get(route('document-branding.asset', [$business,'logo']))
            ->assertOk()->assertSee('private-logo-bytes');
        $this->actingAs($fixture['manager'])->get(route('documents.loan-agreement.print', $fixture['loan']))
            ->assertOk()->assertSee('Step 24 Finance Ltd')->assertSee(route('document-branding.asset', [$business,'logo']), false)
            ->assertSee(route('document-branding.asset', [$business,'stamp']), false);
        $this->actingAs($fixture['manager'])->get(route('documents.expense-voucher.print', $fixture['expense']))
            ->assertOk()->assertSee('EXPENSE VOUCHER')->assertSee($fixture['expense']->expense_number);

        $otherBranch = Branch::create(['business_id'=>$business->id,'name'=>'Step24 Other Branch','status'=>'active']);
        $otherUser = $this->staff('branch-manager', $otherBranch, 'step24_other_branch');
        $this->actingAs($otherUser)->get(route('documents.payment-receipt.pdf', $fixture['payment']))->assertForbidden();
        $this->actingAs($otherUser)->get(route('documents.expense-voucher.pdf', $fixture['expense']))->assertForbidden();
    }

    public function test_private_branding_assets_cannot_be_read_by_user_from_another_business(): void
    {
        Storage::fake('local');
        $fixture = $this->fixture('brand-scope');
        $business = $fixture['business'];
        $business->update(['logo_path'=>'business/1/branding/logo.png']);
        Storage::disk('local')->put($business->logo_path, 'scoped-logo');

        $otherBusiness = Business::create([
            'name'=>'Other Step24 Business','country'=>'Tanzania','currency'=>'TZS','currency_symbol'=>'TZS',
            'number_format'=>'1,234.56','financial_year_start_month'=>1,'accounting_period'=>'monthly','default_interest_method'=>'flat',
        ]);
        $otherBranch = Branch::create(['business_id'=>$otherBusiness->id,'name'=>'Other Business Branch','status'=>'active']);
        $otherUser = $this->staff('branch-manager', $otherBranch, 'step24_other_business');

        $this->actingAs($otherUser)->get(route('document-branding.asset', [$business,'logo']))->assertForbidden();
        $this->actingAs($fixture['manager'])->get(route('document-branding.asset', [$business,'logo']))
            ->assertOk()->assertSee('scoped-logo');
    }

    public function test_expense_voucher_and_collateral_release_require_completed_permanent_events(): void
    {
        $fixture = $this->fixture('guards');
        $pending = Expense::create([
            'expense_number'=>'EXP-S24-PENDING','branch_id'=>$fixture['branch']->id,'expense_category_id'=>$fixture['expense']->expense_category_id,
            'financial_account_id'=>$fixture['account']->id,'description'=>'Pending voucher test','amount'=>'100.00','expense_date'=>'2026-08-12',
            'status'=>'pending','entered_by'=>$fixture['manager']->id,
        ]);

        $this->actingAs($fixture['manager'])->get(route('documents.expense-voucher.pdf', $pending))->assertStatus(422);
        $this->actingAs($fixture['manager'])->get(route('documents.collateral-release.pdf', $fixture['collateral']))->assertNotFound();

        CollateralMovement::create([
            'collateral_id'=>$fixture['collateral']->id,'action'=>'status_changed','from_status'=>'in_custody','to_status'=>'released',
            'notes'=>'Released after full settlement S24','moved_by'=>$fixture['manager']->id,'moved_at'=>'2026-08-12 12:00:00',
        ]);
        $fixture['collateral']->update(['status'=>'released']);

        $response = $this->actingAs($fixture['manager'])->get(route('documents.collateral-release.pdf', $fixture['collateral']))->assertOk();
        $this->assertStringStartsWith('%PDF-1.4', $response->getContent());
        $this->assertStringContainsString('Released after full settlement S24', $response->getContent());
    }

    public function test_all_financial_reports_export_csv_from_the_same_permission_scoped_data(): void
    {
        $fixture = $this->fixture('reports');
        $query = ['from'=>'2026-08-01','to'=>'2026-08-31','branch_ids'=>[$fixture['branch']->id]];

        foreach (array_keys(\App\Services\FinancialReportService::REPORTS) as $report) {
            $response = $this->actingAs($fixture['manager'])->get(route('reports.export', ['report'=>$report] + $query))->assertOk();
            $response->assertHeader('content-type', 'text/csv; charset=UTF-8');
            $this->assertStringContainsString($fixture['branch']->name, $response->streamedContent());
        }

        $collection = $this->actingAs($fixture['manager'])->get(route('reports.export', ['report'=>'collections'] + $query))->assertOk()->streamedContent();
        $this->assertStringContainsString($fixture['payment']->reference, $collection);
        $this->assertStringContainsString("'=SUM(1,1)", $collection);

        $otherBranch = Branch::create(['business_id'=>$fixture['business']->id,'name'=>'Unauthorized Export Branch','status'=>'active']);
        $this->actingAs($fixture['manager'])->get(route('reports.export', [
            'report'=>'cash-flow','from'=>'2026-08-01','to'=>'2026-08-31','branch_ids'=>[$otherBranch->id],
        ]))->assertForbidden();
    }

    public function test_existing_workflow_pages_expose_step_24_document_actions(): void
    {
        $fixture = $this->fixture('ui');
        $user = $fixture['manager'];

        $this->actingAs($user)->get(route('loan-payments.receipt', $fixture['payment']))
            ->assertOk()->assertSee(route('documents.payment-receipt.pdf', $fixture['payment']), false);
        $this->actingAs($user)->get(route('loan-payments.statement', $fixture['loan']))
            ->assertOk()->assertSee(route('documents.loan-statement.pdf', $fixture['loan']), false);
        $this->actingAs($user)->get(route('loan-disbursements.receipt', $fixture['disbursement']))
            ->assertOk()->assertSee(route('documents.disbursement-receipt.pdf', $fixture['disbursement']), false);
        $this->actingAs($user)->get(route('collateral.receipt', $fixture['collateral']))
            ->assertOk()->assertSee(route('documents.collateral-receipt.pdf', $fixture['collateral']), false);
        $this->actingAs($user)->get(route('expenses.show', $fixture['expense']))
            ->assertOk()->assertSee(route('documents.expense-voucher.pdf', $fixture['expense']), false);
    }

    private function fixture(string $tag): array
    {
        $business = Business::firstOrFail();
        $business->update([
            'name'=>'Step 24 Finance Ltd','registration_number'=>'REG-S24','tin'=>'TIN-S24','address'=>'Dodoma CBD',
            'region'=>'Dodoma','district'=>'Dodoma Urban','country'=>'Tanzania','phone'=>'+255700240024','email'=>'docs@step24.test',
            'website'=>'https://step24.test','currency'=>'TZS','currency_symbol'=>'TZS','number_format'=>'1,234.56',
        ]);
        $branch = Branch::create(['business_id'=>$business->id,'name'=>'Step24 '.str($tag)->headline(),'status'=>'active']);
        $manager = $this->staff('branch-manager', $branch, 'step24_manager_'.str_replace('-','_',$tag));
        $customer = Customer::create([
            'branch_id'=>$branch->id,'customer_number'=>'CUS-S24-'.strtoupper(substr(md5($tag),0,8)),'identity_mode'=>'with_nin',
            'first_name'=>'Neema','last_name'=>str($tag)->headline()->toString(),'nin'=>'NIN-S24-'.strtoupper(substr(md5('nin-'.$tag),0,8)),
            'phone'=>'06'.str_pad((string)(10000000 + Customer::count()),8,'0',STR_PAD_LEFT),'status'=>'active','created_by'=>$manager->id,
        ]);
        $product = LoanProduct::create([
            'business_id'=>$business->id,'name'=>'Step24 Product '.str($tag)->headline(),'minimum_amount'=>'100.00','maximum_amount'=>'1000000.00','duration'=>2,'duration_unit'=>'months',
            'interest_rate'=>'10.000000','interest_calculation_method'=>'flat','repayment_frequency'=>'monthly','grace_period_days'=>0,
            'late_payment_penalty_type'=>'none','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-S24-'.strtoupper(substr(md5($tag),0,8)),'principal'=>'100000.00','interest'=>'10000.00','processing_fee'=>'0.00','insurance_fee'=>'0.00',
            'other_charges'=>'0.00','fees'=>'0.00','total_repayment'=>'110000.00','principal_outstanding'=>'91000.00','interest_outstanding'=>'9000.00',
            'fees_outstanding'=>'0.00','penalties_outstanding'=>'0.00','total_outstanding'=>'100000.00','total_paid'=>'10000.00','overdue_principal'=>'0.00','overdue_interest'=>'0.00',
            'overdue_fees'=>'0.00','overdue_penalties'=>'0.00','total_overdue'=>'0.00','days_overdue'=>0,'late_payment_penalty_type'=>'none','late_payment_penalty_value'=>'0.000000',
            'purpose'=>'Step 24 document verification','schedule_start_date'=>'2026-08-01','status'=>'active','contract_generated_at'=>'2026-08-01 08:00:00',
            'contract_generated_by'=>$manager->id,'disbursed_at'=>'2026-08-01 09:00:00','activated_at'=>'2026-08-01 09:00:00','schedule_activated_at'=>'2026-08-01 09:00:00','created_by'=>$manager->id,
        ]);
        $account = FinancialAccount::create([
            'branch_id'=>$branch->id,'name'=>'Step24 Cash '.$tag,'account_type'=>'cash','opening_balance'=>'500000.00','current_balance'=>'407500.00','status'=>'active',
        ]);
        FinancialAccountTransaction::create([
            'transaction_number'=>'FTX-S24-OPEN-'.strtoupper(substr(md5($tag),0,6)),'account_id'=>$account->id,'branch_id'=>$branch->id,
            'transaction_date'=>'2026-08-01 07:00:00','reference'=>'OPEN-S24','type'=>'opening_balance','description'=>'Opening capital','money_in'=>'500000.00','money_out'=>'0.00','running_balance'=>'500000.00','user_id'=>$manager->id,
        ]);
        $disbursementTransaction = FinancialAccountTransaction::create([
            'transaction_number'=>'FTX-S24-DISB-'.strtoupper(substr(md5($tag),0,6)),'account_id'=>$account->id,'branch_id'=>$branch->id,
            'transaction_date'=>'2026-08-01 09:00:00','reference'=>'DISB-S24','type'=>'loan_disbursement','description'=>'Loan disbursement','money_in'=>'0.00','money_out'=>'100000.00','running_balance'=>'400000.00','user_id'=>$manager->id,
        ]);
        $disbursement = LoanDisbursement::create([
            'disbursement_number'=>'DIS-S24-'.strtoupper(substr(md5($tag),0,8)),'loan_id'=>$loan->id,'branch_id'=>$branch->id,'financial_account_id'=>$account->id,
            'amount'=>'100000.00','disbursement_date'=>'2026-08-01','reference'=>'DISB-S24','notes'=>'Customer received funds','disbursed_by'=>$manager->id,
            'financial_account_transaction_id'=>$disbursementTransaction->id,
        ]);
        $paymentTransaction = FinancialAccountTransaction::create([
            'transaction_number'=>'FTX-S24-PAY-'.strtoupper(substr(md5($tag),0,6)),'account_id'=>$account->id,'branch_id'=>$branch->id,
            'transaction_date'=>'2026-08-10 10:00:00','reference'=>'PAYREF-S24','type'=>'loan_payment','description'=>'=SUM(1,1)','money_in'=>'10000.00','money_out'=>'0.00','running_balance'=>'410000.00','user_id'=>$manager->id,
        ]);
        $payment = LoanPayment::create([
            'receipt_number'=>'RCP-S24-'.strtoupper(substr(md5($tag),0,8)),'loan_id'=>$loan->id,'customer_id'=>$customer->id,'branch_id'=>$branch->id,'financial_account_id'=>$account->id,
            'financial_account_transaction_id'=>$paymentTransaction->id,'amount'=>'10000.00','principal_amount'=>'9000.00','interest_amount'=>'1000.00','fees_amount'=>'0.00','penalties_amount'=>'0.00',
            'payment_date'=>'2026-08-10 10:00:00','method'=>'cash','reference'=>'PAYREF-S24','notes'=>'Step 24 payment','received_by'=>$manager->id,
        ]);
        $category = CollateralCategory::where('business_id',$business->id)->firstOrFail();
        $collateral = Collateral::create([
            'customer_id'=>$customer->id,'branch_id'=>$branch->id,'category_id'=>$category->id,'sequence_number'=>9000 + Collateral::count()+1,
            'code'=>'COL-S24-'.strtoupper(substr(md5($tag),0,8)),'item'=>'Television '.$tag,'brand'=>'Samsung','model'=>'S24','serial_number'=>'SER-S24',
            'estimated_value'=>'200000.00','accepted_value'=>'150000.00','condition'=>'Good','received_at'=>'2026-08-01','store_room'=>'A','shelf'=>'2',
            'status'=>'in_custody','received_by'=>$manager->id,'created_by'=>$manager->id,
        ]);
        $expenseTransaction = FinancialAccountTransaction::create([
            'transaction_number'=>'FTX-S24-EXP-'.strtoupper(substr(md5($tag),0,6)),'account_id'=>$account->id,'branch_id'=>$branch->id,
            'transaction_date'=>'2026-08-11 14:00:00','reference'=>'EXP-S24','type'=>'expense','description'=>'Office supplies','money_in'=>'0.00','money_out'=>'2500.00','running_balance'=>'407500.00','user_id'=>$manager->id,
        ]);
        $expenseCategory = ExpenseCategory::where('business_id',$business->id)->firstOrFail();
        $expense = Expense::create([
            'expense_number'=>'EXP-S24-'.strtoupper(substr(md5($tag),0,8)),'branch_id'=>$branch->id,'expense_category_id'=>$expenseCategory->id,'financial_account_id'=>$account->id,
            'description'=>'Printer paper and supplies','amount'=>'2500.00','expense_date'=>'2026-08-11','supplier'=>'Step24 Stationers','notes'=>'Approved operational expense',
            'status'=>'approved','entered_by'=>$manager->id,'approved_by'=>$manager->id,'approved_at'=>'2026-08-11 14:00:00','decision_comments'=>'Verified receipt',
            'financial_account_transaction_id'=>$expenseTransaction->id,'accounting_journal_number'=>'JRN-S24-EXP',
        ]);
        AccountingEntry::create([
            'journal_number'=>'JRN-S24-INT','branch_id'=>$branch->id,'financial_account_id'=>$account->id,'entry_date'=>'2026-08-10 10:00:00',
            'reference'=>$payment->receipt_number,'category'=>'interest','event_type'=>'loan_payment','description'=>'Interest income','debit'=>'0.00','credit'=>'1000.00','user_id'=>$manager->id,
        ]);
        AccountingEntry::create([
            'journal_number'=>'JRN-S24-EXP','branch_id'=>$branch->id,'financial_account_id'=>$account->id,'entry_date'=>'2026-08-11 14:00:00',
            'reference'=>$expense->expense_number,'category'=>'expenses','event_type'=>'expense','description'=>'Operating expense','debit'=>'2500.00','credit'=>'0.00','user_id'=>$manager->id,
        ]);

        return compact('business','branch','manager','customer','product','loan','account','payment','disbursement','collateral','expense');
    }

    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;
    }
}
