<?php

namespace App\Http\Controllers;

use App\Models\Branch;
use App\Models\Collateral;
use App\Models\LoanProduct;
use App\Models\LoanRestructuring;
use App\Models\User;
use App\Services\BranchAccessService;
use App\Services\LoanBranchReportService;
use Carbon\CarbonImmutable;
use Illuminate\Http\Request;
use Illuminate\View\View;
use Symfony\Component\HttpFoundation\StreamedResponse;

class LoanBranchReportController extends Controller
{
    public function __construct(private BranchAccessService $branchAccess)
    {
    }

    public function index(Request $request, LoanBranchReportService $service, ?string $report = null): View
    {
        $report = $report ?: 'active-loan-portfolio';
        abort_unless(array_key_exists($report, LoanBranchReportService::REPORTS), 404);
        $scope = $this->resolveScope($request);
        $data = $service->generate($report,$scope['selectedBranchIds']->all(),$scope['from'],$scope['to'],$scope['filters']);

        return view('loan-branch-reports.index', array_merge($scope,[
            'report'=>$report,
            'reports'=>LoanBranchReportService::REPORTS,
            'data'=>$data,
            'statusOptions'=>$this->statusOptions($report),
        ]));
    }

    public function export(Request $request, LoanBranchReportService $service, string $report): StreamedResponse
    {
        abort_unless(array_key_exists($report, LoanBranchReportService::REPORTS), 404);
        $scope = $this->resolveScope($request);
        $data = $service->generate($report,$scope['selectedBranchIds']->all(),$scope['from'],$scope['to'],$scope['filters']);
        $branchNames = $scope['selectedBranches']->pluck('name')->join(', ');
        $filename = $report.'-'.$scope['from'].'-to-'.$scope['to'].'.csv';

        return response()->streamDownload(function () use ($data,$scope,$branchNames): void {
            $out = fopen('php://output','w');
            fwrite($out,"\xEF\xBB\xBF");
            fputcsv($out,[$data['title']]);
            fputcsv($out,['From',$scope['from'],'To',$scope['to']]);
            fputcsv($out,['Branches',$branchNames]);
            if ($data['note'] !== '') fputcsv($out,['Method',$data['note']]);
            fputcsv($out,[]);
            foreach ($data['summary'] as $summary) fputcsv($out,[$summary['label'],$this->csvCell((string)$summary['value'])]);
            fputcsv($out,[]);
            fputcsv($out,array_values($data['columns']));
            foreach ($data['rows'] as $row) {
                $values = [];
                foreach (array_keys($data['columns']) as $key) $values[] = $this->csvCell((string)($row[$key] ?? ''));
                fputcsv($out,$values);
            }
            fclose($out);
        },$filename,['Content-Type'=>'text/csv; charset=UTF-8']);
    }

    private function resolveScope(Request $request): array
    {
        $user = $request->user();
        $allowedBranchIds = collect($this->branchAccess->accessibleBranchIds($user));
        abort_if($allowedBranchIds->isEmpty(),403,'No branch access is assigned to this user.');

        $validated = validator([
            'from'=>$request->input('from',now()->startOfMonth()->toDateString()),
            'to'=>$request->input('to',now()->toDateString()),
            'branch_ids'=>$request->input('branch_ids',[]),
            'loan_officer_id'=>$request->input('loan_officer_id'),
            'loan_product_id'=>$request->input('loan_product_id'),
            'search'=>$request->input('search'),
            'arrears_bucket'=>$request->input('arrears_bucket'),
            'record_status'=>$request->input('record_status'),
        ],[
            'from'=>['required','date'],
            'to'=>['required','date','after_or_equal:from'],
            'branch_ids'=>['array'],
            'branch_ids.*'=>['integer','exists:branches,id'],
            'loan_officer_id'=>['nullable','integer','exists:users,id'],
            'loan_product_id'=>['nullable','integer','exists:loan_products,id'],
            'search'=>['nullable','string','max:120'],
            'arrears_bucket'=>['nullable','in:1-7 days,8-30 days,31-60 days,61-90 days,90+ days'],
            'record_status'=>['nullable','string','max:60'],
        ])->validate();

        $requested = collect($validated['branch_ids'] ?? [])->map(fn ($id)=>(int)$id)->unique()->values();
        $this->branchAccess->assertBranchesAccessible($user, $requested->all());
        $selectedBranchIds = $requested->isEmpty() ? $allowedBranchIds : $requested;
        $from = CarbonImmutable::parse((string)$validated['from'])->toDateString();
        $to = CarbonImmutable::parse((string)$validated['to'])->toDateString();
        $branches = Branch::whereIn('id',$allowedBranchIds)->orderBy('name')->get();
        $selectedBranches = $branches->whereIn('id',$selectedBranchIds)->values();
        $businessIds = $branches->pluck('business_id')->unique();
        $loanProducts = LoanProduct::whereIn('business_id',$businessIds)->orderBy('name')->get();
        $loanOfficers = User::query()->whereHas('branches',fn ($query) => $query->whereIn('branches.id',$allowedBranchIds))
            ->where('status','active')->orderBy('name')->get();

        return [
            'branches'=>$branches,
            'selectedBranches'=>$selectedBranches,
            'selectedBranchIds'=>$selectedBranchIds,
            'loanProducts'=>$loanProducts,
            'loanOfficers'=>$loanOfficers,
            'from'=>$from,
            'to'=>$to,
            'filters'=>[
                'loan_officer_id'=>$validated['loan_officer_id'] ?? null,
                'loan_product_id'=>$validated['loan_product_id'] ?? null,
                'search'=>trim((string)($validated['search'] ?? '')),
                'arrears_bucket'=>$validated['arrears_bucket'] ?? '',
                'record_status'=>$validated['record_status'] ?? '',
            ],
        ];
    }

    private function statusOptions(string $report): array
    {
        return match ($report) {
            'collateral-report' => collect(Collateral::STATUSES)->mapWithKeys(fn ($status) => [$status => str($status)->replace('_',' ')->headline()->toString()])->all(),
            'restructuring-report' => collect(LoanRestructuring::STATUSES)->mapWithKeys(fn ($status) => [$status => str($status)->headline()->toString()])->all(),
            default => [],
        };
    }

    private function csvCell(string $value): string
    {
        return preg_match('/^[=+\-@]/',$value) ? "'".$value : $value;
    }
}
