<?php

namespace App\Http\Controllers;

use App\Models\Card;
use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Support\Str;
use Sabre\VObject\Reader;

class AttendeeController extends Controller
{
    public function suggest(Request $request)
    {
        $q = trim((string) $request->input('q', $request->input('attendee', '')));
        if ($q === '' || mb_strlen($q) < 2) {
            return response()->view('event.partials.attendee-suggestions', ['suggestions' => []]);
        }

        $principal = $request->user()?->principal_uri;
        if (!$principal) {
            return response()->view('event.partials.attendee-suggestions', ['suggestions' => []]);
        }

        $query = Str::lower($q);
        $contacts = [];
        $seen = [];

        $cards = Card::query()
            ->select('cards.carddata')
            ->join('addressbooks', 'addressbooks.id', '=', 'cards.addressbookid')
            ->where('addressbooks.principaluri', $principal)
            ->whereRaw('LOWER(cards.carddata) LIKE ?', ['%' . $query . '%'])
            ->orderByDesc('cards.lastmodified')
            ->limit(200)
            ->get();

        foreach ($cards as $card) {
            $parsed = $this->parseCardEmails((string) $card->carddata);
            $name = $parsed['name'];

            foreach ($parsed['emails'] as $email) {
                $emailLc = Str::lower($email);
                $matches = str_contains($emailLc, $query)
                    || ($name !== '' && str_contains(Str::lower($name), $query));

                if (!$matches || isset($seen[$emailLc])) {
                    continue;
                }

                $seen[$emailLc] = true;
                $contacts[] = [
                    'email' => $emailLc,
                    'name' => $name !== '' ? $name : null,
                    'attendee_uri' => 'mailto:' . $emailLc,
                    'source' => 'contact',
                ];
            }
        }

        if (filter_var($q, FILTER_VALIDATE_EMAIL)) {
            $emailLc = Str::lower($q);
            if (!isset($seen[$emailLc])) {
                $contacts[] = [
                    'email' => $emailLc,
                    'name' => null,
                    'attendee_uri' => 'mailto:' . $emailLc,
                    'source' => 'email',
                ];
            }
        }

        if (empty($contacts)) {
            return response()->view('event.partials.attendee-suggestions', ['suggestions' => []]);
        }

        usort($contacts, function (array $a, array $b) use ($query) {
            $scoreA = $this->score($a, $query);
            $scoreB = $this->score($b, $query);

            if ($scoreA !== $scoreB) {
                return $scoreA <=> $scoreB;
            }

            return strcmp($a['email'], $b['email']);
        });

        $contacts = array_slice($contacts, 0, 8);
        $emails = array_values(array_unique(array_map(fn ($item) => $item['email'], $contacts)));
        $knownUsers = User::query()
            ->whereIn('email', $emails)
            ->pluck('email')
            ->map(fn ($email) => Str::lower((string) $email))
            ->flip()
            ->all();

        foreach ($contacts as &$item) {
            $item['verified'] = isset($knownUsers[$item['email']]);
        }
        unset($item);

        return response()->view('event.partials.attendee-suggestions', [
            'suggestions' => $contacts,
        ]);
    }

    private function parseCardEmails(string $carddata): array
    {
        $name = '';
        $emails = [];

        try {
            $vcard = Reader::read($carddata);
            $name = trim((string) ($vcard->FN ?? ''));
            foreach ($vcard->select('EMAIL') as $emailProp) {
                $email = Str::lower(trim((string) $emailProp->getValue()));
                if ($email !== '') {
                    $emails[] = $email;
                }
            }
        } catch (\Throwable $e) {
            if (preg_match('/^FN:(.+)$/mi', $carddata, $fn)) {
                $name = trim($fn[1]);
            }

            if (preg_match_all('/^EMAIL[^:]*:(.+)$/mi', $carddata, $matches)) {
                foreach (($matches[1] ?? []) as $emailRaw) {
                    $email = Str::lower(trim((string) $emailRaw));
                    if ($email !== '') {
                        $emails[] = $email;
                    }
                }
            }
        }

        return [
            'name' => $name,
            'emails' => array_values(array_unique($emails)),
        ];
    }

    private function score(array $item, string $query): int
    {
        $email = Str::lower((string) ($item['email'] ?? ''));
        $name = Str::lower((string) ($item['name'] ?? ''));
        $source = (string) ($item['source'] ?? '');

        if ($email === $query) {
            return 0;
        }
        if ($email !== '' && str_starts_with($email, $query)) {
            return 1;
        }
        if ($name !== '' && str_starts_with($name, $query)) {
            return 2;
        }
        if ($source === 'contact') {
            return 3;
        }

        return 4;
    }
}
