<?php

namespace App\Services;

use App\Models\Claim\Status;
use App\Models\Client\Client;
use App\Models\PensionCase\PensionCase;
use App\Models\PensionCase\PensionCaseComment;
use App\Models\PensionCase\PensionCaseInterim;

class PensionCaseService {
    public function __construct(protected HistoryService $history) {}

    public function create(Client $client, array $data): PensionCase {
        $case = $client->pensionCases()->create($data);

        $this->history->add($case, 'Pension case created', [
            'case_number' => $case->case_number,
            'sf_case_id' => $case->sf_case_id,
        ]);

        $this->history->add($client, 'Pension case created', [
            'case_number' => $case->case_number,
            'pension_case_id' => $case->id,
        ]);

        return $case;
    }

    public function update(PensionCase $case, array $data): PensionCase {
        $context = [];

        // Snapshot every field being written so the audit captures ALL changes
        // (financials, PII, dates), not just the status fields.
        $before = $case->only(array_keys($data));

        $case->update($data);

        foreach ($case->getChanges() as $field => $newValue) {
            if (in_array($field, ['updated_at', 'created_at'])) {
                continue;
            }
            $context['changed'][$field] = [
                'from' => $before[$field] ?? null,
                'to'   => $newValue,
            ];
        }

        $label = 'Pension case updated';
        if (!empty($context['changed'])) {
            $parts = [];
            $fieldLabels = [
                'settlement_status' => 'Settlement status',
                'bank_claim_status' => 'Bank claim status',
                'fos_claim_status'  => 'FOS claim status',
                'fscs_claim_status' => 'FSCS claim status',
                'is_open'           => 'Open/closed',
                'is_closed'         => 'Closed flag',
            ];

            // Spell out the key status fields in the label; summarise the rest as a count.
            foreach ($fieldLabels as $field => $name) {
                if (isset($context['changed'][$field])) {
                    $change = $context['changed'][$field];
                    $parts[] = $name . ' changed from "' . ($change['from'] ?? '-') . '" to "' . ($change['to'] ?? '-') . '"';
                }
            }

            $otherCount = count(array_diff_key($context['changed'], $fieldLabels));
            if ($otherCount > 0) {
                $parts[] = $otherCount . ' other field(s) updated';
            }

            $label = 'Pension case updated: ' . implode('; ', $parts);
        }

        $this->history->add($case, $label, $context);

        return $case;
    }

    public function delete(PensionCase $case): void {
        // Record the deletion against the client, which survives the case
        // (the case's own history cascades away with it).
        if ($case->client) {
            $this->history->add($case->client, 'Pension case deleted', [
                'case_number'     => $case->case_number,
                'sf_case_id'      => $case->sf_case_id,
                'pension_case_id' => $case->id,
            ]);
        }

        // Interims cascade on delete via the foreign key constraint.
        $case->delete();
    }

    public function changeClaimType(PensionCase $case, string $claimType, int $statusId): bool {
        if (!in_array($claimType, ['bank', 'fscs', 'fos'], true)) {
            return false;
        }

        if ($case->claim_type === $claimType) {
            return false;
        }

        // Statuses are type-specific (pension-bank / pension-fos / pension-fscs),
        // so the new status must belong to the target type's workflow.
        $valid = Status::forType('pension-' . $claimType)
            ->whereKey($statusId)
            ->exists();

        if (!$valid) {
            return false;
        }

        $from = $case->claim_type;

        $case->update([
            'claim_type' => $claimType,
            'status_id'  => $statusId,
        ]);

        // Close the old status-history entry and open one for the new status.
        $case->updateStatus($statusId);

        $this->history->add($case, 'Claim type changed from "' . strtoupper($from ?? '-') . '" to "' . strtoupper($claimType) . '"', [
            'changed' => [
                'claim_type' => ['from' => $from, 'to' => $claimType],
                'status_id'  => ['from' => null, 'to' => $statusId],
            ],
        ]);

        return true;
    }

    public function updateStatus(PensionCase $case, int $statusId): bool {
        if ($case->status_id === $statusId) {
            return false;
        }

        $oldStatus = $case->status;

        $case->update(['status_id' => $statusId]);
        $case->updateStatus($statusId);

        $newStatus = $case->fresh()->status;

        $this->history->add($case, 'Status changed from "' . ($oldStatus->display_name ?? '-') . '" to "' . ($newStatus->display_name ?? '-') . '"', [
            'from_status_id' => $oldStatus?->id,
            'from_status'    => $oldStatus?->display_name,
            'to_status_id'   => $statusId,
            'to_status'      => $newStatus?->display_name,
        ]);

        return true;
    }

    public function addComment(PensionCase $case, string $text): PensionCaseComment {
        $comment = $case->comments()->create([
            'pension_case_id' => $case->id,
            'comment_created_by' => auth()->id(),
            'comment_created_at' => now(),
            'comment_text' => $text,
        ]);

        $this->history->add($case, 'Comment added', [
            'comment_id' => $comment->id,
            'preview' => mb_substr($text, 0, 100),
        ]);

        return $comment;
    }

    public function addInterim(PensionCase $case, array $data): PensionCaseInterim {
        $interim = $case->interims()->create($data);

        $this->history->add($case, 'Interim payment #' . $interim->interim_number . ' added', array_merge(
            ['interim_id' => $interim->id],
            $this->interimSnapshot($interim)
        ));

        return $interim;
    }

    public function deleteInterim(PensionCaseInterim $interim): void {
        $case = $interim->pensionCase;

        $this->history->add($case, 'Interim payment #' . $interim->interim_number . ' deleted', array_merge(
            ['interim_id' => $interim->id],
            $this->interimSnapshot($interim)
        ));

        $interim->delete();
    }

    /**
     * Full financial breakdown of an interim payment for the append-only audit trail.
     */
    private function interimSnapshot(PensionCaseInterim $interim): array {
        return [
            'interim_number'        => $interim->interim_number,
            'interim_date'          => $interim->interim_date?->format('Y-m-d'),
            'interim_amount'        => $interim->interim_amount,
            'interim_capped_amount' => $interim->interim_capped_amount,
            'net_to_client'         => $interim->net_to_client,
            'vat_amount'            => $interim->vat_amount,
            'cpf_fee_gross'         => $interim->cpf_fee_gross,
            'cpf_fee_gross_capped'  => $interim->cpf_fee_gross_capped,
            'cpf_fee_net'           => $interim->cpf_fee_net,
            'cpf_fee_net_capped'    => $interim->cpf_fee_net_capped,
        ];
    }
}
