<?php

namespace App\Components\PensionCase;

use App\Filters\PensionCaseFilterBuilder;
use App\Filters\PensionCaseFilterConfig;
use App\Models\Claim\Claim;
use App\Models\Claim\Status;
use App\Models\PensionCase\PensionCase;
use App\Models\ReportFilterPreset;
use Carbon\Carbon;
use App\Services\PensionCaseService;
use Illuminate\Database\Eloquent\Builder;
use Livewire\Attributes\Lazy;
use Livewire\Attributes\Computed;
use Livewire\Component;
use Livewire\WithPagination;
use Symfony\Component\HttpFoundation\StreamedResponse;

#[Lazy]
class Index extends Component
{
    use WithPagination;

    public string $search = '';
    public int $perPage = 25;
    public array $selectedExportFields = [];

    public string $openClosed = 'all';
    public string $claimType = '';
    public ?int $statusId = null;
    public string $vulnerable = '';

    public array $filterRows = [];
    public ?int $loadedPresetId = null;
    public bool $showSavePreset = false;
    public string $newPresetName = '';

    public function updatingSearch(): void { $this->resetPage(); }
    public function updatingOpenClosed(): void { $this->resetPage(); }
    public function updatingStatusId(): void { $this->resetPage(); }
    public function updatingVulnerable(): void { $this->resetPage(); }

    public string $exportLimit = 'filtered'; // filtered | all

    public array $exportColumnOrder = [];

    /**
     * Persist the column order sent by the draggable table header.
     * Only known/exportable fields are accepted.
     */
    public function updateColumnOrder(array $orderedFields): void
    {
        $allowedFields = array_keys($this->allExportColumns());

        $this->exportColumnOrder = collect($orderedFields)
            ->filter(
                fn ($field) =>
                    is_string($field) &&
                    in_array($field, $allowedFields, true)
            )
            ->unique()
            ->values()
            ->all();
    }

    public function updatingClaimType(): void
    {
        $this->statusId = null;
        $this->resetPage();
    }

    #[Computed]
    public function visibleColumns(): array
    {
        return $this->exportColumns();
    }

    public function updatedFilterRows($value, $key): void
    {
        if (str_ends_with((string) $key, '.field')) {
            $i = (int) explode('.', $key)[0];
            $this->filterRows[$i]['operator'] = '';
            $this->filterRows[$i]['value'] = '';
            $this->filterRows[$i]['value2'] = '';
        }

        $this->resetPage();
    }

    public function addFilterRow(): void
    {
        $this->filterRows[] = ['field' => '', 'operator' => '', 'value' => '', 'value2' => ''];
        $this->resetPage();
    }

    public function removeFilterRow(int $i): void
    {
        array_splice($this->filterRows, $i, 1);
        $this->filterRows = array_values($this->filterRows);
        $this->resetPage();
    }

    public function clearFilters(): void
    {
        $this->filterRows = [];
        $this->loadedPresetId = null;
        $this->resetPage();
    }

    public function savePreset(): void
    {
        $this->validate(['newPresetName' => 'required|string|max:100']);

        ReportFilterPreset::create([
            'user_id' => auth()->id(),
            'name' => $this->newPresetName,
            'model_type' => 'pension_case',
            'filters' => $this->filterRows,
        ]);

        $this->showSavePreset = false;
        $this->newPresetName = '';
    }

    public function loadPreset(int $id): void
    {
        $preset = ReportFilterPreset::where('user_id', auth()->id())->findOrFail($id);
        $this->filterRows = $preset->filters ?? [];
        $this->loadedPresetId = $id;
        $this->resetPage();
    }

    public function deletePreset(int $id): void
    {
        ReportFilterPreset::where('user_id', auth()->id())->findOrFail($id)->delete();

        if ($this->loadedPresetId === $id) {
            $this->loadedPresetId = null;
        }
    }

