from __future__ import annotations

import os
import sqlite3
from datetime import datetime
from functools import wraps
from pathlib import Path

from flask import (
    Flask,
    flash,
    g,
    jsonify,
    redirect,
    render_template_string,
    request,
    session,
    url_for,
)
from werkzeug.security import check_password_hash, generate_password_hash


BASE_DIR = Path(__file__).resolve().parent
DATABASE = BASE_DIR / "residenthub.sqlite3"

app = Flask(__name__)
app.config["SECRET_KEY"] = os.environ.get("RESIDENTHUB_SECRET", "dev-residenthub-change-me")
application = app

ROLES = {"manager", "tenant", "janitor"}
REPAIR_STATUSES = ["Open", "Assigned", "In Progress", "Resolved"]
BILL_TYPES = ["Water", "Electricity", "Internet", "Trash", "Security", "Other"]


def get_db() -> sqlite3.Connection:
    if "db" not in g:
        g.db = sqlite3.connect(DATABASE)
        g.db.row_factory = sqlite3.Row
        g.db.execute("PRAGMA foreign_keys = ON")
    return g.db


@app.teardown_appcontext
def close_db(_: Exception | None = None) -> None:
    db = g.pop("db", None)
    if db is not None:
        db.close()


def query_all(sql: str, args: tuple = ()) -> list[sqlite3.Row]:
    return get_db().execute(sql, args).fetchall()


def query_one(sql: str, args: tuple = ()) -> sqlite3.Row | None:
    return get_db().execute(sql, args).fetchone()


def execute(sql: str, args: tuple = ()) -> sqlite3.Cursor:
    db = get_db()
    cursor = db.execute(sql, args)
    db.commit()
    return cursor


def init_db() -> None:
    db = get_db()
    db.executescript(
        """
        CREATE TABLE IF NOT EXISTS users (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            name TEXT NOT NULL,
            email TEXT NOT NULL UNIQUE,
            password_hash TEXT NOT NULL,
            role TEXT NOT NULL CHECK(role IN ('manager', 'tenant', 'janitor')),
            unit TEXT,
            phone TEXT,
            created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP
        );

        CREATE TABLE IF NOT EXISTS properties (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            name TEXT NOT NULL,
            address TEXT NOT NULL,
            units INTEGER NOT NULL,
            occupancy INTEGER NOT NULL
        );

        CREATE TABLE IF NOT EXISTS rent_charges (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            tenant_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
            month TEXT NOT NULL,
            amount REAL NOT NULL,
            due_date TEXT NOT NULL,
            status TEXT NOT NULL CHECK(status IN ('Paid', 'Due', 'Overdue')) DEFAULT 'Due',
            paid_at TEXT,
            UNIQUE(tenant_id, month)
        );

        CREATE TABLE IF NOT EXISTS utility_bills (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            tenant_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
            bill_type TEXT NOT NULL,
            period TEXT NOT NULL,
            amount REAL NOT NULL,
            due_date TEXT NOT NULL,
            status TEXT NOT NULL CHECK(status IN ('Paid', 'Due', 'Overdue')) DEFAULT 'Due',
            paid_at TEXT
        );

        CREATE TABLE IF NOT EXISTS repair_requests (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            tenant_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
            janitor_id INTEGER REFERENCES users(id) ON DELETE SET NULL,
            title TEXT NOT NULL,
            location TEXT NOT NULL,
            priority TEXT NOT NULL CHECK(priority IN ('Low', 'Medium', 'High', 'Urgent')),
            status TEXT NOT NULL CHECK(status IN ('Open', 'Assigned', 'In Progress', 'Resolved')) DEFAULT 'Open',
            description TEXT NOT NULL,
            created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
            updated_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP
        );

        CREATE TABLE IF NOT EXISTS activity_log (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            actor_id INTEGER REFERENCES users(id) ON DELETE SET NULL,
            message TEXT NOT NULL,
            created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP
        );
        """
    )
    db.commit()
    cleanup_demo_data()
    ensure_default_property()


def cleanup_demo_data() -> None:
    demo_emails = (
        "manager@residenthub.test",
        "tenant@residenthub.test",
        "tenant2@residenthub.test",
        "janitor@residenthub.test",
        "janitor2@residenthub.test",
    )
    placeholders = ",".join("?" for _ in demo_emails)
    db = get_db()
    db.execute(f"DELETE FROM users WHERE email IN ({placeholders})", demo_emails)
    db.execute(
        "UPDATE properties SET name = ?, address = ?, units = ?, occupancy = ? WHERE name = ?",
        ("Sync Space Residences", "syncspace.co.ke", 1, 0, "ResidentHub Garden Court"),
    )
    db.execute("DELETE FROM activity_log WHERE message LIKE '%demo workspace seeded%'")
    db.commit()


def ensure_default_property() -> None:
    if query_one("SELECT id FROM properties LIMIT 1"):
        return
    execute(
        "INSERT INTO properties (name, address, units, occupancy) VALUES (?, ?, ?, ?)",
        ("Sync Space Residences", "syncspace.co.ke", 1, 0),
    )


@app.before_request
def ensure_database_ready() -> None:
    if not app.config.get("_DB_READY"):
        init_db()
        app.config["_DB_READY"] = True


def current_user() -> sqlite3.Row | None:
    user_id = session.get("user_id")
    if not user_id:
        return None
    return query_one("SELECT * FROM users WHERE id = ?", (user_id,))


def has_manager() -> bool:
    return query_one("SELECT id FROM users WHERE role = 'manager' LIMIT 1") is not None


def login_required(view):
    @wraps(view)
    def wrapped(*args, **kwargs):
        if not current_user():
            return redirect(url_for("login"))
        return view(*args, **kwargs)

    return wrapped


def role_required(*roles: str):
    def decorator(view):
        @wraps(view)
        def wrapped(*args, **kwargs):
            user = current_user()
            if not user:
                return redirect(url_for("login"))
            if user["role"] not in roles:
                return jsonify({"ok": False, "error": "Permission denied"}), 403
            return view(*args, **kwargs)

        return wrapped

    return decorator


