import os
import sqlite3
import uuid
from datetime import datetime
from functools import wraps
from pathlib import Path
from zoneinfo import ZoneInfo, ZoneInfoNotFoundError

from flask import (
    Flask,
    flash,
    g,
    redirect,
    render_template,
    request,
    send_from_directory,
    session,
    url_for,
)
from werkzeug.exceptions import RequestEntityTooLarge
from werkzeug.security import check_password_hash, generate_password_hash
from werkzeug.utils import secure_filename

app = Flask(__name__)
base_dir = Path(__file__).resolve().parent
app.config["SECRET_KEY"] = os.environ.get("SECRET_KEY", "change-me-in-production")
app.config["DB_PATH"] = os.environ.get("DB_PATH", str(base_dir / "app.sqlite3"))
app.config["UPLOAD_FOLDER"] = os.environ.get("UPLOAD_FOLDER", str(base_dir / "uploads"))
app.config["MAX_CONTENT_LENGTH"] = int(os.environ.get("MAX_UPLOAD_BYTES", str(64 * 1024 * 1024)))
app.config["LOCATION_NAME"] = os.environ.get("LOCATION_NAME", "Schlossgarten Venue")
app.config["LOCATION_ADDRESS"] = os.environ.get("LOCATION_ADDRESS", "Musterstraße 12, 12345 Musterstadt")
app.config["LOCATION_WEBSITE_URL"] = os.environ.get("LOCATION_WEBSITE_URL", "https://example.com/location")
app.config["GOOGLE_MAPS_EMBED_URL"] = os.environ.get(
    "GOOGLE_MAPS_EMBED_URL",
    "https://www.google.com/maps/embed?pb=!1m18!1m12!1m3!1d0",
)
app.config["WEDDING_DATE"] = os.environ.get("WEDDING_DATE", "")
app.config["WEDDING_COUNTDOWN_ISO"] = os.environ.get("WEDDING_COUNTDOWN_ISO", "")
app.config["WEDDING_COUNTDOWN_LOCAL"] = os.environ.get("WEDDING_COUNTDOWN_LOCAL", "2026-09-04 15:00")
app.config["WEDDING_TIMEZONE"] = os.environ.get("WEDDING_TIMEZONE", "Europe/Berlin")
app.config["HERO_IMAGE_FILENAME"] = os.environ.get("HERO_IMAGE_FILENAME")

ALLOWED_EXTENSIONS = {"jpg", "jpeg", "png", "heic", "heif"}
ALLOWED_MIME_TYPES = {
    "image/jpeg",
    "image/jpg",
    "image/png",
    "image/heic",
    "image/heif",
    "image/heic-sequence",
    "image/heif-sequence",
}

