<?php
declare(strict_types=1);

namespace App;

final class AccountPages
{
    public static function register(App $app): array
    {
        $flash = $app->flash()->get();
        $isLoggedIn = isset($_SESSION['user_id']);
        $error = '';
        $displayName = '';
        $email = '';

        if ($isLoggedIn) {
            redirect('/dashboard');
        }

        if ($_SERVER['REQUEST_METHOD'] === 'POST') {
            $displayName = trim((string)($_POST['display_name'] ?? ''));
            $email = trim((string)($_POST['email'] ?? ''));
            $password = (string)($_POST['password'] ?? '');
            $password2 = (string)($_POST['password_confirm'] ?? '');

            if ($password !== $password2) {
                $error = 'Passwörter stimmen nicht überein.';
            } elseif (strlen($password) < 8) {
                $error = 'Passwort muss mindestens 8 Zeichen haben.';
            } else {
                try {
                    $auth = new Auth($app);
                    $userId = $auth->register($displayName, $email, $password);
                    $code = $auth->createVerifyCode($userId, $email);
                    $mailer = new Mailer($app);
                    $mailer->sendTemplate('registration_confirm', $email, [
                        'code' => $code,
                        'display_name' => $displayName,
                    ]);
                    $_SESSION['verify_email'] = $email;
                    $app->flash()->set('info', 'Bitte bestätige deine Registrierung mit dem Code aus der E-Mail.');
                    redirect('/verify');
                } catch (\Throwable $e) {
                    $error = $e->getMessage();
                }
            }
        }

        return compact('flash', 'error', 'displayName', 'email');
    }

    public static function login(App $app): array
    {
        $flash = $app->flash()->get();
        $isLoggedIn = isset($_SESSION['user_id']);
        $error = '';
        $emailPrefill = '';

        if ($isLoggedIn) {
            redirect('/dashboard');
        }

        if ($_SERVER['REQUEST_METHOD'] === 'POST') {
            $email = trim((string)($_POST['email'] ?? ''));
            $emailPrefill = $email;
            $password = (string)($_POST['password'] ?? '');
            try {
                $auth = new Auth($app);
                $res = $auth->login($email, $password);
                if ($res['status'] === 'pending') {
                    $code = $auth->createVerifyCode($res['id'], $email);
                    $mailer = new Mailer($app);
                    $mailer->sendTemplate('registration_confirm', $email, [
                        'code' => $code,
                        'display_name' => $email,
                    ]);
                    $_SESSION['verify_email'] = $email;
                    $app->flash()->set('info', 'Bitte bestätige deine Registrierung mit dem Code aus der E-Mail.');
                    redirect('/verify');
                }
                $_SESSION['user_id'] = $res['id'];
                $app->flash()->set('success', 'Erfolgreich angemeldet.');
                redirect('/dashboard');
            } catch (\Throwable $e) {
                $error = $e->getMessage();
            }
        }

        return compact('flash', 'error', 'emailPrefill', 'isLoggedIn');
    }

    public static function verify(App $app): array
    {
        $pdo = $app->pdo();
        $flash = $app->flash()->get();
        $error = '';
        $info = '';
        $email = $_SESSION['verify_email'] ?? '';

        if ($_SERVER['REQUEST_METHOD'] === 'POST') {
            $action = $_POST['action'] ?? 'verify';
            $email = trim((string)($_POST['email'] ?? ''));
            $code = strtoupper(trim((string)($_POST['code'] ?? '')));
            $auth = new Auth($app);
            $mailer = new Mailer($app);

            if ($action === 'resend') {
                try {
                    $stmt = $pdo?->prepare('SELECT id, display_name, status FROM users u JOIN user_profiles p ON p.user_id = u.id WHERE u.email = :email LIMIT 1');
                    $stmt?->execute(['email' => $email]);
                    $row = $stmt?->fetch(\PDO::FETCH_ASSOC);
                    if (!$row) {
                        throw new \RuntimeException('E-Mail nicht gefunden.');
                    }
                    $userId = (int)$row['id'];
                    $codeNew = $auth->createVerifyCode($userId, $email);
                    $mailer->sendTemplate('registration_resend_code', $email, [
                        'code' => $codeNew,
                        'display_name' => $row['display_name'] ?? '',
                    ]);
                    $info = 'Neuer Code wurde versendet.';
                    $_SESSION['verify_email'] = $email;
                } catch (\Throwable $e) {
                    $error = $e->getMessage();
                }
            } else {
                try {
                    $userId = $auth->verifyCode($email, $code);
                    $_SESSION['user_id'] = $userId;
                    unset($_SESSION['verify_email']);
                    $mailer->sendTemplate('registration_welcome', $email, ['display_name' => $email]);
                    $app->flash()->set('success', 'Registrierung bestätigt. Willkommen!');
                    redirect('/dashboard');
                } catch (\Throwable $e) {
                    $error = $e->getMessage();
                }
            }
        }

        return compact('flash', 'error', 'info', 'email');
    }