def log(actor_id: int | None, message: str) -> None:
    execute("INSERT INTO activity_log (actor_id, message) VALUES (?, ?)", (actor_id, message))


def money(value: float | int | None) -> str:
    return f"Ksh{float(value or 0):,.2f}"


def row_to_dict(row: sqlite3.Row) -> dict:
    return {key: row[key] for key in row.keys()}


def dashboard_data(user: sqlite3.Row) -> dict:
    where_tenant = "" if user["role"] == "manager" else "WHERE tenant_id = ?"
    tenant_args = () if user["role"] == "manager" else (user["id"],)

    if user["role"] == "janitor":
        repairs = query_all(
            """
            SELECT r.*, t.name AS tenant_name, t.unit, j.name AS janitor_name
            FROM repair_requests r
            JOIN users t ON t.id = r.tenant_id
            LEFT JOIN users j ON j.id = r.janitor_id
            WHERE r.janitor_id = ? OR r.status = 'Open'
            ORDER BY r.created_at DESC
            """,
            (user["id"],),
        )
        rent = []
        bills = []
    else:
        rent = query_all(
            f"""
            SELECT rc.*, u.name AS tenant_name, u.unit
            FROM rent_charges rc
            JOIN users u ON u.id = rc.tenant_id
            {where_tenant}
            ORDER BY rc.due_date DESC
            """,
            tenant_args,
        )
        bills = query_all(
            f"""
            SELECT ub.*, u.name AS tenant_name, u.unit
            FROM utility_bills ub
            JOIN users u ON u.id = ub.tenant_id
            {where_tenant}
            ORDER BY ub.due_date DESC
            """,
            tenant_args,
        )
        repairs = query_all(
            f"""
            SELECT r.*, t.name AS tenant_name, t.unit, j.name AS janitor_name
            FROM repair_requests r
            JOIN users t ON t.id = r.tenant_id
            LEFT JOIN users j ON j.id = r.janitor_id
            {where_tenant.replace('tenant_id', 'r.tenant_id')}
            ORDER BY r.created_at DESC
            """,
            tenant_args,
        )

    totals = {
        "rent_due": sum(row["amount"] for row in rent if row["status"] != "Paid"),
        "utilities_due": sum(row["amount"] for row in bills if row["status"] != "Paid"),
        "open_repairs": sum(1 for row in repairs if row["status"] != "Resolved"),
        "paid_this_month": sum(row["amount"] for row in rent + bills if row["status"] == "Paid"),
    }

    property_row = query_one("SELECT * FROM properties ORDER BY id LIMIT 1")
    managers = query_all("SELECT id, name, email, phone FROM users WHERE role = 'manager' ORDER BY name")
    tenants = query_all("SELECT id, name, email, unit, phone FROM users WHERE role = 'tenant' ORDER BY unit")
    janitors = query_all("SELECT id, name, email, phone FROM users WHERE role = 'janitor' ORDER BY name")
    activity = query_all(
        """
        SELECT a.*, u.name AS actor_name
        FROM activity_log a
        LEFT JOIN users u ON u.id = a.actor_id
        ORDER BY a.created_at DESC
        LIMIT 12
        """
    )

    return {
        "user": row_to_dict(user),
        "property": row_to_dict(property_row) if property_row else {},
        "rent": [row_to_dict(row) for row in rent],
        "bills": [row_to_dict(row) for row in bills],
        "repairs": [row_to_dict(row) for row in repairs],
        "managers": [row_to_dict(row) for row in managers],
        "tenants": [row_to_dict(row) for row in tenants],
        "janitors": [row_to_dict(row) for row in janitors],
        "activity": [row_to_dict(row) for row in activity],
        "totals": totals,
        "money": money,
        "bill_types": BILL_TYPES,
        "repair_statuses": REPAIR_STATUSES,
    }


@app.route("/")
def index():
    user = current_user()
    if user:
        return redirect(url_for("dashboard"))
    if not has_manager():
        return redirect(url_for("setup"))
    return redirect(url_for("login"))


@app.route("/setup", methods=["GET", "POST"])
def setup():
    if has_manager():
        return redirect(url_for("login"))
    property_row = query_one("SELECT * FROM properties ORDER BY id LIMIT 1")
    if request.method == "POST":
        password = request.form.get("password", "")
        confirm_password = request.form.get("confirm_password", "")
        if len(password) < 8:
            flash("Use at least 8 characters for the manager password.")
        elif password != confirm_password:
            flash("The passwords do not match.")
        else:
            cursor = execute(
                """
                INSERT INTO users (name, email, password_hash, role, unit, phone)
                VALUES (?, ?, ?, 'manager', NULL, ?)
                """,
                (
                    request.form["name"].strip(),
                    request.form["email"].strip().lower(),
                    generate_password_hash(password),
                    request.form.get("phone") or None,
                ),
            )
            execute(
                "UPDATE properties SET name = ?, address = ?, units = ?, occupancy = ? WHERE id = ?",
                (
                    request.form["property_name"].strip(),
                    request.form["property_address"].strip(),
                    int(request.form.get("units") or 1),
                    int(request.form.get("occupancy") or 0),
                    property_row["id"],
                ),
            )
            session.clear()
            session["user_id"] = cursor.lastrowid
            session["role"] = "manager"
            log(cursor.lastrowid, "Created the first Sync Space manager account.")
            return redirect(url_for("dashboard"))
    return render_template_string(SETUP_TEMPLATE, property=property_row)


@app.route("/login", methods=["GET", "POST"])
def login():
    if not has_manager():
        return redirect(url_for("setup"))
    if request.method == "POST":
        email = request.form.get("email", "").strip().lower()
        password = request.form.get("password", "")
        user = query_one("SELECT * FROM users WHERE email = ?", (email,))
        if user and check_password_hash(user["password_hash"], password):
            session.clear()
            session["user_id"] = user["id"]
            session["role"] = user["role"]
            log(user["id"], f"{user['name']} signed in.")
            return redirect(url_for("dashboard"))
        flash("Invalid email or password.")
    return render_template_string(LOGIN_TEMPLATE)