DEFAULT_INVITATION_GROUPS = [
    {
        "name": "Bubus",
        "password": "Bubu!Herz24#",
        "role": "admin",
        "members": ["Svenja", "Dominic"],
    },
    {"name": "Remi", "password": "Remi#Ring24!", "role": "guest", "members": ["Remi"]},
    {"name": "Chantal", "password": "Chan!Tanz24#", "role": "guest", "members": ["Chantal"]},
    {"name": "Madeleine", "password": "Madi$Rose24!", "role": "guest", "members": ["Madeleine"]},
    {
        "name": "Julie & Daniel",
        "password": "Juli&Dan24!#",
        "role": "guest",
        "members": ["Julie", "Daniel"],
    },
    {
        "name": "Tim & Sophie",
        "password": "Tim+Sofi24!#",
        "role": "guest",
        "members": ["Tim", "Sophie"],
    },
    {
        "name": "Marcel & Kathrin",
        "password": "Marc&Kath24#",
        "role": "guest",
        "members": ["Marcel", "Kathrin"],
    },
    {
        "name": "Familie Olsem",
        "password": "Olse!Fam24#?",
        "role": "guest",
        "members": ["Laura", "Sven", "Lena", "Finn"],
    },
    {
        "name": "Maxime",
        "password": "Maxi#Love24!",
        "role": "guest",
        "members": ["Maxime", "Freund"],
    },
    {
        "name": "Familie Löster",
        "password": "Loes@Ring24#",
        "role": "guest",
        "members": ["Claudia", "Mario", "Mélodie"],
    },
    {
        "name": "Familie Thiels",
        "password": "Thie$Fest24!",
        "role": "guest",
        "members": ["Matthias", "Opa Bernd", "Oma Heidi"],
    },
    {
        "name": "Familie Gollor",
        "password": "Goll%Herz24!",
        "role": "guest",
        "members": ["Michael", "Christin", "Bruno"],
    },
    {"name": "Monika", "password": "Moni!Rose24#", "role": "guest", "members": ["Monika"]},
    {
        "name": "Familie Konrad",
        "password": "Konr#Fest24!",
        "role": "guest",
        "members": ["Michael", "Sandra", "Christoph", "Alexander"],
    },
    {"name": "Mark", "password": "Mark!Gold24#", "role": "guest", "members": ["Mark"]},
    {"name": "Elias", "password": "Elia$Ring24!", "role": "guest", "members": ["Elias"]},
    {"name": "Milan", "password": "Mila#Tanz24!", "role": "guest", "members": ["Milan"]},
    {
        "name": "Familie Wolff",
        "password": "Wolf!Herz24#",
        "role": "guest",
        "members": ["Anja", "Bodo"],
    },
    {
        "name": "Anna & Leon",
        "password": "Anna&Leo24!#",
        "role": "guest",
        "members": ["Anna", "Leon"],
    },
    {"name": "Aryan", "password": "Arya!Fest24#", "role": "guest", "members": ["Aryan"]},
    {
        "name": "Sebastian",
        "password": "Seba$Ring24!",
        "role": "guest",
        "members": ["Sebastian", "Olivia"],
    },
    {
        "name": "Leander & Heni",
        "password": "Lea&Heni24!#",
        "role": "guest",
        "members": ["Leander", "Heni"],
    },
    {"name": "Flo", "password": "Flo!Liebe24#", "role": "guest", "members": ["Flo"]},
    {
        "name": "Nico & Pia",
        "password": "Nico&Pia24!#",
        "role": "guest",
        "members": ["Nico", "Pia"],
    },
    {"name": "Kiki", "password": "Kiki!Rose24#", "role": "guest", "members": ["Kiki"]},
    {
        "name": "Lana & Eric",
        "password": "Lan&Eric24!#",
        "role": "guest",
        "members": ["Lana", "Eric"],
    },
    {"name": "Britta", "password": "Brit!Tanz24#", "role": "guest", "members": ["Britta"]},
    {"name": "Holzi", "password": "Holz!Ring24#", "role": "guest", "members": ["Holzi"]},
    {"name": "Eirene", "password": "Eire$Fest24!", "role": "guest", "members": ["Eirene"]},
    {
        "name": "Family Hynes",
        "password": "Hyne#Love24!",
        "role": "guest",
        "members": ["Steven", "Martha", "William", "Tim", "Steven Jr."],
    },
    {"name": "Timbo", "password": "Timb!Rose24#", "role": "guest", "members": ["Timbo"]},
    {
        "name": "Karen & Jay",
        "password": "Kare&Jay24!#",
        "role": "guest",
        "members": ["Karen", "Jay"],
    },
    {"name": "Alina", "password": "Alin!Gold24#", "role": "guest", "members": ["Alina"]},
    {"name": "Max", "password": "Max!Liebe24#", "role": "guest", "members": ["Max"]},
    {
        "name": "Paul & Alix",
        "password": "Paul&Alx24!#",
        "role": "guest",
        "members": ["Paul", "Alix"],
    },
    {
        "name": "Alfred & Nadia",
        "password": "Alfr&Nad24!#",
        "role": "guest",
        "members": ["Alfred", "Nadia"],
    },
    {
        "name": "Anne-Marie & Erny",
        "password": "Anne&Ern24!#",
        "role": "guest",
        "members": ["Anne-Marie", "Erny"],
    },
    {
        "name": "Familie Kieffer",
        "password": "Kief!Fest24#",
        "role": "guest",
        "members": ["Anny", "John", "Jana"],
    },
]
AGE_REQUIRED_NAMES = {"Lena", "Finn", "Fin", "Bruno"}