    public static function dashboard(App $app): array
    {
        if (!isset($_SESSION['user_id'])) {
            redirect('/login');
        }

        $pdo = $app->pdo();
        $flash = $app->flash()->get();
        $userId = (int)$_SESSION['user_id'];
        $error = '';
        $info = '';
        $crypto = null;
        try { $crypto = new Crypto($app->config()); } catch (\Throwable) {}

        if ($_SERVER['REQUEST_METHOD'] === 'POST') {
            $action = $_POST['action'] ?? '';
            try {
                if ($action === 'profile') {
                    $languages = $_POST['languages'] ?? '';
                    if (is_array($languages)) {
                        $languages = implode(', ', array_map('trim', $languages));
                    }
                    $phoneEnc = $crypto ? $crypto->encrypt(trim((string)$_POST['contact_phone'])) : trim((string)$_POST['contact_phone']);
                    $stmt = $pdo?->prepare('UPDATE user_profiles SET display_name=:name, first_name=:fname, last_name=:lname, zip=:zip, city=:city, profession=:prof, languages=:langs, about=:about, contact_phone=:phone, updated_at=NOW() WHERE user_id=:id');
                    $stmt?->execute([
                        'name'  => trim((string)$_POST['display_name']),
                        'fname' => trim((string)$_POST['first_name']),
                        'lname' => trim((string)$_POST['last_name']),
                        'zip'   => trim((string)$_POST['zip']),
                        'city'  => trim((string)$_POST['city']),
                        'prof'  => trim((string)$_POST['profession']),
                        'langs' => trim((string)$languages),
                        'about' => trim((string)$_POST['about']),
                        'phone' => $phoneEnc,
                        'id'    => $userId,
                    ]);
                    $info = 'Profil gespeichert.';
                } elseif ($action === 'child_add') {
                    $firstNameEnc = $crypto ? $crypto->encrypt(trim((string)$_POST['first_name'])) : trim((string)$_POST['first_name']);
                    $noteEnc = $crypto ? $crypto->encrypt(trim((string)$_POST['note'])) : trim((string)$_POST['note']);
                    $stmt = $pdo?->prepare('INSERT INTO children (user_id, gender, birthdate, age_years, encrypted_first_name, note, created_at, updated_at) VALUES (:uid, :gender, :birthdate, :age, :name, :note, NOW(), NOW())');
                    $stmt?->execute([
                        'uid' => $userId,
                        'gender' => $_POST['gender'] ?? 'unknown',
                        'birthdate' => $_POST['birthdate'] ?: null,
                        'age' => $_POST['age_years'] ?: null,
                        'name' => $firstNameEnc,
                        'note' => $noteEnc,
                    ]);
                    $info = 'Kind hinzugefügt.';
                } elseif ($action === 'event_add' || $action === 'event_update') {
                    $street = trim((string)($_POST['street'] ?? ''));
                    $zip = trim((string)($_POST['zip'] ?? ''));
                    $city = trim((string)($_POST['city'] ?? ''));
                    $region = trim((string)($_POST['region'] ?? ''));
                    $lat = isset($_POST['lat']) && $_POST['lat'] !== '' ? (float)$_POST['lat'] : null;
                    $lng = isset($_POST['lng']) && $_POST['lng'] !== '' ? (float)$_POST['lng'] : null;
                    $needsGeocode = ($lat === null || $lng === null || $region === '');
                    if ($needsGeocode) {
                        [$geoLat, $geoLng, $geoRegion] = self::geocodeAddress($street, $zip, $city, $region);
                        if ($lat === null) { $lat = $geoLat; }
                        if ($lng === null) { $lng = $geoLng; }
                        if ($region === '' && $geoRegion) { $region = $geoRegion; }
                    }

                    if ($action === 'event_add') {
                        $stmt = $pdo?->prepare('INSERT INTO events (created_by, title, teaser_public, description, location_label, street, zip, city, region, lat, lng, starts_at, allow_kids, visibility, status, created_at, updated_at) VALUES (:uid, :title, :teaser, :descr, :loc, :street, :zip, :city, :region, :lat, :lng, :start, :allow, :vis, :status, NOW(), NOW())');
                        $stmt?->execute([
                            'uid' => $userId,
                            'title' => trim((string)$_POST['title']),
                            'teaser' => trim((string)$_POST['teaser']),
                            'descr' => trim((string)$_POST['description']),
                            'loc' => trim((string)$_POST['location_label']),
                            'street' => $street ?: null,
                            'zip' => $zip,
                            'city' => $city,
                            'region' => $region,
                            'lat' => $lat,
                            'lng' => $lng,
                            'start' => $_POST['starts_at'] ?? null,
                            'allow' => isset($_POST['allow_kids']) ? 0 : 1,
                            'vis' => $_POST['visibility'] ?? 'public',
                            'status' => 'published',
                        ]);
                        $info = 'Event gespeichert.';
                        // Punkte für Event-Erstellung vergeben
                        try {
                            $cfgPath = dirname(__DIR__, 2) . '/config/community.php';
                            $communityCfg = file_exists($cfgPath) ? require $cfgPath : [];
                            $community = new Community($pdo, $communityCfg);
                            $community->addPoints($userId, 'event', 'create', ['event_id' => $pdo?->lastInsertId()]);
                        } catch (\Throwable) {
                            // still continue, points optional
                        }
                    } else {
                        $eventId = (int)($_POST['event_id'] ?? 0);
                        $stmt = $pdo?->prepare('UPDATE events SET title=:title, teaser_public=:teaser, description=:descr, location_label=:loc, street=:street, zip=:zip, city=:city, region=:region, lat=:lat, lng=:lng, starts_at=:start, allow_kids=:allow, visibility=:vis, updated_at=NOW() WHERE id=:id AND created_by=:uid');
                        $stmt?->execute([
                            'id' => $eventId,
                            'uid' => $userId,
                            'title' => trim((string)$_POST['title']),
                            'teaser' => trim((string)$_POST['teaser']),
                            'descr' => trim((string)$_POST['description']),
                            'loc' => trim((string)$_POST['location_label']),
                            'street' => $street ?: null,
                            'zip' => $zip,
                            'city' => $city,
                            'region' => $region,
                            'lat' => $lat,
                            'lng' => $lng,
                            'start' => $_POST['starts_at'] ?? null,
                            'allow' => isset($_POST['allow_kids']) ? 0 : 1,
                            'vis' => $_POST['visibility'] ?? 'public',
                        ]);
                        $info = 'Event aktualisiert.';
                    }
                } elseif ($action === 'event_delete') {
                    $eventId = (int)($_POST['event_id'] ?? 0);
                    $stmt = $pdo?->prepare('SELECT id, created_by, status, (SELECT COUNT(*) FROM event_participants ep WHERE ep.event_id = events.id) AS participant_count FROM events WHERE id = :id LIMIT 1');
                    $stmt?->execute(['id' => $eventId]);
                    $ev = $stmt?->fetch(\PDO::FETCH_ASSOC);
                    if (!$ev || (int)$ev['created_by'] !== $userId) {
                        throw new \RuntimeException('Event nicht gefunden.');
                    }
                    if ((int)$ev['participant_count'] > 0) {
                        throw new \RuntimeException('Event hat Anmeldungen und kann nicht gelöscht werden.');
                    }
                    $pdo?->prepare('DELETE FROM events WHERE id = :id')->execute(['id' => $eventId]);
                    $info = 'Event gelöscht.';
                } elseif ($action === 'event_cancel') {
                    $eventId = (int)($_POST['event_id'] ?? 0);
                    $stmt = $pdo?->prepare('SELECT id, created_by FROM events WHERE id = :id LIMIT 1');
                    $stmt?->execute(['id' => $eventId]);
                    $ev = $stmt?->fetch(\PDO::FETCH_ASSOC);
                    if (!$ev || (int)$ev['created_by'] !== $userId) {
                        throw new \RuntimeException('Event nicht gefunden.');
                    }
                    $pdo?->prepare('UPDATE events SET status = :st, updated_at = NOW() WHERE id = :id')->execute([
                        'st' => 'cancelled',
                        'id' => $eventId,
                    ]);
                    $info = 'Event wurde abgesagt.';
                }
            } catch (\Throwable $e) {
                $error = $e->getMessage();
            }
        }

        // Daten laden
        $profile = [
            'display_name' => '',
            'first_name' => '',
            'last_name' => '',
            'zip' => '',
            'city' => '',
            'profession' => '',
            'languages' => '',
            'about' => '',
            'email' => '',
            'contact_phone' => '',
        ];
        $stmt = $pdo?->prepare('SELECT u.email, u.status, p.display_name, p.first_name, p.last_name, p.zip, p.city, p.profession, p.languages, p.about, p.contact_phone FROM users u LEFT JOIN user_profiles p ON p.user_id = u.id WHERE u.id = :id LIMIT 1');
        $stmt?->execute(['id' => $userId]);
        $row = $stmt?->fetch(\PDO::FETCH_ASSOC);
        if ($row) {
            $profile = array_merge($profile, array_filter($row, fn($v) => $v !== null));
            if ($crypto && !empty($profile['contact_phone'])) {
                $profile['contact_phone'] = $crypto->decrypt((string)$profile['contact_phone']) ?: '';
            }
        }

        $children = [];
        $stmt = $pdo?->prepare('SELECT id, encrypted_first_name AS first_name, note, gender, birthdate, age_years FROM children WHERE user_id = :id ORDER BY id DESC');
        $stmt?->execute(['id' => $userId]);
        $childrenRaw = $stmt?->fetchAll(\PDO::FETCH_ASSOC) ?: [];
        foreach ($childrenRaw as $c) {
            if ($crypto) {
                $c['first_name'] = $crypto->decrypt((string)$c['first_name']) ?: '';
                $c['note'] = $crypto->decrypt((string)($c['note'] ?? '')) ?: '';
            }
            $children[] = $c;
        }

        $eventsUpcoming = [];
        $eventsPast = [];
        $editEvent = null;
        $stmt = $pdo?->prepare(
          'SELECT e.id, e.title, e.teaser_public, e.description, e.location_label, e.street, e.zip, e.city, e.region, e.starts_at, e.allow_kids, e.visibility, e.status, e.lat, e.lng,
                  (SELECT COUNT(*) FROM event_participants ep WHERE ep.event_id = e.id) AS participant_count
           FROM events e
           WHERE e.created_by = :id AND e.starts_at >= NOW()
           ORDER BY e.starts_at ASC'
        );
        $stmt?->execute(['id' => $userId]);
        $eventsUpcoming = $stmt?->fetchAll(\PDO::FETCH_ASSOC) ?: [];

        $stmt = $pdo?->prepare(
          'SELECT e.id, e.title, e.teaser_public, e.starts_at, e.city, e.visibility, e.status,
                  (SELECT COUNT(*) FROM event_participants ep WHERE ep.event_id = e.id) AS participant_count
           FROM events e
           WHERE e.created_by = :id AND e.starts_at < NOW()
           ORDER BY e.starts_at DESC'
        );
        $stmt?->execute(['id' => $userId]);
        $eventsPast = $stmt?->fetchAll(\PDO::FETCH_ASSOC) ?: [];

        if (isset($_GET['edit_event'])) {
          $editId = (int)$_GET['edit_event'];
          $stmt = $pdo?->prepare('SELECT * FROM events WHERE id = :id AND created_by = :uid AND starts_at >= NOW() LIMIT 1');
          $stmt?->execute(['id' => $editId, 'uid' => $userId]);
          $editEvent = $stmt?->fetch(\PDO::FETCH_ASSOC) ?: null;
        }

        return compact('flash','info','error','profile','children','eventsUpcoming','eventsPast','editEvent');
    }

