<?php
declare(strict_types=1);

class Guests
{
    private string $file;

    public function __construct(string $dataDir)
    {
        $this->file = $dataDir . '/guests.json';
    }

    // ── Groups ────────────────────────────────────────────────────────────────

    public function getGroups(): array
    {
        return $this->load()['groups'] ?? [];
    }


    public function createGroup(array $input): array
    {
        $data  = $this->load();
        $group = [
            'id'    => $this->uuid(),
            'name'  => $this->clean($input['name'] ?? ''),
            'order' => count($data['groups']),
        ];
        $data['groups'][] = $group;
        $this->save($data);
        return $group;
    }

    public function updateGroup(string $id, array $input): array
    {
        $data = $this->load();
        foreach ($data['groups'] as &$g) {
            if ($g['id'] === $id) {
                if (isset($input['name'])) $g['name'] = $this->clean($input['name']);
                $this->save($data);
                return $g;
            }
        }
        throw new RuntimeException("Group not found: $id");
    }

    public function deleteGroup(string $id): void
    {
        $data = $this->load();
        // Remove group from all guests
        foreach ($data['guests'] as &$guest) {
            $guest['group_ids'] = array_values(array_filter($guest['group_ids'] ?? [], fn($gid) => $gid !== $id));
        }
        $data['groups'] = array_values(array_filter($data['groups'], fn($g) => $g['id'] !== $id));
        $this->save($data);
    }

    // ── Guests ────────────────────────────────────────────────────────────────

    public function getAll(?string $groupId = null, ?string $rsvp = null): array
    {
        $data   = $this->load();
        $guests = $data['guests'] ?? [];

        if ($groupId) {
            $guests = array_filter($guests, fn($g) => in_array($groupId, $g['group_ids'] ?? [], true));
        }
        if ($rsvp) {
            $guests = array_filter($guests, fn($g) => ($g['rsvp'] ?? 'pending') === $rsvp);
        }

        return array_values($guests);
    }

    public function create(array $input): array
    {
        $data  = $this->load();
        $guest = $this->buildGuest($input);
        $data['guests'][] = $guest;
        $this->save($data);
        return $guest;
    }

    public function update(string $id, array $input): array
    {
        $data = $this->load();
        foreach ($data['guests'] as &$guest) {
            if ($guest['id'] === $id) {
                $guest = array_merge($guest, $this->buildGuest($input, $guest));
                $guest['id'] = $id; // preserve id
                $this->save($data);
                return $guest;
            }
        }
        throw new RuntimeException("Guest not found: $id");
    }

    public function delete(string $id): void
    {
        $data = $this->load();
        $data['guests'] = array_values(array_filter($data['guests'], fn($g) => $g['id'] !== $id));
        $this->save($data);
    }

    public function exportCsv(?string $groupId = null): string
    {
        $guests = $this->getAll($groupId);
        $groups = $this->getGroupMap();

        $lines = [];
        $lines[] = implode(';', ['Name', 'Begrüßung', 'E-Mail', 'Sprache', 'Gruppen', 'Personen', 'RSVP', 'Essenswünsche', 'Nachricht', 'Token', 'Erstellt']);

        foreach ($guests as $g) {
            $groupNames = implode(', ', array_map(fn($id) => $groups[$id] ?? $id, $g['group_ids'] ?? []));
            $mealStr    = implode(', ', array_map(
                fn($p) => trim(($p['name'] ?? '') . ': ' . ($p['meal'] ?? '')),
                $g['meal_persons'] ?? []
            ));
            $lines[] = implode(';', [
                $this->csvEsc($g['name'] ?? ''),
                $this->csvEsc($g['greeting'] ?? ''),
                $this->csvEsc($g['email'] ?? ''),
                $this->csvEsc($g['language'] ?? ''),
                $this->csvEsc($groupNames),
                $g['persons'] ?? 1,
                $this->csvEsc($g['rsvp'] ?? 'pending'),
                $this->csvEsc($mealStr),
                $this->csvEsc($g['message'] ?? ''),
                $this->csvEsc($g['token'] ?? ''),
                $this->csvEsc($g['created_at'] ?? ''),
            ]);
        }

        return implode("\n", $lines);
    }