TEXTS = {
    "de": {
        "brand": "Svenja & Dominic",
        "subtitle": "Willkommen zu unserer Hochzeits-App",
        "login_note": "Passwortgeschützter Zugriff für unsere Gäste.",
        "login": "Login",
        "group_name": "Gruppenname",
        "group_password": "Gruppenpasswort",
        "login_submit": "Weiter zum Gästebereich",
        "guest_area": "Gästebereich",
        "hello_guest": "Hallo {name}.",
        "logout": "Abmelden",
        "rsvp": "RSVP",
        "upload": "Upload",
        "upload_intro": "Hier könnt ihr Fotos von der Hochzeit hochladen.",
        "gallery": "Galerie",
        "host_area": "Gastgeberbereich",
        "info": "Infos",
        "save": "Speichern",
        "attending": "Ich komme",
        "not_attending": "Ich komme nicht",
        "member_status": "Rückmeldung",
        "member_age": "Alter",
        "member_age_hint": "Bitte Alter eintragen, wenn die Person kommt.",
        "rsvp_members_intro": "Bitte gib die Zu- oder Absage pro Mitglied an.",
        "file": "Bild auswählen",
        "upload_picker_hint": "Tippe hier, um ein oder mehrere Bilder auszuwählen.",
        "add_more_files": "Weitere Datei hinzufügen",
        "upload_multi_hint": "Du kannst mehrere Bilder auf einmal auswählen oder weitere Felder hinzufügen.",
        "upload_selected_count": "{count} Bilder ausgewählt",
        "upload_ready": "Bereit zum Hochladen",
        "upload_submit": "Foto hochladen",
        "schedule": "Ablauf",
        "hotels": "Hotels",
        "taxi": "Taxi",
        "location": "Location",
        "visit_location": "Zur Location-Webseite",
        "maps_privacy_notice": "Zur Anzeige der Karte werden Daten an Google übertragen.",
        "maps_load_button": "Google Maps anzeigen",
        "privacy": "Datenschutz",
        "imprint": "Impressum",
        "hero_headline": "Willkommen zu unserer Hochzeit",
        "hero_text": "Wir freuen uns riesig, diesen besonderen Tag mit euch zu feiern.",
        "to_guest_area": "Zum Gästebereich",
        "schedule_text": "15:00 Trauung, 17:00 Empfang, 19:00 Dinner.",
        "hotels_text": "Empfehlungen folgen. Bitte frühzeitig buchen.",
        "taxi_text": "Taxi-Service: 01234 / 567890 (24/7).",
        "gallery_uploaded_by": "von {name}",
        "gallery_empty": "Noch keine Bilder vorhanden.",
        "gallery_image_alt": "Upload von {name}",
        "flash_enter_group_name": "Bitte Gruppenname eingeben.",
        "flash_invalid_group_login": "Ungültiger Gruppenname oder Passwort.",
        "flash_rsvp_select": "Bitte für alle Mitglieder eine RSVP-Auswahl treffen.",
        "flash_rsvp_age_missing": "Bitte Alter für {name} angeben.",
        "flash_rsvp_age_invalid": "Bitte ein gültiges Alter (0-17) für {name} angeben.",
        "flash_rsvp_saved": "RSVP gespeichert.",
        "flash_select_image": "Bitte eine Bilddatei auswählen.",
        "flash_allowed_types": "Nur JPG/JPEG/PNG/HEIC/HEIF sind erlaubt.",
        "flash_upload_success_count": "{count} Bilder erfolgreich hochgeladen.",
        "flash_upload_too_large": "Upload zu groß. Bitte in kleineren Paketen hochladen (max. {max_mb} MB pro Anfrage).",
        "flash_upload_failed": "Upload fehlgeschlagen. Bitte erneut versuchen.",
        "host_stats_title": "Übersicht",
        "total_guests": "Gäste gesamt",
        "attending_yes": "Zusagen",
        "attending_no": "Absagen",
        "attending_open": "Noch offen",
        "host_table_group": "Gruppe",
        "host_table_member": "Mitglied",
        "host_table_status": "RSVP",
        "host_table_age": "Alter",
        "status_yes": "Kommt",
        "status_no": "Kommt nicht",
        "status_open": "Offen",
        "legal_de_authoritative": "Bei rechtlichen Unklarheiten gilt die deutsche Fassung.",
        "download": "Download",
        "delete": "Löschen",
        "flash_delete_not_allowed": "Du darfst dieses Bild nicht löschen.",
        "flash_image_deleted": "Bild gelöscht.",
        "flash_admin_only": "Dieser Bereich ist nur für Admins verfügbar.",
        "dashboard": "Dashboard",
        "back": "Zurück",
        "countdown_button_label": "Countdown bis zur Hochzeit",
        "countdown_until": "Noch",
        "countdown_started": "Die Feier hat begonnen",
        "countdown_days": "Tage",
        "countdown_hours": "Std",
        "countdown_minutes": "Min",
        "countdown_seconds": "Sek",
        "countdown_subline": "bis zur Hochzeit",
    },
    "en": {
        "brand": "Svenja & Dominic",
        "subtitle": "Welcome to our wedding app",
        "login_note": "Password-protected access for our guests.",
        "login": "Login",
        "group_name": "Group name",
        "group_password": "Group password",
        "login_submit": "Open guest area",
        "guest_area": "Guest Area",
        "hello_guest": "Hello {name}.",
        "logout": "Logout",
        "rsvp": "RSVP",
        "upload": "Upload",
        "upload_intro": "Upload your wedding photos here.",
        "gallery": "Gallery",
        "host_area": "Host Area",
        "info": "Info",
        "save": "Save",
        "attending": "I will attend",
        "not_attending": "I cannot attend",
        "member_status": "RSVP status",
        "member_age": "Age",
        "member_age_hint": "Please add age if this person attends.",
        "rsvp_members_intro": "Please submit attendance for each group member.",
        "file": "Select image",
        "upload_picker_hint": "Tap here to select one or more images.",
        "add_more_files": "Add more files",
        "upload_multi_hint": "You can select multiple images at once or add more file fields.",
        "upload_selected_count": "{count} images selected",
        "upload_ready": "Ready to upload",
        "upload_submit": "Upload photo",
        "schedule": "Schedule",
        "hotels": "Hotels",
        "taxi": "Taxi",
        "location": "Location",
        "visit_location": "Visit location website",
        "maps_privacy_notice": "To display the map, data will be transferred to Google.",
        "maps_load_button": "Show Google Maps",
        "privacy": "Privacy",
        "imprint": "Imprint",
        "hero_headline": "Welcome to our wedding",
        "hero_text": "We are so excited to celebrate this special day with you.",
        "to_guest_area": "Open guest area",
        "schedule_text": "3:00 PM ceremony, 5:00 PM reception, 7:00 PM dinner.",
        "hotels_text": "Recommendations will follow. Please book early.",
        "taxi_text": "Taxi service: 01234 / 567890 (24/7).",
        "gallery_uploaded_by": "by {name}",
        "gallery_empty": "No photos available yet.",
        "gallery_image_alt": "Uploaded by {name}",
        "flash_enter_group_name": "Please enter group name.",
        "flash_invalid_group_login": "Invalid group name or password.",
        "flash_rsvp_select": "Please choose RSVP values for all members.",
        "flash_rsvp_age_missing": "Please enter age for {name}.",
        "flash_rsvp_age_invalid": "Please enter a valid age (0-17) for {name}.",
        "flash_rsvp_saved": "RSVP saved.",
        "flash_select_image": "Please select an image file.",
        "flash_allowed_types": "Only JPG/JPEG/PNG/HEIC/HEIF are allowed.",
        "flash_upload_success_count": "{count} images uploaded successfully.",
        "flash_upload_too_large": "Upload too large. Please upload smaller batches (max {max_mb} MB per request).",
        "flash_upload_failed": "Upload failed. Please try again.",
        "host_stats_title": "Overview",
        "total_guests": "Total guests",
        "attending_yes": "Attending",
        "attending_no": "Declined",
        "attending_open": "Pending",
        "host_table_group": "Group",
        "host_table_member": "Member",
        "host_table_status": "RSVP",
        "host_table_age": "Age",
        "status_yes": "Attending",
        "status_no": "Not attending",
        "status_open": "Pending",
        "legal_de_authoritative": "In case of legal ambiguity, the German version shall prevail.",
        "download": "Download",
        "delete": "Delete",
        "flash_delete_not_allowed": "You are not allowed to delete this image.",
        "flash_image_deleted": "Image deleted.",
        "flash_admin_only": "This area is available to admins only.",
        "dashboard": "Dashboard",
        "back": "Back",
        "countdown_button_label": "Wedding countdown",
        "countdown_until": "Starts in",
        "countdown_started": "The celebration has started",
        "countdown_days": "Days",
        "countdown_hours": "Hrs",
        "countdown_minutes": "Min",
        "countdown_seconds": "Sec",
        "countdown_subline": "until the wedding",
    },
}