    // -----------------------------------------------------------------------
    // Export
    // -----------------------------------------------------------------------
    public function export(): StreamedResponse
    {
        $query = $this->exportLimit === 'all'
            ? PensionCase::query()
            : $this->buildQuery();

        $query = $query->with(['client', 'status', 'leadSource']);
    
        $columns = $this->exportColumns();
        $filename = 'cpf-cases-' . now()->format('Y-m-d-His') . '.csv';
    
        return response()->streamDownload(function () use ($query, $columns) {
            $handle = fopen('php://output', 'w');
    
            fwrite($handle, "\xEF\xBB\xBF");
            fputcsv($handle, collect($columns)->pluck('label')->all());
    
            $query->orderByDesc('created_at')->chunk(500, function ($cases) use ($handle, $columns) {
                foreach ($cases as $case) {
                    fputcsv($handle, collect($columns)->map(fn ($column) => $column['value']($case))->all());
                }
            });
    
            fclose($handle);
        }, $filename, [
            'Content-Type' => 'text/csv; charset=UTF-8',
            'Content-Disposition' => 'attachment; filename="' . $filename . '"',
        ]);
    }

    // -----------------------------------------------------------------------
    // Delete
    // -----------------------------------------------------------------------

    public function deleteCase(PensionCaseService $service, int $id): void {
        $case = PensionCase::findOrFail($id);

        $this->authorize('delete', $case);

        $label = $case->displayId;
        $service->delete($case);

        $this->dispatch('toast', title: 'CPF case deleted', message: $label . ' has been permanently deleted.', type: 'success');
    }

    private function buildQuery(): Builder
    {
        $query = PensionCase::query();

        if ($this->search) {
            $term = '%' . $this->search . '%';
            $tokens = preg_split('/\s+/', $this->search, -1, PREG_SPLIT_NO_EMPTY);

            if (count($tokens) > 1) {
                $query->where(function ($q) use ($tokens) {
                    $q->orWhereHas('client', function ($cq) use ($tokens) {
                        foreach ($tokens as $token) {
                            $cq->where(function ($inner) use ($token) {
                                $inner->where('name_first', 'like', '%' . $token . '%')
                                    ->orWhere('name_middle', 'like', '%' . $token . '%')
                                    ->orWhere('name_last', 'like', '%' . $token . '%')
                                    ->orWhere('name_preferred', 'like', '%' . $token . '%');
                            });
                        }
                    });
                });
            } else {
                $query->where(function ($q) use ($term) {
                    $q->orWhereHas('client', function ($cq) use ($term) {
                        $cq->where('name_first', 'like', $term)
                            ->orWhere('name_middle', 'like', $term)
                            ->orWhere('name_last', 'like', $term)
                            ->orWhere('name_preferred', 'like', $term);
                    })
                    ->orWhere('sf_case_id', 'like', $term)
                    ->orWhere('case_number', 'like', $term)
                    ->orWhere('claim_subject', 'like', $term)
                    ->orWhere('national_insurance', 'like', $term)
                    ->orWhere('investment_library', 'like', $term)
                    ->orWhere('cmc_client_reference', 'like', $term)
                    ->orWhere('fscs_reference', 'like', $term)
                    ->orWhereHas('leadSource', function ($lq) use ($term) {
                        $lq->where('display_name', 'like', $term);
                    });
                });
            }
        }

        if ($this->openClosed === 'open') {
            $query->open();
        }

        if ($this->openClosed === 'closed') {
            $query->closed();
        }

        if ($this->claimType) {
            $query->where('claim_type', $this->claimType);
        }

        if ($this->statusId) {
            $query->where('status_id', $this->statusId);
        }

        if ($this->vulnerable === '1') {
            $query->vulnerable();
        }

        PensionCaseFilterBuilder::apply($query, $this->filterRows);

        return $query;
    }

    private function defaultExportFields(): array
    {
        return [
            'case_reference',
            'client_full_name',
            'claim_type',
            'case_owner',
            'status_id',
            'open_closed',
        ];
    }

