<?php

namespace App\Components\Claim\Application;

use App\Models\Claim\Claim;
use App\Models\Client\Client;
use App\Services\HistoryService;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Illuminate\Support\Facades\DB;
use Livewire\Component;

class FurtherInformation extends Component {
    use AuthorizesRequests;

    public Claim $claim;

    public $aps = false;
    public $confirm_aps = false;
    public $name_in_care = null;
    public $legal_name_change = null;
    public $supporting_documents = null;

    // Section 7a care-background fields
    public $name_before_care = null;
    public $mothers_name = null;
    public $fathers_name = null;
    public $address_prior_line_1 = null;
    public $address_prior_line_2 = null;
    public $address_prior_line_3 = null;
    public $address_prior_postcode = null;
    public $gp_surgery = null;
    public array $hospitals = [];

    public function mount(): void {
        if ($this->claim->furtherInformation()->exists()) {
            $fi = $this->claim->furtherInformation;
            $this->aps = $fi->apply_aps;
            $this->confirm_aps = $fi->confirm_care_setting;
            $this->name_in_care = $fi->name_in_care;
            $this->legal_name_change = $fi->legal_name_change;
            // Fixed typo: Ensure column name matches the one used in save()
            $this->supporting_documents = $fi->supporting_docs_provision;

            $this->name_before_care = $fi->name_before_care;
            $this->mothers_name = $fi->mothers_name;
            $this->fathers_name = $fi->fathers_name;
            $this->address_prior_line_1 = $fi->address_prior_line_1;
            $this->address_prior_line_2 = $fi->address_prior_line_2;
            $this->address_prior_line_3 = $fi->address_prior_line_3;
            $this->address_prior_postcode = $fi->address_prior_postcode;
            $this->gp_surgery = $fi->gp_surgery;
            $this->hospitals = $fi->hospitals ?? [];
        }
    }

    public function addHospital(): void {
        $this->hospitals[] = '';
    }

    public function removeHospital(int $index): void {
        unset($this->hospitals[$index]);
        $this->hospitals = array_values($this->hospitals);
    }

    public function save(HistoryService $history) {
        $this->authorize('claims.edit');
        $this->validate([
            'aps' => 'required|boolean',
            'confirm_aps' => 'required|boolean',
            'name_in_care' => 'nullable|string',
            'legal_name_change' => 'nullable|string',
            'supporting_documents' => 'required|in:NA,Help,Yes',

            'name_before_care' => 'nullable|string|max:255',
            'mothers_name' => 'nullable|string|max:255',
            'fathers_name' => 'nullable|string|max:255',
            'address_prior_line_1' => 'nullable|string|max:255',
            'address_prior_line_2' => 'nullable|string|max:255',
            'address_prior_line_3' => 'nullable|string|max:255',
            'address_prior_postcode' => 'nullable|string|max:20',
            'gp_surgery' => 'nullable|string|max:255',
            'hospitals' => 'array',
            'hospitals.*' => 'nullable|string|max:255',
        ]);

        try {
            DB::transaction(function () {
                $fi = $this->claim->furtherInformation()->updateOrCreate([
                    'claim_id' => $this->claim->id,
                ], [
                    'apply_aps' => $this->aps,
                    'confirm_care_setting' => $this->confirm_aps,
                    'name_in_care' => $this->name_in_care,
                    'legal_name_change' => $this->legal_name_change,
                    'supporting_docs_provision' => $this->supporting_documents,

                    'name_before_care' => $this->name_before_care,
                    'mothers_name' => $this->mothers_name,
                    'fathers_name' => $this->fathers_name,
                    'address_prior_line_1' => $this->address_prior_line_1,
                    'address_prior_line_2' => $this->address_prior_line_2,
                    'address_prior_line_3' => $this->address_prior_line_3,
                    'address_prior_postcode' => $this->address_prior_postcode,
                    'gp_surgery' => $this->gp_surgery,
                    'hospitals' => $this->cleanHospitals(),
                ]);

                $this->syncMirrors($fi);
            });

            $history->add($this->claim->client, 'Further information updated.', [
                'claim_id' => $this->claim->id,
                'apply_aps' => $this->aps,
                'supporting_docs_provision' => $this->supporting_documents,
            ]);

            $this->dispatch('toast', message: 'Further information saved successfully.', type: 'success');

        } catch (\Exception $exception) {
            $this->dispatch('toast', message: $exception->getMessage(), type: 'error');
        }
    }