def get_lang() -> str:
    lang = session.get("lang", "de")
    return lang if lang in TEXTS else "de"


def t(key: str) -> str:
    return TEXTS[get_lang()].get(key, key)


def get_hero_image_asset() -> str:
    assets_dir = base_dir / "static" / "assets"
    configured = app.config.get("HERO_IMAGE_FILENAME")

    candidates = []
    if configured:
        candidates.append(configured)
    candidates.extend(["hero.jpg", "hero.jpeg", "hero.png", "image.png", "image-1.png"])

    for filename in candidates:
        if (assets_dir / filename).is_file():
            return f"assets/{filename}"

    return "assets/hero.jpg"


def get_wedding_countdown_iso() -> str:
    configured_iso = str(app.config.get("WEDDING_COUNTDOWN_ISO", "") or "").strip()
    if configured_iso:
        try:
            datetime.fromisoformat(configured_iso.replace("Z", "+00:00"))
            return configured_iso
        except ValueError:
            pass

    local_value = str(app.config.get("WEDDING_COUNTDOWN_LOCAL", "") or "").strip()
    timezone_name = str(app.config.get("WEDDING_TIMEZONE", "Europe/Berlin") or "Europe/Berlin").strip()
    if not local_value:
        return "2026-09-04T15:00:00+02:00"

    parsed_local = None
    for fmt in ("%Y-%m-%d %H:%M", "%Y-%m-%dT%H:%M", "%Y-%m-%d %H:%M:%S", "%Y-%m-%dT%H:%M:%S"):
        try:
            parsed_local = datetime.strptime(local_value, fmt)
            break
        except ValueError:
            continue

    if parsed_local is None:
        return "2026-09-04T15:00:00+02:00"

    try:
        tz = ZoneInfo(timezone_name)
    except ZoneInfoNotFoundError:
        tz = ZoneInfo("Europe/Berlin")

    return parsed_local.replace(tzinfo=tz).isoformat()