    private function exportColumns(): array
    {

        $allColumns = $this->allExportColumns();

        $advancedFilterFields = collect($this->filterRows)
            ->pluck('field')
            ->filter()
            ->all();
    
        $selectedFields = array_unique([
            ...$this->defaultExportFields(),
            ...$advancedFilterFields,
            ...$this->selectedExportFields,
        ]);
    
        $orderedFields = collect($this->exportColumnOrder)
            ->filter(fn ($field) => in_array($field, $selectedFields))
            ->values()
            ->all();
    
        $missingFields = collect($selectedFields)
            ->reject(fn ($field) => in_array($field, $orderedFields))
            ->values()
            ->all();
    
        $finalFields = [
            ...$orderedFields,
            ...$missingFields,
        ];
    
        return collect($allColumns)
            ->only($finalFields)
            ->sortBy(fn ($column, $key) => array_search($key, $finalFields))
            ->all();
        
    }

    public function getSelectedExportColumnLabelsProperty(): array
    {
        return collect($this->exportColumns())
            ->pluck('label')
            ->values()
            ->all();
    }

    private function allExportColumns(): array
    {
        $bool = fn($v) => $v ? 'Yes' : 'No';
        $date = fn($v) => $v ? Carbon::parse($v)->format('d/m/Y') : '';
        $dt = fn($v) => $v ? Carbon::parse($v)->format('d/m/Y H:i') : '';

        return [
            'case_reference' => [
                'label' => 'Case #',
                'value' => fn($c) => $c->fscs_reference ?? $c->case_number ?? $c->displayId,
            ],
            'case_id' => [
                'label' => 'Case ID',
                'value' => fn($c) => $c->displayId,
            ],
            'sf_case_id' => [
                'label' => 'SF Case ID',
                'value' => fn($c) => $c->sf_case_id ?? '',
            ],
            'case_number' => [
                'label' => 'Case Number',
                'value' => fn($c) => $c->case_number ?? '',
            ],
            'national_insurance' => [
                'label' => 'National Insurance',
                'value' => fn($c) => $c->national_insurance ?? '',
            ],
            'cmc_client_reference' => [
                'label' => 'CMC Client Reference',
                'value' => fn($c) => $c->cmc_client_reference ?? '',
            ],
            'fscs_reference' => [
                'label' => 'FSCS Reference',
                'value' => fn($c) => $c->fscs_reference ?? '',
            ],
            'fscs_contact_name' => [
                'label' => 'FSCS Contact Name',
                'value' => fn($c) => $c->fscs_contact_name ?? '',
            ],
            'fscs_contact_reference' => [
                'label' => 'FSCS Contact Reference',
                'value' => fn($c) => $c->fscs_contact_reference ?? '',
            ],
            'account_name' => [
                'label' => 'Account Name',
                'value' => fn($c) => $c->account_name ?? '',
            ],
            'car_reg' => [
                'label' => 'Car Reg',
                'value' => fn($c) => $c->car_reg ?? '',
            ],
            'atc' => [
                'label' => 'ATC',
                'value' => fn($c) => $c->atc ?? '',
            ],
            'lead_source_cpf' => [
                'label' => 'Lead Source CPF',
                'value' => fn($c) => $c->lead_source_cpf ?? '',
            ],

            'client_first_name' => [
                'label' => 'Client First Name',
                'value' => fn($c) => $c->client?->name_first ?? '',
            ],
            'client_last_name' => [
                'label' => 'Client Last Name',
                'value' => fn($c) => $c->client?->name_last ?? '',
            ],
            'client_full_name' => [
                'label' => 'Client Full Name',
                'value' => fn($c) => trim(($c->client?->name_first ?? '') . ' ' . ($c->client?->name_last ?? '')),
            ],
            'lead_source_id' => [
                'label' => 'Lead Source',
                'value' => fn($c) => $c->leadSource?->display_name ?? '',
            ],

            'case_owner' => [
                'label' => 'Case Owner',
                'value' => fn($c) => $c->case_owner ?? '',
            ],
            'claims_writer' => [
                'label' => 'Claims Writer',
                'value' => fn($c) => $c->claims_writer ?? '',
            ],
            'file_investigator' => [
                'label' => 'File Investigator',
                'value' => fn($c) => $c->file_investigator ?? '',
            ],
            'created_by' => [
                'label' => 'Created By',
                'value' => fn($c) => $c->created_by ?? '',
            ],
            'case_last_modified_by' => [
                'label' => 'Last Modified By',
                'value' => fn($c) => $c->case_last_modified_by ?? '',
            ],
            'tob_issued_by' => [
                'label' => 'TOB Issued By',
                'value' => fn($c) => $c->tob_issued_by ?? '',
            ],
            'sar_to' => [
                'label' => 'SAR To',
                'value' => fn($c) => $c->sar_to ?? '',
            ],
            'sar_received_from' => [
                'label' => 'SAR Received From',
                'value' => fn($c) => $c->sar_received_from ?? '',
            ],

            'status_id' => [
                'label' => 'Status',
                'value' => fn($c) => $c->status?->display_name ?? '',
            ],
            'claim_type' => [
                'label' => 'Claim Type',
                'value' => fn($c) => strtoupper($c->claim_type ?? ''),
            ],
            'claim_subject' => [
                'label' => 'Claim Subject',
                'value' => fn($c) => $c->claim_subject ?? '',
            ],
            'claim_basis' => [
                'label' => 'Claim Basis',
                'value' => fn($c) => $c->claim_basis ?? '',
            ],
            'priority' => [
                'label' => 'Priority',
                'value' => fn($c) => $c->priority ?? '',
            ],
            'settlement_status' => [
                'label' => 'Settlement Status',
                'value' => fn($c) => $c->settlement_status ?? '',
            ],
            'bank_claim_status' => [
                'label' => 'Bank Claim Status',
                'value' => fn($c) => $c->bank_claim_status ?? '',
            ],
            'fos_claim_status' => [
                'label' => 'FOS Claim Status',
                'value' => fn($c) => $c->fos_claim_status ?? '',
            ],
            'fscs_claim_status' => [
                'label' => 'FSCS Claim Status',
                'value' => fn($c) => $c->fscs_claim_status ?? '',
            ],
            'client_settlement_instruction' => [
                'label' => 'Client Settlement Instruction',
                'value' => fn($c) => $c->client_settlement_instruction ?? '',
            ],
            'fee_structure' => [
                'label' => 'Fee Structure',
                'value' => fn($c) => $c->fee_structure ?? '',
            ],

            'open_closed' => [
                'label' => 'Open / Closed',
                'value' => fn($c) => $c->is_open ? 'Open' : 'Closed',
            ],
            'is_open' => [
                'label' => 'Is Open',
                'value' => fn($c) => $bool($c->is_open),
            ],
            'is_closed' => [
                'label' => 'Is Closed',
                'value' => fn($c) => $bool($c->is_closed),
            ],

            'vulnerable_client_flag' => [
                'label' => 'Vulnerable Client',
                'value' => fn($c) => $bool($c->vulnerable_client_flag),
            ],
            'vulnerable_client_details' => [
                'label' => 'Vulnerable Client Details',
                'value' => fn($c) => $c->vulnerable_client_details ?? '',
            ],
            'vulnerable_client_flag_2' => [
                'label' => 'Vulnerable Client (2)',
                'value' => fn($c) => $bool($c->vulnerable_client_flag_2),
            ],
            'vulnerable_client_details_2' => [
                'label' => 'Vulnerable Client Details (2)',
                'value' => fn($c) => $c->vulnerable_client_details_2 ?? '',
            ],
            'special_contact_requirements_flag' => [
                'label' => 'Special Contact Requirements',
                'value' => fn($c) => $bool($c->special_contact_requirements_flag),
            ],
            'special_contact_requirements_details' => [
                'label' => 'Special Contact Details',
                'value' => fn($c) => $c->special_contact_requirements_details ?? '',
            ],
            'special_contact_requirements_flag_2' => [
                'label' => 'Special Contact Requirements (2)',
                'value' => fn($c) => $bool($c->special_contact_requirements_flag_2),
            ],
            'sms_opt_out' => [
                'label' => 'SMS Opt Out',
                'value' => fn($c) => $bool($c->sms_opt_out),
            ],
            'do_not_contact' => [
                'label' => 'Do Not Contact',
                'value' => fn($c) => $bool($c->do_not_contact),
            ],
            'ifa_referred' => [
                'label' => 'IFA Referred',
                'value' => fn($c) => $bool($c->ifa_referred),
            ],
            'ifa_refer_opted_out' => [
                'label' => 'IFA Refer Opted Out',
                'value' => fn($c) => $bool($c->ifa_refer_opted_out),
            ],
            'ifa_refer_opted_out_date' => [
                'label' => 'IFA Refer Opted Out Date',
                'value' => fn($c) => $date($c->ifa_refer_opted_out_date),
            ],
            'client_in_dispute' => [
                'label' => 'Client In Dispute',
                'value' => fn($c) => $bool($c->client_in_dispute),
            ],
            'sar_sent' => [
                'label' => 'SAR Sent',
                'value' => fn($c) => $bool($c->sar_sent),
            ],
            'sar_received' => [
                'label' => 'SAR Received',
                'value' => fn($c) => $bool($c->sar_received),
            ],
            'fee_capping_flag' => [
                'label' => 'Fee Capping',
                'value' => fn($c) => $bool($c->fee_capping_flag),
            ],
            'atc_received' => [
                'label' => 'ATC Received',
                'value' => fn($c) => $bool($c->atc_received),
            ],
            'pcp_subject' => [
                'label' => 'PCP Subject',
                'value' => fn($c) => $bool($c->pcp_subject),
            ],

            'investment_library' => [
                'label' => 'Investment Library',
                'value' => fn($c) => $c->investment_library ?? '',
            ],
            'underlying_investment' => [
                'label' => 'Underlying Investment',
                'value' => fn($c) => $c->underlying_investment ?? '',
            ],

            'quantum' => [
                'label' => 'Quantum (£)',
                'value' => fn($c) => $c->quantum ?? '',
            ],
            'settlement_to_date_new' => [
                'label' => 'Settlement to Date (£)',
                'value' => fn($c) => $c->settlement_to_date_new ?? '',
            ],
            'settlement_to_date_raw' => [
                'label' => 'Settlement to Date (Raw)',
                'value' => fn($c) => $c->settlement_to_date_raw ?? '',
            ],
            'projected_future_settlement' => [
                'label' => 'Projected Future Settlement (£)',
                'value' => fn($c) => $c->projected_future_settlement ?? '',
            ],
            'cpfc_fee_percentage' => [
                'label' => 'CPFC Fee %',
                'value' => fn($c) => $c->cpfc_fee_percentage ?? '',
            ],
            'cpfc_fee_total_nett' => [
                'label' => 'CPFC Fee Total Nett (£)',
                'value' => fn($c) => $c->cpfc_fee_total_nett ?? '',
            ],
            'our_fees_percentage_raw' => [
                'label' => 'Our Fees %',
                'value' => fn($c) => $c->our_fees_percentage_raw ?? '',
            ],

            'reasons_for_not_proceeding' => [
                'label' => 'Reasons for Not Proceeding',
                'value' => fn($c) => $c->reasons_for_not_proceeding ?? '',
            ],
            'additional_claims_info' => [
                'label' => 'Additional Claims Info',
                'value' => fn($c) => $c->additional_claims_info ?? '',
            ],
            'detail_of_outstanding_evidence' => [
                'label' => 'Detail of Outstanding Evidence',
                'value' => fn($c) => $c->detail_of_outstanding_evidence ?? '',
            ],

            'opened_at' => [
                'label' => 'Opened At',
                'value' => fn($c) => $dt($c->opened_at),
            ],
            'closed_at' => [
                'label' => 'Closed At',
                'value' => fn($c) => $dt($c->closed_at),
            ],
            'case_last_modified_at' => [
                'label' => 'Last Modified At',
                'value' => fn($c) => $dt($c->case_last_modified_at),
            ],
            'created_at' => [
                'label' => 'Created At',
                'value' => fn($c) => $dt($c->created_at),
            ],

            'issued_tob_date' => [
                'label' => 'TOB Issued',
                'value' => fn($c) => $date($c->issued_tob_date),
            ],
            'tob_in_date' => [
                'label' => 'TOB In',
                'value' => fn($c) => $date($c->tob_in_date),
            ],
            'ifa_referred_date' => [
                'label' => 'IFA Referred Date',
                'value' => fn($c) => $date($c->ifa_referred_date),
            ],
            'application_submitted_date' => [
                'label' => 'Application Submitted',
                'value' => fn($c) => $date($c->application_submitted_date),
            ],
            'date_requested_evidence' => [
                'label' => 'Evidence Requested',
                'value' => fn($c) => $date($c->date_requested_evidence),
            ],
            'sar_sent_date' => [
                'label' => 'SAR Sent Date',
                'value' => fn($c) => $date($c->sar_sent_date),
            ],
            'sar_received_date' => [
                'label' => 'SAR Received Date',
                'value' => fn($c) => $date($c->sar_received_date),
            ],
            'sar_reviewed_scanned_date' => [
                'label' => 'SAR Reviewed / Scanned',
                'value' => fn($c) => $date($c->sar_reviewed_scanned_date),
            ],
            'atc_received_date' => [
                'label' => 'ATC Received Date',
                'value' => fn($c) => $date($c->atc_received_date),
            ],
            'allocated_to_investigator_date' => [
                'label' => 'Allocated to Investigator',
                'value' => fn($c) => $date($c->allocated_to_investigator_date),
            ],
            'application_out_for_signature_date' => [
                'label' => 'App Out for Signature',
                'value' => fn($c) => $date($c->application_out_for_signature_date),
            ],

            'fos_allocated_date' => [
                'label' => 'FOS Allocated',
                'value' => fn($c) => $date($c->fos_allocated_date),
            ],
            'fos_app_submitted_date' => [
                'label' => 'FOS App Submitted',
                'value' => fn($c) => $date($c->fos_app_submitted_date),
            ],
            'fos_app_written_sent_to_client_date' => [
                'label' => 'FOS App Written & Sent to Client',
                'value' => fn($c) => $date($c->fos_app_written_sent_to_client_date),
            ],
            'fos_complaint_letter_sent_to_client_date' => [
                'label' => 'FOS Complaint Letter Sent to Client',
                'value' => fn($c) => $date($c->fos_complaint_letter_sent_to_client_date),
            ],
            'fos_initial_complaint_sent_date' => [
                'label' => 'FOS Initial Complaint Sent',
                'value' => fn($c) => $date($c->fos_initial_complaint_sent_date),
            ],
            'fos_under_review_with_investigator_date' => [
                'label' => 'FOS Under Review with Investigator',
                'value' => fn($c) => $date($c->fos_under_review_with_investigator_date),
            ],
            'fscs_allocated_date' => [
                'label' => 'FSCS Allocated',
                'value' => fn($c) => $date($c->fscs_allocated_date),
            ],
        ];
    }