@app.route("/logout")
def logout():
    session.clear()
    return redirect(url_for("login"))


@app.route("/dashboard")
@login_required
def dashboard():
    user = current_user()
    return render_template_string(APP_TEMPLATE, **dashboard_data(user))


@app.route("/api/pay/<kind>/<int:item_id>", methods=["POST"])
@login_required
def pay_item(kind: str, item_id: int):
    user = current_user()
    if kind not in {"rent", "utility"}:
        return jsonify({"ok": False, "error": "Unknown payment type"}), 400
    table = "rent_charges" if kind == "rent" else "utility_bills"
    row = query_one(f"SELECT * FROM {table} WHERE id = ?", (item_id,))
    if not row:
        return jsonify({"ok": False, "error": "Item not found"}), 404
    if user["role"] == "tenant" and row["tenant_id"] != user["id"]:
        return jsonify({"ok": False, "error": "Permission denied"}), 403
    if user["role"] == "janitor":
        return jsonify({"ok": False, "error": "Permission denied"}), 403

    execute(
        f"UPDATE {table} SET status = 'Paid', paid_at = ? WHERE id = ?",
        (datetime.now().isoformat(timespec="seconds"), item_id),
    )
    log(user["id"], f"Marked {kind} #{item_id} as paid.")
    return jsonify({"ok": True})


@app.route("/api/rent", methods=["POST"])
@role_required("manager")
def add_rent():
    user = current_user()
    tenant_id = int(request.form["tenant_id"])
    month = request.form["month"].strip()
    amount = float(request.form["amount"])
    due_date = request.form["due_date"]
    status = request.form.get("status", "Due")
    execute(
        """
        INSERT OR REPLACE INTO rent_charges (tenant_id, month, amount, due_date, status, paid_at)
        VALUES (?, ?, ?, ?, ?, CASE WHEN ? = 'Paid' THEN ? ELSE NULL END)
        """,
        (tenant_id, month, amount, due_date, status, status, datetime.now().isoformat(timespec="seconds")),
    )
    log(user["id"], f"Updated rent charge for tenant #{tenant_id}.")
    return redirect(url_for("dashboard"))


@app.route("/api/utility", methods=["POST"])
@role_required("manager")
def add_utility():
    user = current_user()
    execute(
        """
        INSERT INTO utility_bills (tenant_id, bill_type, period, amount, due_date, status, paid_at)
        VALUES (?, ?, ?, ?, ?, ?, CASE WHEN ? = 'Paid' THEN ? ELSE NULL END)
        """,
        (
            int(request.form["tenant_id"]),
            request.form["bill_type"],
            request.form["period"].strip(),
            float(request.form["amount"]),
            request.form["due_date"],
            request.form.get("status", "Due"),
            request.form.get("status", "Due"),
            datetime.now().isoformat(timespec="seconds"),
        ),
    )
    log(user["id"], "Created a utility bill.")
    return redirect(url_for("dashboard"))


@app.route("/api/repair", methods=["POST"])
@login_required
def add_repair():
    user = current_user()
    tenant_id = int(request.form.get("tenant_id") or user["id"])
    if user["role"] == "tenant":
        tenant_id = user["id"]
    if user["role"] == "janitor":
        return jsonify({"ok": False, "error": "Janitors cannot create tenant repair requests"}), 403
    execute(
        """
        INSERT INTO repair_requests (tenant_id, title, location, priority, description)
        VALUES (?, ?, ?, ?, ?)
        """,
        (
            tenant_id,
            request.form["title"].strip(),
            request.form["location"].strip(),
            request.form.get("priority", "Medium"),
            request.form["description"].strip(),
        ),
    )
    log(user["id"], "Created a repair request.")
    return redirect(url_for("dashboard"))


@app.route("/api/repair/<int:repair_id>", methods=["POST"])
@login_required
def update_repair(repair_id: int):
    user = current_user()
    repair = query_one("SELECT * FROM repair_requests WHERE id = ?", (repair_id,))
    if not repair:
        return jsonify({"ok": False, "error": "Repair not found"}), 404
    if user["role"] == "tenant" and repair["tenant_id"] != user["id"]:
        return jsonify({"ok": False, "error": "Permission denied"}), 403

    status = request.form.get("status", repair["status"])
    janitor_id = request.form.get("janitor_id") or repair["janitor_id"]
    if status not in REPAIR_STATUSES:
        return jsonify({"ok": False, "error": "Invalid status"}), 400
    if user["role"] == "janitor" and repair["janitor_id"] not in {None, user["id"]}:
        return jsonify({"ok": False, "error": "Permission denied"}), 403
    if user["role"] == "janitor":
        janitor_id = user["id"]
    execute(
        "UPDATE repair_requests SET status = ?, janitor_id = ?, updated_at = ? WHERE id = ?",
        (status, janitor_id, datetime.now().isoformat(timespec="seconds"), repair_id),
    )
    log(user["id"], f"Updated repair #{repair_id} to {status}.")
    return jsonify({"ok": True})


@app.route("/api/tenant", methods=["POST"])
@role_required("manager")
def add_tenant():
    user = current_user()
    role = request.form.get("role", "tenant")
    if role not in ROLES:
        role = "tenant"
    password = request.form.get("password", "")
    if len(password) < 8:
        flash("User password must be at least 8 characters.")
        return redirect(url_for("dashboard") + "#people")
    try:
        execute(
            """
            INSERT INTO users (name, email, password_hash, role, unit, phone)
            VALUES (?, ?, ?, ?, ?, ?)
            """,
            (
                request.form["name"].strip(),
                request.form["email"].strip().lower(),
                generate_password_hash(password),
                role,
                request.form.get("unit") or None,
                request.form.get("phone") or None,
            ),
        )
    except sqlite3.IntegrityError:
        flash("That email address is already in use.")
        return redirect(url_for("dashboard") + "#people")
    log(user["id"], f"Added {role} {request.form['name'].strip()}.")
    return redirect(url_for("dashboard") + "#people")