@app.context_processor
def inject_common() -> dict:
    role = session.get("role", "guest")
    group_id = session.get("group_id")
    return {
        "t": t,
        "lang": get_lang(),
        "guest_name": session.get("group_name"),
        "guest_id": group_id,
        "group_name": session.get("group_name"),
        "group_id": group_id,
        "is_host": role == "admin",
        "is_admin": role == "admin",
        "location_name": app.config["LOCATION_NAME"],
        "location_address": app.config["LOCATION_ADDRESS"],
        "location_website_url": app.config["LOCATION_WEBSITE_URL"],
        "google_maps_embed_url": app.config["GOOGLE_MAPS_EMBED_URL"],
        "wedding_date": app.config["WEDDING_DATE"],
        "wedding_countdown_iso": get_wedding_countdown_iso(),
        "hero_image_url": url_for("static", filename=get_hero_image_asset()),
    }


def get_db() -> sqlite3.Connection:
    db_path = app.config["DB_PATH"]
    os.makedirs(os.path.dirname(db_path), exist_ok=True)

    if "db" not in g:
        g.db = sqlite3.connect(db_path)
        g.db.row_factory = sqlite3.Row
    return g.db


@app.teardown_appcontext
def close_db(_error) -> None:
    db = g.pop("db", None)
    if db is not None:
        db.close()


def table_columns(conn: sqlite3.Connection, table_name: str) -> set[str]:
    rows = conn.execute(f"PRAGMA table_info({table_name})").fetchall()
    return {str(row[1]) for row in rows}


