<?php

namespace App\Services;

use App\Models\Client\Client;
use Illuminate\Support\Facades\DB;
use Illuminate\Validation\ValidationException;

class ClientIntakeService
{
    public function create(array $data): Client
    {
        $data = $this->normaliseNameData($data);

        return DB::transaction(function () use ($data) {
            $client = Client::create([
                'solicitor_id' => $data['solicitor_id'] ?? null,
                'reference' => $data['reference'] ?? null,

                'title' => $this->standardiseName($data['title'] ?? null),
                'name_first' => $this->standardiseName($data['first_name'] ?? null),
                'name_middle' => $this->standardiseName($data['middle_name'] ?? null),
                'name_last' => $this->standardiseName($data['last_name'] ?? null),
                'name_preferred' => $this->standardiseName($data['preferred_name'] ?? null),

                'date_of_birth' => $data['dob'] ?? $data['date_of_birth'] ?? null,
                'email_address' => strtolower(trim($data['email'] ?? $data['email_address'] ?? '')),
                'phone_number' => str_replace(' ', '', $data['phone'] ?? $data['phone_number'] ?? ''),
                'landline_number' => $data['landline'] ?? $data['landline_number'] ?? null,
                
                'tranche_id' => $data['tranche_id'] ?? null,
                'lead_source_id' => $data['lead_source_id'] ?? null,
                'lead_source_endpoint_id' => $data['lead_source_endpoint_id'] ?? null,
            ]);

            $client->claim()->create([
                'solicitor_id' => $data['solicitor_id'] ?? null,
                'status_id' => $data['status_id'] ?? 21,
            ]);

            $this->createIntakeNotes($client, $data);

            return $client->load('claim');
        });
    }

    private function createIntakeNotes(Client $client, array $data): void
    {
        $notes = [
            'what_type_of_care_were_you_in' => 'Type of care',
            'care_eligibility_responses' => 'Care eligibility responses',
            'anything_you_wish_to_tell_us' => 'Additional notes',
            'preferred_callback_time' => 'Preferred callback time',
            'preferred_callback_daypart' => 'Preferred callback daypart',
            'privacy_consent' => 'Privacy consent',
            'terms_consent' => 'Terms consent',
            'submitted_at' => 'Submission timestamp',
            'collection_id' => 'Internal collection ID',
            'external_reference_id' => 'Internal reference ID',
        ];

        foreach ($notes as $field => $label) {
            if (! array_key_exists($field, $data)) {
                continue;
            }

            $value = $this->formatNoteValue($data[$field]);

            if ($value === null) {
                continue;
            }

            $client->notes()->create([
                'solicitor_id' => $client->solicitor_id,
                'user_id' => $data['user_id'] ?? null,
                'note' => "{$label}: {$value}",
            ]);
        }
    }

    private function formatNoteValue(mixed $value): ?string {
        if ($value === null || $value === '') {
            return null;
        }

        if (is_bool($value)) {
            return $value ? 'Yes' : 'No';
        }

        if (is_array($value)) {
            $value = array_filter(
                $value,
                static fn (mixed $item): bool => $item !== null && $item !== ''
            );

            return $value === []
                ? null
                : implode(', ', array_map(
                    static fn (mixed $item): string => is_bool($item)
                        ? ($item ? 'Yes' : 'No')
                        : (string) $item,
                    $value
                ));
        }

        return trim((string) $value) ?: null;
    }

    private function normaliseNameData(array $data): array
    {
        $firstName = trim($data['first_name'] ?? '');
        $lastName = trim($data['last_name'] ?? '');
        $fullName = trim($data['full_name'] ?? $data['name'] ?? '');

        $hasFirstAndLast = $firstName !== '' && $lastName !== '';
        $hasFullName = $fullName !== '';

        if (! $hasFirstAndLast && ! $hasFullName) {
            throw ValidationException::withMessages([
                'name' => 'Either full name or both first name and last name are required.',
            ]);
        }

        if (! $hasFirstAndLast && $hasFullName) {
            $splitName = $this->splitFullName($fullName);

            $data['title'] = $data['title'] ?? $splitName['title'];
            $data['first_name'] = $data['first_name'] ?? $splitName['first_name'];
            $data['last_name'] = $data['last_name'] ?? $splitName['last_name'];
        }

        if (empty($data['first_name']) || empty($data['last_name'])) {
            throw ValidationException::withMessages([
                'name' => 'The name must include both a first name and last name.',
            ]);
        }

        return $data;
    }

    private function splitFullName(string $fullName): array
    {
        $titles = [
            'mr' => 'Mr',
            'mrs' => 'Mrs',
            'ms' => 'Ms',
            'miss' => 'Miss',
            'dr' => 'Dr',
            'prof' => 'Prof',
            'rev' => 'Rev',
            'sir' => 'Sir',
            'lady' => 'Lady',
            'lord' => 'Lord',
            'mx' => 'Mx',
        ];

        $result = [
            'title' => null,
            'first_name' => null,
            'last_name' => null,
        ];

        $clean = trim(preg_replace('/\s+/', ' ', $fullName));

        if ($clean === '') {
            return $result;
        }

        $tokens = explode(' ', $clean);

        $firstKey = strtolower(rtrim($tokens[0], '.'));

        if (isset($titles[$firstKey])) {
            $result['title'] = $titles[$firstKey];
            array_shift($tokens);
        }

        if (empty($tokens)) {
            return $result;
        }

        $result['first_name'] = array_shift($tokens);
        $result['last_name'] = ! empty($tokens) ? implode(' ', $tokens) : null;

        return $result;
    }

    private function standardiseName(?string $value): ?string
    {
        if (! $value) {
            return null;
        }

        $value = trim($value);
        $value = preg_replace('/\s+/', ' ', $value);

        return mb_convert_case($value, MB_CASE_TITLE, 'UTF-8');
    }
}