    private static function geocodeAddress(?string $street, ?string $zip, ?string $city, ?string $region): array
    {
        $parts = array_filter([
            $street ?: null,
            $zip ?: null,
            $city ?: null,
            $region ?: null,
        ]);
        if (!$parts) {
            return [null, null, null];
        }

        $query = implode(', ', $parts);
        $url = 'https://nominatim.openstreetmap.org/search?' . http_build_query([
            'format' => 'jsonv2',
            'limit' => 1,
            'q' => $query,
        ]);

        $ctx = stream_context_create([
            'http' => [
                'method' => 'GET',
                'header' => "User-Agent: papa-kind-treff/1.0\r\nAccept-Language: de\r\n",
                'timeout' => 6,
            ],
        ]);

        $resp = @file_get_contents($url, false, $ctx);
        if ($resp === false) {
            return [null, null, null];
        }
        $json = json_decode($resp, true);
        if (!is_array($json) || empty($json[0]['lat']) || empty($json[0]['lon'])) {
            return [null, null, null];
        }
        $addr = $json[0]['address'] ?? [];
        $regionGuess = $addr['city_district'] ?? $addr['suburb'] ?? $addr['state'] ?? $addr['county'] ?? $addr['region'] ?? $addr['state_district'] ?? null;
        return [round((float)$json[0]['lat'], 7), round((float)$json[0]['lon'], 7), $regionGuess];
    }
}