@app.route("/api/property", methods=["POST"])
@role_required("manager")
def update_property():
    user = current_user()
    execute(
        "UPDATE properties SET name = ?, address = ?, units = ?, occupancy = ? WHERE id = ?",
        (
            request.form["name"].strip(),
            request.form["address"].strip(),
            int(request.form["units"]),
            int(request.form["occupancy"]),
            int(request.form["id"]),
        ),
    )
    log(user["id"], "Updated property profile.")
    return redirect(url_for("dashboard"))


@app.template_filter("money")
def money_filter(value):
    return money(value)


SETUP_TEMPLATE = """
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Set Up Sync Space</title>
  <style>
    :root {
      --ink: #2c2924;
      --muted: #776f63;
      --paper: #f7f0e6;
      --line: #e1d6c7;
      --accent: #b8643c;
      --accent-strong: #8f4328;
      --sage: #75866b;
      --surface: #fffaf2;
      --danger: #b94735;
      font-family: Aptos, "Segoe UI", ui-sans-serif, system-ui, sans-serif;
    }
    * { box-sizing: border-box; }
    body {
      min-height: 100vh;
      margin: 0;
      color: var(--ink);
      background: linear-gradient(135deg, #f8efe4 0%, #efe0cd 46%, #dbe2d1 100%);
      display: grid;
      place-items: center;
      padding: 24px;
    }
    .shell {
      width: min(1060px, 100%);
      display: grid;
      grid-template-columns: .9fr 1.1fr;
      min-height: 680px;
      background: var(--surface);
      border: 1px solid var(--line);
      box-shadow: 0 28px 90px rgba(44, 41, 36, .14);
      border-radius: 10px;
      overflow: hidden;
    }
    .brand {
      padding: 48px;
      background: #302821;
      color: #fff7ed;
      display: flex;
      flex-direction: column;
      justify-content: space-between;
      position: relative;
      overflow: hidden;
    }
    .brand::after {
      content: "";
      position: absolute;
      inset: auto -90px -100px 18%;
      height: 270px;
      background: #b8643c;
      transform: skewY(-8deg);
      opacity: .92;
    }
    h1 { font-size: clamp(42px, 7vw, 78px); line-height: .94; margin: 0; letter-spacing: 0; font-weight: 620; }
    .tagline { max-width: 420px; color: #ead9c7; font-size: 18px; line-height: 1.6; position: relative; z-index: 1; }
    .setup { padding: 40px; display: grid; align-content: center; gap: 16px; }
    h2 { margin: 0; font-size: 28px; font-weight: 620; }
    p { color: var(--muted); line-height: 1.5; margin: 0; }
    label { display: grid; gap: 7px; color: var(--muted); font-size: 14px; font-weight: 560; }
    input {
      width: 100%;
      border: 1px solid var(--line);
      background: #fffdf8;
      padding: 12px 13px;
      border-radius: 6px;
      font: inherit;
      color: var(--ink);
    }
    form { display: grid; gap: 12px; }
    .row { display: grid; gap: 12px; grid-template-columns: repeat(2, minmax(0, 1fr)); }
    button {
      border: 0;
      border-radius: 6px;
      background: var(--accent);
      color: white;
      padding: 13px 18px;
      margin-top: 6px;
      font: inherit;
      font-weight: 620;
      cursor: pointer;
    }
    button:hover { background: var(--accent-strong); }
    .error { background: #fff0ea; color: var(--danger); padding: 12px; border-radius: 6px; }
    @media (max-width: 860px) {
      .shell { grid-template-columns: 1fr; }
      .brand { min-height: 260px; padding: 32px; }
      .setup { padding: 28px; }
      .row { grid-template-columns: 1fr; }
    }
  </style>
</head>
<body>
  <main class="shell">
    <section class="brand">
      <h1>Sync Space</h1>
      <p class="tagline">ResidentHub for warm, organized property operations at syncspace.co.ke.</p>
    </section>
    <section class="setup">
      <div>
        <h2>Create the first manager</h2>
        <p>This replaces demo access with a real Sync Space manager account.</p>
      </div>
      {% with messages = get_flashed_messages() %}
        {% if messages %}<div class="error">{{ messages[0] }}</div>{% endif %}
      {% endwith %}
      <form method="post">
        <div class="row">
          <label>Property name<input name="property_name" value="{{ property.name }}" required></label>
          <label>Website / address<input name="property_address" value="{{ property.address }}" required></label>
        </div>
        <div class="row">
          <label>Total units<input name="units" type="number" min="1" value="{{ property.units }}" required></label>
          <label>Occupied units<input name="occupancy" type="number" min="0" value="{{ property.occupancy }}" required></label>
        </div>
        <div class="row">
          <label>Manager name<input name="name" autocomplete="name" required></label>
          <label>Phone<input name="phone" autocomplete="tel"></label>
        </div>
        <label>Email<input name="email" type="email" autocomplete="email" required></label>
        <div class="row">
          <label>Password<input name="password" type="password" autocomplete="new-password" minlength="8" required></label>
          <label>Confirm password<input name="confirm_password" type="password" autocomplete="new-password" minlength="8" required></label>
        </div>
        <button type="submit">Create manager account</button>
      </form>
    </section>
  </main>
</body>
</html>
"""


