<?php

namespace App\Components\Claim\Application;

use App\Models\Claim\Claim;
use App\Rules\PhoneNumber;
use App\Services\HistoryService;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Illuminate\Validation\ValidationException;
use InvalidArgumentException;
use Livewire\Component;

class Beneficiary extends Component {
    use AuthorizesRequests;
    public Claim $claim;
    public bool $hasBeneficiary = false;

    public $beneficiary = [
        'first_name' => '',
        'middle_name' => '',
        'surname' => '',
        'date_of_birth' => '',
        'address_line_1' => '',
        'address_line_2' => '',
        'address_line_3' => '',
        'address_line_4' => '',
        'postcode' => '',
        'email' => '',
        'telephone' => '',
    ];

    public function mount(Claim $claim) {
        $this->claim = $claim;

        if ($claim->beneficiary) {
            $this->beneficiary = collect(
                $claim->beneficiary->only(array_keys($this->beneficiary))
            )->mapWithKeys(function ($value, $key)
            {
                if (in_array($key, ['postcode', 'email', 'telephone'], true)) {
                    return [$key => $value !== null ? (string)$value : ''];
                }
                return [$key => $value];
            })->toArray();

            $this->hasBeneficiary = true;
        }
    }

    public function updatedHasBeneficiary($value, HistoryService $history) {
        if (!$value) {
            $history->add($this->claim->client, "Beneficiary status set to 'No'.", ['claim_id' => $this->claim->id]);
        } else {
            $history->add($this->claim->client, "Beneficiary status set to 'Yes'.", ['claim_id' => $this->claim->id]);
        }
    }

    protected function resetBeneficiary() {
        foreach ($this->beneficiary as $key => $value) {
            $this->beneficiary[$key] = '';
        }
    }

    public function save(HistoryService $service) {
        $this->authorize('claims.edit');
        $this->validate([
            'beneficiary.first_name' => 'required|string|max:255',
            'beneficiary.middle_name' => 'nullable|string|max:255',
            'beneficiary.surname' => 'required|string|max:255',
            'beneficiary.date_of_birth' => 'nullable|date',
            'beneficiary.address_line_1' => 'nullable|string|max:255',
            'beneficiary.address_line_2' => 'nullable|string|max:255',
            'beneficiary.address_line_3' => 'nullable|string|max:255',
            'beneficiary.address_line_4' => 'nullable|string|max:255',
            'beneficiary.postcode' => 'nullable|string|max:20',
            'beneficiary.email' => 'nullable|email|max:255',
            'beneficiary.telephone' => ['nullable', 'string', new PhoneNumber()],
        ]);

        try {
            $existing = $this->claim->beneficiary;
            $before = $existing?->toArray() ?? null;

            $model = $this->claim->beneficiary()->updateOrCreate(
                [],
                $this->beneficiary
            );

        } catch (InvalidArgumentException $e) {

            throw ValidationException::withMessages([
                $this->mapCastExceptionToField($e) => $e->getMessage(),
            ]);
        }

        if (!$existing) {
            $service->add(
                $this->claim->client,
                'Beneficiary "' . $this->beneficiary['first_name'] . ' ' . $this->beneficiary['surname'] . '" added',
                [
                    'claim_id' => $this->claim->id,
                    'added'    => $this->snapshot(),
                ]
            );
        } else {
            $changes = [];

            foreach ($model->getChanges() as $field => $newValue) {
                if (in_array($field, ['updated_at', 'created_at', 'id', 'claim_id'])) {
                    continue;
                }

                $changes[$field] = [
                    'from' => $before[$field] ?? null,
                    'to' => $newValue,
                ];
            }

            if (!empty($changes)) {
                $service->add(
                    $this->claim->client,
                    'Beneficiary "' . $this->beneficiary['first_name'] . ' ' . $this->beneficiary['surname'] . '" updated',
                    [
                        'claim_id' => $this->claim->id,
                        'changed'  => $changes,
                    ]
                );
            }
        }

        $this->hasBeneficiary = true;

        $this->dispatch('toast',
            title: 'Saved',
            message: 'Successfully saved beneficiary.',
            type: 'success'
        );
    }

    private function mapCastExceptionToField(InvalidArgumentException $e): string {
        $message = strtolower($e->getMessage());

        return match (true) {
            str_contains($message, 'email') =>
            'beneficiary.email',

            str_contains($message, 'telephone'),
            str_contains($message, 'phone') =>
            'beneficiary.telephone',

            str_contains($message, 'postcode') =>
            'beneficiary.postcode',

            default =>
            'beneficiary.first_name',
        };
    }

    private function snapshot(): array {
        return [
            'first_name' => $this->beneficiary['first_name'],
            'middle_name' => $this->beneficiary['middle_name'],
            'surname' => $this->beneficiary['surname'],
            'date_of_birth' => $this->beneficiary['date_of_birth'],
            'address_line_1' => $this->beneficiary['address_line_1'],
            'address_line_2' => $this->beneficiary['address_line_2'],
            'address_line_3' => $this->beneficiary['address_line_3'],
            'address_line_4' => $this->beneficiary['address_line_4'],
            'postcode' => $this->beneficiary['postcode'],
            'email' => $this->beneficiary['email'],
            'telephone' => $this->beneficiary['telephone'],
        ];
    }

    public function deleteBeneficiary(HistoryService $history) {
        $this->authorize('claims.edit');
        if ($this->claim->beneficiary) {
            $name = $this->claim->beneficiary->first_name . ' ' . $this->claim->beneficiary->surname;
            $this->claim->beneficiary()->delete();

            $history->add($this->claim->client, 'Beneficiary "' . $name . '" deleted', [
                'claim_id' => $this->claim->id,
                'deleted_name' => $name,
            ]);
        }

        $this->resetBeneficiary();
        $this->hasBeneficiary = false;
    }

    public function render() {
        return view('components.claim.application.beneficiary');
    }
}