    /**
     * Mirror the name/address fields into the client's other features so they
     * surface in Previous Names, the Family Tree and the Address Book. Each
     * mirrored row's id is stored on the further_information record, so a
     * re-save updates the same row rather than duplicating it, and clearing a
     * field removes its mirrored row.
     */
    private function syncMirrors($fi): void {
        $client = $this->claim->client;

        $fi->name_before_care_previous_name_id = $this->syncPreviousName(
            $client,
            $fi->name_before_care_previous_name_id,
            $this->name_before_care,
        );

        $fi->mother_relation_id = $this->syncRelation(
            $client,
            $fi->mother_relation_id,
            'Mother',
            $this->mothers_name,
        );

        $fi->father_relation_id = $this->syncRelation(
            $client,
            $fi->father_relation_id,
            'Father',
            $this->fathers_name,
        );

        $fi->address_prior_to_care_id = $this->syncPriorAddress(
            $client,
            $fi->address_prior_to_care_id,
        );

        $fi->save();
    }

    /**
     * Create/update/remove the "Name Before Care" Previous Name row.
     * Returns the linked id (or null when the field is empty).
     */
    private function syncPreviousName(Client $client, ?int $existingId, ?string $fullName): ?int {
        $fullName = trim((string) $fullName);
        $existing = $existingId ? $client->previousNames()->find($existingId) : null;

        if ($fullName === '') {
            $existing?->delete();
            return null;
        }

        [$first, $last] = $this->splitName($fullName);

        if ($existing) {
            $existing->update([
                'name_first' => $first,
                'name_middle' => null,
                'name_last' => $last,
            ]);
            return $existing->id;
        }

        return $client->previousNames()->create([
            'name_first' => $first,
            'name_middle' => null,
            'name_last' => $last,
        ])->id;
    }

    /**
     * Create/update/remove a Family Tree (Relation) row of the given type.
     * Returns the linked id (or null when the field is empty).
     */
    private function syncRelation(Client $client, ?int $existingId, string $type, ?string $fullName): ?int {
        $fullName = trim((string) $fullName);
        $existing = $existingId ? $client->relations()->find($existingId) : null;

        if ($fullName === '') {
            $existing?->delete();
            return null;
        }

        [$first, $last] = $this->splitName($fullName);

        if ($existing) {
            $existing->update([
                'type' => $type,
                'first_name' => $first,
                'last_name' => $last,
            ]);
            return $existing->id;
        }

        return $client->relations()->create([
            'type' => $type,
            'first_name' => $first,
            'last_name' => $last,
        ])->id;
    }

    /**
     * Create/update/remove the "Address Prior to Care" address. Always stored
     * as a non-current address. Returns the linked id (or null when empty).
     */
    private function syncPriorAddress(Client $client, ?int $existingId): ?int {
        $lines = [
            'line_1' => $this->cleanLine($this->address_prior_line_1),
            'line_2' => $this->cleanLine($this->address_prior_line_2),
            'line_3' => $this->cleanLine($this->address_prior_line_3),
            'postcode' => $this->cleanPostcode($this->address_prior_postcode),
        ];

        $existing = $existingId ? $client->addresses()->find($existingId) : null;

        $hasAny = collect($lines)->filter(fn ($v) => !empty($v))->isNotEmpty();

        if (!$hasAny) {
            $existing?->delete();
            return null;
        }

        if ($existing) {
            $existing->update([...$lines, 'is_current' => false]);
            return $existing->id;
        }

        return $client->addresses()->create([
            ...$lines,
            'display_name' => 'Address prior to care',
            'is_current' => false,
        ])->id;
    }

    /**
     * Split a full name into [first, last]: first token, then the remainder.
     * Mirrors the HasName::fullName setter.
     */
    private function splitName(string $fullName): array {
        $parts = preg_split('/\s+/', trim($fullName), 2);
        return [$parts[0] ?? '', $parts[1] ?? ''];
    }

    private function cleanLine(?string $value): ?string {
        $value = trim((string) $value);
        return $value === '' ? null : ucwords(strtolower($value));
    }

    private function cleanPostcode(?string $value): ?string {
        $value = trim((string) $value);
        return $value === '' ? null : strtoupper(str_replace(' ', '', $value));
    }

    private function cleanHospitals(): array {
        return collect($this->hospitals)
            ->map(fn ($h) => trim((string) $h))
            ->filter(fn ($h) => $h !== '')
            ->values()
            ->all();
    }

    public function render() {
        return view('components.claim.application.further-information');
    }
}