    public function render()
    {
        $fieldConfig = PensionCaseFilterConfig::fields();
        $allStatuses = PensionCaseFilterConfig::allStatuses();
        $statuses = $this->claimType
            ? Status::forType('pension-' . $this->claimType)->get()
            : collect();

        $presets = ReportFilterPreset::forUser(auth()->id())
            ->where('model_type', 'pension_case')
            ->orderBy('name')
            ->get();

        return view('components.pension-case.index', [
            'cases' => $this->buildQuery()
                ->with(['client', 'status', 'leadSource'])
                ->orderByDesc('created_at')
                ->paginate($this->perPage),
            'fieldConfig' => $fieldConfig,
            'allStatuses' => $allStatuses,
            'statuses' => $statuses,
            'presets' => $presets,
            'exportFields' => $this->allExportColumns(),
            'selectedExportColumnLabels' => $this->selectedExportColumnLabels,
            'visibleColumns' => $this->visibleColumns(),
            'defaultExportFields' => $this->defaultExportFields(),
        ]);

       
    }

    public function placeholder()
    {
        return <<<HTML
<div>
    <div class="placeholder-glow mb-3">
        <span class="placeholder col-4 placeholder-lg"></span>
    </div>
    <div class="card shadow-sm">
        <div class="card-body">
            <div class="placeholder-glow">
                <span class="placeholder col-12 mb-2"></span>
                <span class="placeholder col-12 mb-2"></span>
                <span class="placeholder col-10 mb-2"></span>
            </div>
        </div>
    </div>
</div>
HTML;
    }
}