<?php

namespace App\Http\Controllers;

use App\Models\Branch;
use App\Services\BranchAccessService;
use App\Services\CreditScore\CreditScoreCalibrationReportService;
use Illuminate\Http\Request;
use Illuminate\Support\Collection;
use Illuminate\View\View;
use Symfony\Component\HttpFoundation\StreamedResponse;

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

    public function index(Request $request, CreditScoreCalibrationReportService $service): View
    {
        $scope = $this->scope($request);
        $scoreVersion = $this->scoreVersion($request);

        return view('credit-score-reports.calibration', [
            ...$scope,
            'data' => $service->generate($scope['selectedBranchIds']->all(), $scoreVersion),
        ]);
    }

    public function export(Request $request, CreditScoreCalibrationReportService $service): StreamedResponse
    {
        $scope = $this->scope($request);
        $data = $service->generate($scope['selectedBranchIds']->all(), $this->scoreVersion($request));
        $branchNames = $scope['selectedBranches']->pluck('name')->join(', ');

        return response()->streamDownload(function () use ($data, $branchNames): void {
            $out = fopen('php://output', 'w');
            fwrite($out, "\xEF\xBB\xBF");
            fputcsv($out, ['Credit Score Calibration & Monitoring']);
            fputcsv($out, ['Generated', $data['generated_at']->format('Y-m-d H:i:s')]);
            fputcsv($out, ['Score version', $data['selected_score_version']]);
            fputcsv($out, ['Branches', $branchNames]);
            fputcsv($out, []);

            fputcsv($out, ['Summary metric', 'Value']);
            foreach ($data['summary'] as $label => $value) {
                fputcsv($out, [str($label)->replace('_', ' ')->headline()->toString(), $value ?? '']);
            }

            fputcsv($out, []);
            fputcsv($out, ['Application band', 'Applications', 'Avg score', 'Avg confidence', 'Approved', 'Rejected', 'Pending', 'Approval rate %', 'Observed loans', '30+ DPD', 'Written off', 'Write-off rate %', 'Outstanding', 'Overdue', 'Arrears rate %']);
            foreach ($data['bands'] as $row) {
                fputcsv($out, [
                    $row['label'], $row['applications'], $row['average_application_score'] ?? '', $row['average_confidence'] ?? '',
                    $row['approved'], $row['rejected'], $row['pending'], $row['approval_rate'], $row['observed_loans'],
                    $row['serious_arrears_loans'], $row['written_off_loans'], $row['write_off_rate'],
                    $row['total_outstanding'], $row['total_overdue'], $row['arrears_rate'],
                ]);
            }

            fputcsv($out, []);
            fputcsv($out, ['Branch', 'Applications', 'Avg score', 'Approved', 'Rejected', 'Approval rate %', 'Observed loans', '30+ DPD', 'Written off', 'Write-off rate %', 'Arrears rate %']);
            foreach ($data['branches'] as $row) {
                fputcsv($out, [
                    $row['branch_name'], $row['applications'], $row['average_application_score'] ?? '',
                    $row['approved'], $row['rejected'], $row['approval_rate'], $row['observed_loans'],
                    $row['serious_arrears_loans'], $row['written_off_loans'], $row['write_off_rate'], $row['arrears_rate'],
                ]);
            }

            fputcsv($out, []);
            fputcsv($out, ['Score version', 'Applications', 'Avg score', 'Approval rate %', 'Observed loans', 'Written off', 'Write-off rate %', 'Arrears rate %']);
            foreach ($data['versions'] as $row) {
                fputcsv($out, [
                    $row['score_version'], $row['applications'], $row['average_application_score'] ?? '',
                    $row['approval_rate'], $row['observed_loans'], $row['written_off_loans'], $row['write_off_rate'], $row['arrears_rate'],
                ]);
            }

            fputcsv($out, []);
            fputcsv($out, ['Migration band', 'Comparable', 'Avg score change', 'Improved band', 'Stable band', 'Worsened band', 'Cross version', 'Missing current']);
            foreach ($data['migration']['bands'] as $row) {
                fputcsv($out, [
                    $row['label'], $row['comparable'], $row['average_score_change'] ?? '', $row['improved_band'],
                    $row['stable_band'], $row['worsened_band'], $row['cross_version'], $row['missing_current'],
                ]);
            }

            fputcsv($out, []);
            fputcsv($out, ['Month', 'Applications', 'Avg score', 'Approval rate %', 'Observed loans', 'Written off', 'Write-off rate %', 'Arrears rate %']);
            foreach ($data['monthly_trend'] as $row) {
                fputcsv($out, [
                    $row['month'], $row['applications'], $row['average_application_score'] ?? '', $row['approval_rate'],
                    $row['observed_loans'], $row['written_off_loans'], $row['write_off_rate'], $row['arrears_rate'],
                ]);
            }

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

    private function scope(Request $request): array
    {
        $user = $request->user();
        $allowedBranchIds = collect($this->branchAccess->accessibleBranchIds($user))
            ->map(fn ($id) => (int) $id)
            ->unique()
            ->values();

        abort_if($allowedBranchIds->isEmpty(), 403, 'No branch access is assigned to this user.');

        $validated = validator($request->all(), [
            'branch_ids' => ['sometimes', 'array'],
            'branch_ids.*' => ['integer', 'exists:branches,id'],
        ])->validate();

        $requested = collect($validated['branch_ids'] ?? [])
            ->map(fn ($id) => (int) $id)
            ->unique()
            ->values();

        $this->branchAccess->assertBranchesAccessible($user, $requested->all());
        $selectedBranchIds = $requested->isEmpty() ? $allowedBranchIds : $requested;
        $branches = Branch::query()->whereIn('id', $allowedBranchIds)->orderBy('name')->get();

        return [
            'branches' => $branches,
            'selectedBranchIds' => $selectedBranchIds,
            'selectedBranches' => $this->selectedBranches($branches, $selectedBranchIds),
        ];
    }

    private function scoreVersion(Request $request): string
    {
        $validated = validator($request->all(), [
            'score_version' => ['sometimes', 'nullable', 'string', 'max:32'],
        ])->validate();

        return filled($validated['score_version'] ?? null)
            ? trim((string) $validated['score_version'])
            : (string) config('credit_score.version', 'v1');
    }

    private function selectedBranches(Collection $branches, Collection $selectedBranchIds): Collection
    {
        return $branches->whereIn('id', $selectedBranchIds)->values();
    }
}