def init_db() -> None:
    db_path = app.config["DB_PATH"]
    db_dir = os.path.dirname(db_path)
    if db_dir:
        os.makedirs(db_dir, exist_ok=True)

    with sqlite3.connect(db_path) as conn:
        conn.execute(
            """
            CREATE TABLE IF NOT EXISTS groups (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                name TEXT NOT NULL UNIQUE,
                password_hash TEXT NOT NULL,
                role TEXT NOT NULL DEFAULT 'guest',
                created_at TEXT NOT NULL
            )
            """
        )

        conn.execute(
            """
            CREATE TABLE IF NOT EXISTS group_members (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                group_id INTEGER NOT NULL,
                name TEXT NOT NULL,
                attending INTEGER,
                child_age INTEGER,
                requires_age INTEGER NOT NULL DEFAULT 0,
                created_at TEXT NOT NULL,
                FOREIGN KEY(group_id) REFERENCES groups(id),
                UNIQUE(group_id, name)
            )
            """
        )

        conn.execute(
            """
            CREATE TABLE IF NOT EXISTS uploads (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                filename TEXT NOT NULL,
                uploaded_by INTEGER NOT NULL,
                uploaded_at TEXT NOT NULL,
                FOREIGN KEY(uploaded_by) REFERENCES groups(id)
            )
            """
        )

        member_cols = table_columns(conn, "group_members")
        if "child_age" not in member_cols:
            conn.execute("ALTER TABLE group_members ADD COLUMN child_age INTEGER")
        if "requires_age" not in member_cols:
            conn.execute("ALTER TABLE group_members ADD COLUMN requires_age INTEGER NOT NULL DEFAULT 0")
        if "created_at" not in member_cols:
            conn.execute("ALTER TABLE group_members ADD COLUMN created_at TEXT NOT NULL DEFAULT ''")

        group_cols = table_columns(conn, "groups")
        if "role" not in group_cols:
            conn.execute("ALTER TABLE groups ADD COLUMN role TEXT NOT NULL DEFAULT 'guest'")

        conn.commit()


def seed_default_groups() -> None:
    db = get_db()
    existing = db.execute("SELECT COUNT(*) AS c FROM groups").fetchone()
    if int(existing["c"] or 0) > 0:
        return

    now = datetime.utcnow().isoformat()
    for entry in DEFAULT_INVITATION_GROUPS:
        password_hash = generate_password_hash(entry["password"])
        cursor = db.execute(
            "INSERT INTO groups (name, password_hash, role, created_at) VALUES (?, ?, ?, ?)",
            (entry["name"], password_hash, entry["role"], now),
        )
        group_id = int(cursor.lastrowid)
        member_rows = []
        for member_name in entry["members"]:
            member_rows.append(
                (
                    group_id,
                    member_name,
                    1 if member_name in AGE_REQUIRED_NAMES else 0,
                    now,
                )
            )

        db.executemany(
            """
            INSERT INTO group_members (group_id, name, requires_age, created_at)
            VALUES (?, ?, ?, ?)
            """,
            member_rows,
        )

    db.commit()


def login_required(view):
    @wraps(view)
    def wrapped(*args, **kwargs):
        if "group_id" not in session:
            return redirect(url_for("landing"))
        return view(*args, **kwargs)

    return wrapped


def admin_required(view):
    @wraps(view)
    def wrapped(*args, **kwargs):
        if session.get("role") != "admin":
            flash(t("flash_admin_only"))
            return redirect(url_for("guest_area"))
        return view(*args, **kwargs)

    return wrapped


