<?php

namespace App\Livewire\Admin;

use App\Domain\Financial\Services\AccountAnomalyScanner;
use App\Domain\Shared\Support\ArabicSearch;
use Livewire\Attributes\Layout;
use Livewire\Attributes\Title;
use Livewire\Attributes\Url;
use Livewire\Component;

/**
 * Every account that needs a human decision, worst first.
 *
 * The alternative — and what actually happened on the first academy to go live
 * — is that nobody knows a file is wrong until a parent argues at the desk. By
 * then the money is months old and the person who took it has gone home. This
 * screen reads the same books the roster reads and says: these are the players
 * whose file does not add up, and this is why.
 *
 * Read-only. Every row links into the settlement wizard, where a person decides
 * what actually happened before anything is written.
 */
#[Layout('layouts.app')]
#[Title('حالات تحتاج تسوية')]
class SettlementWorklist extends Component
{
    #[Url(as: 'case')]
    public string $caseFilter = '';

    #[Url(as: 'q')]
    public string $search = '';

    public function mount(): void
    {
        $this->authorize('settlements.manage');
    }

    public function clearFilter(): void
    {
        $this->caseFilter = '';
        $this->search = '';
    }

    /**
     * The scan is a handful of grouped queries over the branch's own rows, not
     * a query per participant — an academy of a few hundred players is one
     * page-load, and the branch scope on Participant is what keeps it to this
     * branch.
     */
    private function rows(): array
    {
        $rows = app(AccountAnomalyScanner::class)->scan(
            branchId: null,
            only: $this->caseFilter ?: null,
        );

        $term = trim($this->search);

        if ($term === '') {
            return $rows;
        }

        return array_values(array_filter($rows, function ($row) use ($term) {
            $person = $row['participant']->person;

            // Same folding the wizard's search uses, so a name found there is
            // found here too.
            return ArabicSearch::matches($person?->name_ar, $term)
                || ArabicSearch::matches($person?->name, $term)
                || str_contains((string) $person?->phone, ArabicSearch::normalise($term));
        }));
    }

    /** The worklist as a spreadsheet, for the people who work off paper. */
    public function export()
    {
        $this->authorize('settlements.manage');

        $rows = $this->rows();
        $cases = AccountAnomalyScanner::CASES;

        return response()->streamDownload(function () use ($rows, $cases) {
            $out = fopen('php://output', 'w');
            // BOM so Excel opens Arabic correctly instead of as mojibake.
            fwrite($out, "\xEF\xBB\xBF");
            fputcsv($out, ['رقم المشترك', 'الاسم', 'الهاتف', 'البرنامج', 'الحالات', 'فواتير غير مسددة', 'المستحق (ج.م)', 'المحصَّل (ج.م)', 'زيادة محاسَبة (ج.م)']);

            foreach ($rows as $row) {
                $participant = $row['participant'];

                // Money the books are asking for twice. It belongs in the export
                // for the same reason it belongs on the screen: it is the one
                // figure on the row that is not a debt.
                $overBilled = array_sum(array_column($row['detail']['over_billed_bundle'] ?? [], 'over_billed'));

                fputcsv($out, [
                    $participant->id,
                    $participant->person?->name_ar,
                    $participant->person?->phone,
                    $participant->enrollments->first()?->program?->name_ar,
                    implode(' / ', array_map(fn ($c) => $cases[$c]['label'], $row['cases'])),
                    $row['unpaid_invoices'],
                    number_format($row['owed'] / 100, 2, '.', ''),
                    number_format($row['paid'] / 100, 2, '.', ''),
                    $overBilled > 0 ? number_format($overBilled / 100, 2, '.', '') : '',
                ]);
            }

            fclose($out);
        }, 'settlement-worklist-' . now()->format('Y-m-d') . '.csv', [
            'Content-Type' => 'text/csv; charset=UTF-8',
        ]);
    }

    public function render()
    {
        $rows = $this->rows();

        $counts = [];
        foreach ($rows as $row) {
            foreach ($row['cases'] as $case) {
                $counts[$case] = ($counts[$case] ?? 0) + 1;
            }
        }

        return view('livewire.admin.settlement-worklist', [
            'rows' => $rows,
            'counts' => $counts,
            'cases' => AccountAnomalyScanner::CASES,
            'totalOwed' => array_sum(array_column($rows, 'owed')),
        ]);
    }
}