LOGIN_TEMPLATE = """
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Sync Space Login</title>
  <style>
    :root {
      --ink: #2c2924;
      --muted: #776f63;
      --paper: #f7f0e6;
      --line: #e1d6c7;
      --accent: #b8643c;
      --accent-strong: #8f4328;
      --warn: #cf8f28;
      --danger: #b94735;
      --surface: #fffaf2;
      font-family: Aptos, "Segoe UI", ui-sans-serif, system-ui, sans-serif;
    }
    * { box-sizing: border-box; }
    body {
      min-height: 100vh;
      margin: 0;
      color: var(--ink);
      background: linear-gradient(135deg, #f8efe4 0%, #efe0cd 46%, #dbe2d1 100%);
      display: grid;
      place-items: center;
      padding: 24px;
    }
    .shell {
      width: min(980px, 100%);
      display: grid;
      grid-template-columns: 1fr 420px;
      min-height: 620px;
      background: var(--surface);
      border: 1px solid var(--line);
      box-shadow: 0 24px 80px rgba(44, 41, 36, .14);
      border-radius: 10px;
      overflow: hidden;
    }
    .brand {
      padding: 52px;
      background: #302821;
      color: #fff7ed;
      display: flex;
      flex-direction: column;
      justify-content: space-between;
      position: relative;
      overflow: hidden;
    }
    .brand::after {
      content: "";
      position: absolute;
      inset: auto -80px -120px 20%;
      height: 300px;
      background: #b8643c;
      transform: skewY(-10deg);
      opacity: .9;
    }
    h1 { font-size: clamp(42px, 7vw, 76px); line-height: .92; margin: 0; letter-spacing: 0; font-weight: 620; }
    .tagline { max-width: 420px; color: #ead9c7; font-size: 18px; line-height: 1.6; position: relative; z-index: 1; }
    .login { padding: 44px; display: flex; flex-direction: column; justify-content: center; }
    h2 { margin: 0 0 8px; font-size: 28px; font-weight: 620; }
    p { color: var(--muted); line-height: 1.5; }
    label { display: block; margin: 18px 0 8px; font-weight: 560; color: var(--muted); }
    input {
      width: 100%;
      border: 1px solid var(--line);
      background: #fffdf8;
      padding: 13px 14px;
      border-radius: 6px;
      font: inherit;
      color: var(--ink);
    }
    button {
      width: 100%;
      border: 0;
      border-radius: 6px;
      background: var(--accent);
      color: white;
      padding: 14px 18px;
      margin-top: 22px;
      font-weight: 620;
      cursor: pointer;
    }
    button:hover { background: var(--accent-strong); }
    .demo {
      display: grid;
      gap: 8px;
      margin-top: 22px;
      padding: 14px;
      background: #f4eadc;
      border: 1px solid var(--line);
      border-radius: 6px;
      color: var(--muted);
      font-size: 14px;
    }
    .error { background: #fff1ef; color: var(--danger); padding: 12px; border-radius: 6px; }
    @media (max-width: 820px) {
      .shell { grid-template-columns: 1fr; min-height: 0; }
      .brand { min-height: 280px; padding: 32px; }
      .login { padding: 28px; }
    }
  </style>
</head>
<body>
  <main class="shell">
    <section class="brand">
      <h1>Sync Space</h1>
      <p class="tagline">ResidentHub keeps rent, utilities, repairs, and property operations quietly organized.</p>
    </section>
    <section class="login">
      <h2>Sign in</h2>
      <p>Access your Sync Space property workspace.</p>
      {% with messages = get_flashed_messages() %}
        {% if messages %}<div class="error">{{ messages[0] }}</div>{% endif %}
      {% endwith %}
      <form method="post">
        <label for="email">Email</label>
        <input id="email" name="email" type="email" autocomplete="email" required>
        <label for="password">Password</label>
        <input id="password" name="password" type="password" autocomplete="current-password" required>
        <button type="submit">Enter dashboard</button>
      </form>
    </section>
  </main>
</body>
</html>
"""