    // Called by Rsvp handler when a guest submits the form
    public function upsertFromRsvp(array $input): array
    {
        $data = $this->load();

        $name  = $this->clean($input['name'] ?? '');
        $email = filter_var($input['email'] ?? '', FILTER_SANITIZE_EMAIL);
        $token = $input['token'] ?? '';

        // Try to match by token first (personalized link), then by email for anonymous submissions
        foreach ($data['guests'] as &$guest) {
            $matchToken = $token && ($guest['token'] ?? '') === $token;
            $matchEmail = !$token && $email && strtolower($guest['email'] ?? '') === strtolower($email);
            if ($matchToken || $matchEmail) {
                $guest['rsvp']    = $input['rsvp'] ?? $guest['rsvp'];
                $guest['persons'] = (int)($input['persons'] ?? $guest['persons']);
                $guest['meal']    = $input['meal'] ?? $guest['meal'];
                $guest['message'] = $this->clean($input['message'] ?? '');
                $this->save($data);
                return $guest;
            }
        }

        // New guest from public form
        $guest = [
            'id'         => $this->uuid(),
            'name'       => $name,
            'greeting'   => '',
            'email'      => $this->clean($input['email'] ?? ''),
            'language'   => $this->clean($input['language'] ?? 'de'),
            'group_ids'  => [],
            'persons'    => (int)($input['persons'] ?? 1),
            'rsvp'       => $input['rsvp'] ?? 'confirmed',
            'meal'       => array_map(fn($m) => $this->clean($m), (array)($input['meal'] ?? [])),
            'message'    => $this->clean($input['message'] ?? ''),
            'created_at' => date('c'),
        ];
        $data['guests'][] = $guest;
        $this->save($data);
        return $guest;
    }

    // ── Private ───────────────────────────────────────────────────────────────

    private function buildGuest(array $input, array $existing = []): array
    {
        return [
            'id'           => $existing['id'] ?? $this->uuid(),
            'token'        => $existing['token'] ?? $this->token(),
            'name'         => $this->clean($input['name'] ?? $existing['name'] ?? ''),
            'greeting'     => $this->clean($input['greeting'] ?? $existing['greeting'] ?? ''),
            'email'        => filter_var($input['email'] ?? $existing['email'] ?? '', FILTER_SANITIZE_EMAIL),
            'language'     => $this->clean($input['language'] ?? $existing['language'] ?? 'de'),
            'group_ids'    => array_map('strval', (array)($input['group_ids'] ?? $existing['group_ids'] ?? [])),
            'persons'      => max(1, (int)($input['persons'] ?? $existing['persons'] ?? 1)),
            'rsvp'         => in_array($input['rsvp'] ?? '', ['pending','confirmed','declined'], true) ? $input['rsvp'] : ($existing['rsvp'] ?? 'pending'),
            'meal_persons' => $this->buildMealPersons($input['meal_persons'] ?? $existing['meal_persons'] ?? []),
            'message'      => $this->clean($input['message'] ?? $existing['message'] ?? ''),
            'created_at'   => $existing['created_at'] ?? date('c'),
        ];
    }

    private function buildMealPersons(array $items): array
    {
        return array_values(array_map(fn($p) => [
            'name' => $this->clean($p['name'] ?? ''),
            'meal' => $this->clean($p['meal'] ?? ''),
        ], $items));
    }

    private function getGroupMap(): array
    {
        $map = [];
        foreach ($this->load()['groups'] as $g) $map[$g['id']] = $g['name'];
        return $map;
    }

    private function load(): array
    {
        if (!file_exists($this->file)) return ['groups' => [], 'guests' => []];
        return json_decode(file_get_contents($this->file), true) ?? ['groups' => [], 'guests' => []];
    }

    private function save(array $data): void
    {
        $tmp = $this->file . '.tmp';
        file_put_contents($tmp, json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE), LOCK_EX);
        rename($tmp, $this->file);
    }

    private function clean(string $val): string
    {
        return htmlspecialchars(strip_tags(trim($val)), ENT_QUOTES, 'UTF-8');
    }

    private function csvEsc(string $val): string
    {
        return '"' . str_replace('"', '""', $val) . '"';
    }

    private function token(): string
    {
        return bin2hex(random_bytes(4)); // 8 hex chars
    }

    private function uuid(): string
    {
        $b = random_bytes(16);
        $b[6] = chr(ord($b[6]) & 0x0f | 0x40);
        $b[8] = chr(ord($b[8]) & 0x3f | 0x80);
        return vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex($b), 4));
    }
}