@app.errorhandler(RequestEntityTooLarge)
def handle_request_too_large(_error):
    max_mb = max(1, int(app.config.get("MAX_CONTENT_LENGTH", 0)) // (1024 * 1024))
    flash(t("flash_upload_too_large").format(max_mb=max_mb))
    if request.method == "POST":
        return redirect(url_for("upload"))
    return redirect(url_for("landing"))


def is_allowed_file(filename: str) -> bool:
    return "." in filename and filename.rsplit(".", 1)[1].lower() in ALLOWED_EXTENSIONS


@app.get("/health")
def health():
    return {"status": "ok"}


@app.get("/")
def landing():
    if "group_id" in session:
        return redirect(url_for("welcome"))
    return render_template("login.html")


@app.post("/login")
def login():
    group_name = (request.form.get("group_name") or "").strip()
    group_password = request.form.get("group_password") or ""

    if not group_name:
        flash(t("flash_enter_group_name"))
        return redirect(url_for("landing"))

    db = get_db()
    group = db.execute(
        "SELECT id, name, password_hash, role FROM groups WHERE LOWER(name) = LOWER(?)",
        (group_name,),
    ).fetchone()

    if group is None or not check_password_hash(str(group["password_hash"]), group_password):
        flash(t("flash_invalid_group_login"))
        return redirect(url_for("landing"))

    session["group_id"] = int(group["id"])
    session["group_name"] = str(group["name"])
    session["role"] = str(group["role"] or "guest")
    return redirect(url_for("welcome"))


@app.post("/logout")
def logout():
    session.clear()
    return redirect(url_for("landing"))


@app.post("/lang/<code>")
def set_lang(code: str):
    if code in TEXTS:
        session["lang"] = code
    return redirect(request.referrer or url_for("landing"))


@app.get("/welcome")
@login_required
def welcome():
    return render_template("welcome.html")


@app.get("/gaestebereich")
@app.get("/gästebereich")
@login_required
def guest_area():
    return render_template("guest_area.html")


@app.route("/gastgeberbereich", methods=["GET"])
@login_required
@admin_required
def host_area():
    db = get_db()
    stats_row = db.execute(
        """
        SELECT
            COUNT(*) AS total_guests,
            SUM(CASE WHEN attending = 1 THEN 1 ELSE 0 END) AS attending_yes,
            SUM(CASE WHEN attending = 0 THEN 1 ELSE 0 END) AS attending_no,
            SUM(CASE WHEN attending IS NULL THEN 1 ELSE 0 END) AS attending_open
        FROM group_members
        """
    ).fetchone()

    members = db.execute(
        """
        SELECT groups.name AS group_name, group_members.name, group_members.attending, group_members.child_age, group_members.requires_age
        FROM group_members
        JOIN groups ON groups.id = group_members.group_id
        ORDER BY groups.name COLLATE NOCASE ASC, group_members.name COLLATE NOCASE ASC
        """
    ).fetchall()

    stats = {
        "total_guests": int(stats_row["total_guests"] or 0),
        "attending_yes": int(stats_row["attending_yes"] or 0),
        "attending_no": int(stats_row["attending_no"] or 0),
        "attending_open": int(stats_row["attending_open"] or 0),
    }
    return render_template("host_area.html", stats=stats, members=members)


@app.get("/dashboard")
@login_required
def dashboard():
    return redirect(url_for("guest_area"))


@app.route("/rsvp", methods=["GET", "POST"])
@login_required
def rsvp():
    db = get_db()
    current_group_id = int(session["group_id"])

    members = db.execute(
        """
        SELECT id, name, attending, child_age, requires_age
        FROM group_members
        WHERE group_id = ?
        ORDER BY id ASC
        """,
        (current_group_id,),
    ).fetchall()

    if request.method == "POST":
        updates = []
        for member in members:
            member_id = int(member["id"])
            attending_raw = request.form.get(f"attending_{member_id}")

            if attending_raw not in {"yes", "no"}:
                flash(t("flash_rsvp_select"))
                return redirect(url_for("rsvp"))

            attending = 1 if attending_raw == "yes" else 0
            child_age = None
            if attending == 1 and int(member["requires_age"] or 0) == 1:
                age_raw = (request.form.get(f"age_{member_id}") or "").strip()
                if not age_raw:
                    flash(t("flash_rsvp_age_missing").format(name=member["name"]))
                    return redirect(url_for("rsvp"))
                if not age_raw.isdigit():
                    flash(t("flash_rsvp_age_invalid").format(name=member["name"]))
                    return redirect(url_for("rsvp"))
                age_value = int(age_raw)
                if age_value < 0 or age_value > 17:
                    flash(t("flash_rsvp_age_invalid").format(name=member["name"]))
                    return redirect(url_for("rsvp"))
                child_age = age_value

            updates.append((attending, child_age, member_id, current_group_id))

        db.executemany(
            """
            UPDATE group_members
            SET attending = ?, child_age = ?
            WHERE id = ? AND group_id = ?
            """,
            updates,
        )
        db.commit()
        flash(t("flash_rsvp_saved"))
        return redirect(url_for("rsvp"))

    return render_template("rsvp.html", members=members)


@app.route("/upload", methods=["GET", "POST"])
@login_required
def upload():
    if request.method == "POST":
        try:
            files = [f for f in request.files.getlist("photo") if f and f.filename]
            if not files:
                flash(t("flash_select_image"))
                return redirect(url_for("upload"))

            for file in files:
                if not is_allowed_file(file.filename):
                    flash(t("flash_allowed_types"))
                    return redirect(url_for("upload"))
                mime_type = (file.mimetype or "").lower()
                if mime_type and mime_type not in ALLOWED_MIME_TYPES:
                    flash(t("flash_allowed_types"))
                    return redirect(url_for("upload"))

            upload_dir = app.config["UPLOAD_FOLDER"]
            os.makedirs(upload_dir, exist_ok=True)
            db = get_db()
            now = datetime.utcnow().isoformat()
            upload_rows = []
            for file in files:
                safe_name = secure_filename(file.filename)
                if "." not in safe_name:
                    flash(t("flash_allowed_types"))
                    return redirect(url_for("upload"))
                ext = safe_name.rsplit(".", 1)[1].lower()
                stored_name = f"{uuid.uuid4().hex}.{ext}"
                file.save(os.path.join(upload_dir, stored_name))
                upload_rows.append((stored_name, int(session["group_id"]), now))

            db.executemany(
                "INSERT INTO uploads (filename, uploaded_by, uploaded_at) VALUES (?, ?, ?)",
                upload_rows,
            )
            db.commit()

            flash(t("flash_upload_success_count").format(count=len(upload_rows)))
            return redirect(url_for("gallery"))
        except RequestEntityTooLarge:
            raise
        except Exception:
            app.logger.exception("Upload failed")
            flash(t("flash_upload_failed"))
            return redirect(url_for("upload"))

    return render_template("upload.html")


@app.get("/gallery")
@login_required
def gallery():
    db = get_db()
    images = db.execute(
        """
        SELECT uploads.id, uploads.filename, uploads.uploaded_by, uploads.uploaded_at,
               COALESCE(groups.name, 'Unbekannt') AS uploaded_by_name
        FROM uploads
        LEFT JOIN groups ON groups.id = uploads.uploaded_by
        ORDER BY uploads.id DESC
        """
    ).fetchall()
    return render_template("gallery.html", images=images)


@app.post("/gallery/delete/<int:image_id>")
@login_required
def delete_image(image_id: int):
    db = get_db()
    image = db.execute(
        "SELECT id, filename, uploaded_by FROM uploads WHERE id = ?",
        (image_id,),
    ).fetchone()
    if image is None:
        return redirect(url_for("gallery"))

    current_group_id = int(session["group_id"])
    is_admin = session.get("role") == "admin"
    if not is_admin and int(image["uploaded_by"]) != current_group_id:
        flash(t("flash_delete_not_allowed"))
        return redirect(url_for("gallery"))

    db.execute("DELETE FROM uploads WHERE id = ?", (image_id,))
    db.commit()

    file_path = os.path.join(app.config["UPLOAD_FOLDER"], image["filename"])
    if os.path.isfile(file_path):
        os.remove(file_path)

    flash(t("flash_image_deleted"))
    return redirect(url_for("gallery"))


@app.get("/uploads/<path:filename>")
@login_required
def serve_upload(filename: str):
    as_attachment = request.args.get("download") == "1"
    return send_from_directory(
        app.config["UPLOAD_FOLDER"],
        filename,
        as_attachment=as_attachment,
        download_name=filename if as_attachment else None,
    )


@app.get("/info/<page>")
@login_required
def info(page: str):
    allowed = {"schedule", "hotels", "taxi", "location"}
    if page not in allowed:
        return redirect(url_for("guest_area"))
    return render_template("info.html", page=page)


@app.get("/datenschutz")
def datenschutz():
    return render_template("datenschutz.html")


@app.get("/impressum")
def impressum():
    return render_template("impressum.html")


init_db()
with app.app_context():
    seed_default_groups()

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=8000)