APP_TEMPLATE = """
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Sync Space ResidentHub</title>
  <style>
    :root {
      --ink: #2c2924;
      --muted: #776f63;
      --paper: #f7f0e6;
      --surface: #fffaf2;
      --line: #e2d6c8;
      --accent: #b8643c;
      --accent-strong: #8f4328;
      --blue: #536f8d;
      --yellow: #c8892f;
      --red: #b94735;
      --green: #6f7f57;
      --charcoal: #302821;
      font-family: Aptos, "Segoe UI", ui-sans-serif, system-ui, sans-serif;
    }
    * { box-sizing: border-box; }
    body { margin: 0; background: var(--paper); color: var(--ink); }
    a { color: inherit; text-decoration: none; }
    .app { min-height: 100vh; display: grid; grid-template-columns: 260px 1fr; }
    .sidebar {
      background: var(--charcoal);
      color: #f8faf7;
      padding: 28px 22px;
      display: flex;
      flex-direction: column;
      gap: 26px;
    }
    .logo { font-size: 26px; font-weight: 650; letter-spacing: 0; }
    .role {
      display: inline-flex;
      align-items: center;
      width: fit-content;
      border: 1px solid rgba(255,255,255,.18);
      padding: 6px 10px;
      border-radius: 999px;
      color: #dce7e0;
      text-transform: capitalize;
      font-size: 13px;
      font-weight: 580;
    }
    .nav { display: grid; gap: 8px; }
    .nav button {
      text-align: left;
      border: 0;
      background: transparent;
      color: #cfd8d4;
      padding: 12px 12px;
      border-radius: 6px;
      font: inherit;
      cursor: pointer;
      font-weight: 540;
    }
    .nav button.active, .nav button:hover { background: rgba(255,255,255,.1); color: #fff; }
    .account { margin-top: auto; display: grid; gap: 8px; color: #cfd8d4; line-height: 1.35; }
    .account strong { color: #fff; }
    .logout { color: #f1bf92; font-weight: 580; }
    .main { padding: 28px; overflow: auto; }
    .topbar { display: flex; justify-content: space-between; gap: 18px; align-items: flex-start; margin-bottom: 24px; }
    h1 { margin: 0; font-size: 34px; letter-spacing: 0; font-weight: 620; }
    h2 { margin: 0 0 14px; font-size: 20px; letter-spacing: 0; font-weight: 620; }
    h3 { margin: 0 0 10px; font-size: 16px; letter-spacing: 0; font-weight: 600; }
    .muted { color: var(--muted); }
    .grid { display: grid; gap: 18px; }
    .stats { grid-template-columns: repeat(4, minmax(150px, 1fr)); }
    .stat, .panel, .card {
      background: var(--surface);
      border: 1px solid var(--line);
      border-radius: 8px;
    }
    .stat { padding: 18px; min-height: 112px; display: grid; align-content: space-between; }
    .stat span { color: var(--muted); font-weight: 560; font-size: 13px; text-transform: uppercase; }
    .stat strong { font-size: 28px; letter-spacing: 0; font-weight: 620; }
    .panel { padding: 18px; }
    .two { grid-template-columns: 1.15fr .85fr; align-items: start; }
    .three { grid-template-columns: repeat(3, 1fr); }
    .section { display: none; }
    .section.active { display: grid; gap: 18px; }
    table { width: 100%; border-collapse: collapse; }
    th, td { text-align: left; padding: 13px 10px; border-bottom: 1px solid var(--line); vertical-align: middle; }
    th { color: var(--muted); font-size: 12px; text-transform: uppercase; letter-spacing: 0; }
    tr:last-child td { border-bottom: 0; }
    .pill {
      display: inline-flex;
      align-items: center;
      min-height: 28px;
      padding: 5px 9px;
      border-radius: 999px;
      background: #eef3ed;
      font-size: 13px;
      font-weight: 560;
      white-space: nowrap;
    }
    .Paid, .Resolved { color: var(--green); background: #e8f6ee; }
    .Due, .Assigned { color: var(--blue); background: #ebf3fc; }
    .Overdue, .Urgent { color: var(--red); background: #fff0ee; }
    .Open, .In-Progress, .Medium { color: var(--yellow); background: #fff7df; }
    .Low { color: var(--muted); }
    .High { color: var(--red); background: #fff0ee; }
    .actions { display: flex; gap: 8px; flex-wrap: wrap; }
    button, .button {
      border: 0;
      background: var(--accent);
      color: white;
      border-radius: 6px;
      padding: 10px 13px;
      font: inherit;
      font-weight: 600;
      cursor: pointer;
      line-height: 1;
    }
    button.secondary, .button.secondary { background: #e8ece8; color: var(--ink); }
    button.warn { background: var(--yellow); }
    button:disabled { opacity: .45; cursor: not-allowed; }
    input, select, textarea {
      width: 100%;
      border: 1px solid var(--line);
      background: #fbfcf8;
      border-radius: 6px;
      color: var(--ink);
      padding: 11px 12px;
      font: inherit;
      min-height: 42px;
    }
    textarea { min-height: 104px; resize: vertical; }
    label { display: grid; gap: 7px; color: var(--muted); font-size: 13px; font-weight: 560; }
    form .row { display: grid; gap: 12px; grid-template-columns: repeat(2, minmax(0, 1fr)); }
    form { display: grid; gap: 12px; }
    .cards { display: grid; gap: 12px; }
    .card { padding: 15px; display: grid; gap: 10px; }
    .card-head { display: flex; justify-content: space-between; gap: 12px; align-items: start; }
    .activity { display: grid; gap: 10px; }
    .activity div { padding-bottom: 10px; border-bottom: 1px solid var(--line); color: var(--muted); }
    .activity div:last-child { border-bottom: 0; }
    .notice { display: none; position: fixed; right: 18px; bottom: 18px; background: var(--charcoal); color: #fff; padding: 13px 16px; border-radius: 8px; box-shadow: 0 18px 50px rgba(23,32,46,.2); }
    .notice.show { display: block; }
    @media (max-width: 980px) {
      .app { grid-template-columns: 1fr; }
      .sidebar { position: static; }
      .nav { grid-template-columns: repeat(2, 1fr); }
      .stats, .two, .three { grid-template-columns: 1fr; }
      .topbar, .card-head { flex-direction: column; }
    }
    @media (max-width: 620px) {
      .main { padding: 18px; }
      .sidebar { padding: 22px 18px; }
      form .row { grid-template-columns: 1fr; }
      table { display: block; overflow-x: auto; white-space: nowrap; }
      .nav { grid-template-columns: 1fr; }
    }
  </style>
</head>
<body>
  <div class="app">
    <aside class="sidebar">
      <div>
        <div class="logo">Sync Space</div>
        <p class="muted">ResidentHub portal</p>
        <p class="muted">{{ property.name }}</p>
        <span class="role">{{ user.role }}</span>
      </div>
      <nav class="nav">
        <button class="active" data-tab="overview">Overview</button>
        {% if user.role != 'janitor' %}
          <button data-tab="rent">Rent</button>
          <button data-tab="utilities">Utilities</button>
        {% endif %}
        <button data-tab="repairs">Repairs</button>
        {% if user.role == 'manager' %}
          <button data-tab="people">People</button>
          <button data-tab="settings">Property</button>
        {% endif %}
      </nav>
      <div class="account">
        <strong>{{ user.name }}</strong>
        <span>{{ user.email }}</span>
        {% if user.unit %}<span>Unit {{ user.unit }}</span>{% endif %}
        <a class="logout" href="{{ url_for('logout') }}">Sign out</a>
      </div>
    </aside>

    <main class="main">
      <header class="topbar">
        <div>
          <h1>{{ 'Operations' if user.role == 'manager' else 'My Home' if user.role == 'tenant' else 'Work Orders' }}</h1>
          <p class="muted">{{ property.address }}</p>
        </div>
        <div class="actions">
          {% if user.role == 'tenant' %}<button data-tab-jump="repairs">New repair</button>{% endif %}
          {% if user.role == 'manager' %}<button data-tab-jump="people">Add resident</button>{% endif %}
        </div>
      </header>
      {% with messages = get_flashed_messages() %}
        {% if messages %}<div class="panel">{{ messages[0] }}</div>{% endif %}
      {% endwith %}

      <section id="overview" class="section active">
        <div class="grid stats">
          <div class="stat"><span>Rent outstanding</span><strong>{{ totals.rent_due|money }}</strong></div>
          <div class="stat"><span>Utilities due</span><strong>{{ totals.utilities_due|money }}</strong></div>
          <div class="stat"><span>Open repairs</span><strong>{{ totals.open_repairs }}</strong></div>
          <div class="stat"><span>Paid balance</span><strong>{{ totals.paid_this_month|money }}</strong></div>
        </div>
        <div class="grid two">
          <div class="panel">
            <h2>Recent Repair Requests</h2>
            <div class="cards">
              {% for repair in repairs[:4] %}
                <article class="card">
                  <div class="card-head">
                    <div>
                      <h3>{{ repair.title }}</h3>
                      <div class="muted">{{ repair.location }} · {{ repair.tenant_name }}{% if repair.unit %}, Unit {{ repair.unit }}{% endif %}</div>
                    </div>
                    <span class="pill {{ repair.status|replace(' ', '-') }}">{{ repair.status }}</span>
                  </div>
                  <p class="muted">{{ repair.description }}</p>
                </article>
              {% else %}
                <p class="muted">No repair requests yet.</p>
              {% endfor %}
            </div>
          </div>
          <div class="panel">
            <h2>Activity</h2>
            <div class="activity">
              {% for item in activity %}
                <div><strong>{{ item.actor_name or 'System' }}</strong><br>{{ item.message }}<br><small>{{ item.created_at }}</small></div>
              {% endfor %}
            </div>
          </div>
        </div>
      </section>

      {% if user.role != 'janitor' %}
      <section id="rent" class="section">
        <div class="grid two">
          <div class="panel">
            <h2>Rent Collection</h2>
            <table>
              <thead><tr><th>Tenant</th><th>Month</th><th>Due</th><th>Amount</th><th>Status</th><th></th></tr></thead>
              <tbody>
                {% for item in rent %}
                <tr>
                  <td>{{ item.tenant_name }}{% if item.unit %}<br><small class="muted">Unit {{ item.unit }}</small>{% endif %}</td>
                  <td>{{ item.month }}</td>
                  <td>{{ item.due_date }}</td>
                  <td>{{ item.amount|money }}</td>
                  <td><span class="pill {{ item.status }}">{{ item.status }}</span></td>
                  <td>{% if item.status != 'Paid' %}<button data-pay="rent" data-id="{{ item.id }}">Pay</button>{% endif %}</td>
                </tr>
                {% endfor %}
              </tbody>
            </table>
          </div>
          {% if user.role == 'manager' %}
          <div class="panel">
            <h2>Create or Update Rent</h2>
            <form method="post" action="{{ url_for('add_rent') }}">
              <label>Tenant<select name="tenant_id">{% for tenant in tenants %}<option value="{{ tenant.id }}">{{ tenant.unit }} · {{ tenant.name }}</option>{% endfor %}</select></label>
              <div class="row">
                <label>Month<input name="month" value="{{ rent[0].month if rent else '' }}" placeholder="May 2026" required></label>
                <label>Amount<input name="amount" type="number" step="0.01" min="0" required></label>
              </div>
              <div class="row">
                <label>Due date<input name="due_date" type="date" required></label>
                <label>Status<select name="status"><option>Due</option><option>Paid</option><option>Overdue</option></select></label>
              </div>
              <button type="submit">Save rent</button>
            </form>
          </div>
          {% endif %}
        </div>
      </section>

      <section id="utilities" class="section">
        <div class="grid two">
          <div class="panel">
            <h2>Utility Billing</h2>
            <table>
              <thead><tr><th>Tenant</th><th>Type</th><th>Period</th><th>Due</th><th>Amount</th><th>Status</th><th></th></tr></thead>
              <tbody>
                {% for bill in bills %}
                <tr>
                  <td>{{ bill.tenant_name }}{% if bill.unit %}<br><small class="muted">Unit {{ bill.unit }}</small>{% endif %}</td>
                  <td>{{ bill.bill_type }}</td>
                  <td>{{ bill.period }}</td>
                  <td>{{ bill.due_date }}</td>
                  <td>{{ bill.amount|money }}</td>
                  <td><span class="pill {{ bill.status }}">{{ bill.status }}</span></td>
                  <td>{% if bill.status != 'Paid' %}<button data-pay="utility" data-id="{{ bill.id }}">Pay</button>{% endif %}</td>
                </tr>
                {% endfor %}
              </tbody>
            </table>
          </div>
          {% if user.role == 'manager' %}
          <div class="panel">
            <h2>Add Utility Bill</h2>
            <form method="post" action="{{ url_for('add_utility') }}">
              <label>Tenant<select name="tenant_id">{% for tenant in tenants %}<option value="{{ tenant.id }}">{{ tenant.unit }} · {{ tenant.name }}</option>{% endfor %}</select></label>
              <div class="row">
                <label>Type<select name="bill_type">{% for type in bill_types %}<option>{{ type }}</option>{% endfor %}</select></label>
                <label>Period<input name="period" placeholder="May 2026" required></label>
              </div>
              <div class="row">
                <label>Amount<input name="amount" type="number" step="0.01" min="0" required></label>
                <label>Due date<input name="due_date" type="date" required></label>
              </div>
              <label>Status<select name="status"><option>Due</option><option>Paid</option><option>Overdue</option></select></label>
              <button type="submit">Create bill</button>
            </form>
          </div>
          {% endif %}
        </div>
      </section>
      {% endif %}

      <section id="repairs" class="section">
        <div class="grid two">
          <div class="panel">
            <h2>Repair Requests</h2>
            <div class="cards">
              {% for repair in repairs %}
              <article class="card">
                <div class="card-head">
                  <div>
                    <h3>{{ repair.title }}</h3>
                    <div class="muted">{{ repair.location }} · {{ repair.tenant_name }}{% if repair.janitor_name %} · {{ repair.janitor_name }}{% endif %}</div>
                  </div>
                  <div class="actions">
                    <span class="pill {{ repair.priority }}">{{ repair.priority }}</span>
                    <span class="pill {{ repair.status|replace(' ', '-') }}">{{ repair.status }}</span>
                  </div>
                </div>
                <p class="muted">{{ repair.description }}</p>
                {% if user.role in ['manager', 'janitor'] %}
                <form class="repair-update" data-id="{{ repair.id }}">
                  <div class="row">
                    <label>Status<select name="status">{% for status in repair_statuses %}<option {% if repair.status == status %}selected{% endif %}>{{ status }}</option>{% endfor %}</select></label>
                    {% if user.role == 'manager' %}
                    <label>Assign<select name="janitor_id"><option value="">Unassigned</option>{% for janitor in janitors %}<option value="{{ janitor.id }}" {% if repair.janitor_id == janitor.id %}selected{% endif %}>{{ janitor.name }}</option>{% endfor %}</select></label>
                    {% endif %}
                  </div>
                  <button type="submit">Update repair</button>
                </form>
                {% endif %}
              </article>
              {% else %}
                <p class="muted">No repairs to show.</p>
              {% endfor %}
            </div>
          </div>
          {% if user.role in ['manager', 'tenant'] %}
          <div class="panel">
            <h2>New Repair Request</h2>
            <form method="post" action="{{ url_for('add_repair') }}">
              {% if user.role == 'manager' %}
              <label>Tenant<select name="tenant_id">{% for tenant in tenants %}<option value="{{ tenant.id }}">{{ tenant.unit }} · {{ tenant.name }}</option>{% endfor %}</select></label>
              {% endif %}
              <label>Title<input name="title" placeholder="Bathroom drain clogged" required></label>
              <div class="row">
                <label>Location<input name="location" value="{{ user.unit or '' }}" placeholder="Unit or common area" required></label>
                <label>Priority<select name="priority"><option>Low</option><option selected>Medium</option><option>High</option><option>Urgent</option></select></label>
              </div>
              <label>Description<textarea name="description" placeholder="Describe what is happening..." required></textarea></label>
              <button type="submit">Submit request</button>
            </form>
          </div>
          {% endif %}
        </div>
      </section>

      {% if user.role == 'manager' %}
      <section id="people" class="section">
        <div class="grid two">
          <div class="panel">
            <h2>Residents, Managers and Staff</h2>
            <div class="grid three">
              {% for manager in managers %}
              <article class="card">
                <h3>{{ manager.name }}</h3>
                <div class="muted">Manager</div>
                <div>{{ manager.email }}</div>
                <div>{{ manager.phone or '' }}</div>
              </article>
              {% endfor %}
              {% for tenant in tenants %}
              <article class="card">
                <h3>{{ tenant.name }}</h3>
                <div class="muted">Tenant · Unit {{ tenant.unit }}</div>
                <div>{{ tenant.email }}</div>
                <div>{{ tenant.phone }}</div>
              </article>
              {% endfor %}
              {% for janitor in janitors %}
              <article class="card">
                <h3>{{ janitor.name }}</h3>
                <div class="muted">Janitor</div>
                <div>{{ janitor.email }}</div>
                <div>{{ janitor.phone }}</div>
              </article>
              {% endfor %}
            </div>
          </div>
          <div class="panel">
            <h2>Add User</h2>
            <form method="post" action="{{ url_for('add_tenant') }}">
              <div class="row">
                <label>Name<input name="name" required></label>
                <label>Email<input name="email" type="email" required></label>
              </div>
              <div class="row">
                <label>Role<select name="role"><option value="tenant">Tenant</option><option value="janitor">Janitor</option><option value="manager">Manager</option></select></label>
                <label>Unit<input name="unit" placeholder="C-302"></label>
              </div>
              <div class="row">
                <label>Phone<input name="phone"></label>
                <label>Password<input name="password" type="password" minlength="8" required></label>
              </div>
              <button type="submit">Add user</button>
            </form>
          </div>
        </div>
      </section>

      <section id="settings" class="section">
        <div class="panel">
          <h2>Property Settings</h2>
          <form method="post" action="{{ url_for('update_property') }}">
            <input type="hidden" name="id" value="{{ property.id }}">
            <label>Name<input name="name" value="{{ property.name }}" required></label>
            <label>Address<input name="address" value="{{ property.address }}" required></label>
            <div class="row">
              <label>Total units<input name="units" type="number" min="1" value="{{ property.units }}" required></label>
              <label>Occupied units<input name="occupancy" type="number" min="0" value="{{ property.occupancy }}" required></label>
            </div>
            <button type="submit">Save property</button>
          </form>
        </div>
      </section>
      {% endif %}
    </main>
  </div>
  <div class="notice" id="notice"></div>
  <script>
    const tabs = [...document.querySelectorAll("[data-tab]")];
    const sections = [...document.querySelectorAll(".section")];
    const notice = document.querySelector("#notice");

    function showNotice(message) {
      notice.textContent = message;
      notice.classList.add("show");
      setTimeout(() => notice.classList.remove("show"), 2200);
    }

    function activate(tabName) {
      tabs.forEach(tab => tab.classList.toggle("active", tab.dataset.tab === tabName));
      sections.forEach(section => section.classList.toggle("active", section.id === tabName));
      history.replaceState(null, "", "#" + tabName);
    }

    tabs.forEach(tab => tab.addEventListener("click", () => activate(tab.dataset.tab)));
    document.querySelectorAll("[data-tab-jump]").forEach(button => {
      button.addEventListener("click", () => activate(button.dataset.tabJump));
    });
    if (location.hash && document.querySelector(location.hash)) activate(location.hash.slice(1));

    document.querySelectorAll("[data-pay]").forEach(button => {
      button.addEventListener("click", async () => {
        button.disabled = true;
        const response = await fetch(`/api/pay/${button.dataset.pay}/${button.dataset.id}`, { method: "POST" });
        const result = await response.json();
        if (!result.ok) {
          button.disabled = false;
          showNotice(result.error || "Payment failed");
          return;
        }
        showNotice("Payment recorded");
        setTimeout(() => location.reload(), 500);
      });
    });

    document.querySelectorAll(".repair-update").forEach(form => {
      form.addEventListener("submit", async event => {
        event.preventDefault();
        const button = form.querySelector("button");
        button.disabled = true;
        const response = await fetch(`/api/repair/${form.dataset.id}`, {
          method: "POST",
          body: new FormData(form)
        });
        const result = await response.json();
        if (!result.ok) {
          button.disabled = false;
          showNotice(result.error || "Update failed");
          return;
        }
        showNotice("Repair updated");
        setTimeout(() => location.reload(), 500);
      });
    });
  </script>
</body>
</html>
"""


if __name__ == "__main__":
    with app.app_context():
        init_db()
        app.config["_DB_READY"] = True
    app.run(
        debug=os.environ.get("FLASK_DEBUG") == "1",
        host=os.environ.get("HOST", "127.0.0.1"),
        port=int(os.environ.get("PORT", "5000")),
    )
