import os
import io
import re
import socket
import sqlite3
import subprocess
import functools
from flask import Flask, request, session, redirect, url_for, Response
from markupsafe import escape

# ---------------------------------------------------------------------------
# 1. EMOJI POOL + KEY GENERATOR
# ---------------------------------------------------------------------------

EMOJI_POOL = [
    "😀","😂","😍","😎","😭","😡","🥰","🤔","😴","🤯",
    "🎉","🔥","💀","👻","🌙","⭐","🌈","🍎","🍕","🎸",
    "🐶","🐱","🐸","🦊","🐺","🐻","🦁","🐯","🦋","🐙",
    "🌺","🍀","🌊","⚡","🏆","🎯","🔑","💎","🛸","🎭",
    "🌋","🧊","🍄","🦄","🔮","🗝️","🧲","🎪",
]

def generate_emoji_key(length):
    return "".join(EMOJI_POOL[b % len(EMOJI_POOL)] for b in os.urandom(length))

# ---------------------------------------------------------------------------
# 2. MODULE-LEVEL STATE
# ---------------------------------------------------------------------------

ADMIN_POST_KEY   = generate_emoji_key(4)
TRUSTED_POST_KEY = generate_emoji_key(2)
DEFAULT_POST_KEY = generate_emoji_key(2)
QR_VISIBLE       = True

def get_all_local_ips():
    """Return list of (ip, iface_name) for all non-loopback IPv4 interfaces."""
    results = []

    # Method 1: ifconfig (net-tools) — uses socket ioctl, no netlink needed.
    # Works in Termux. Install with: pkg install net-tools
    try:
        out = subprocess.run(
            ['ifconfig'], capture_output=True, text=True, timeout=3
        ).stdout
        current_iface = None
        for line in out.splitlines():
            # New-style: "wlan0: flags=..."  Old-style: "wlan0     Link encap:..."
            m = re.match(r'^(\S+?)(?::\s+flags=|\s+Link)', line)
            if m:
                current_iface = m.group(1).rstrip(':')
            # New-style: "inet 192.x.x.x"  Old-style: "inet addr:192.x.x.x"
            m = re.search(r'inet\s+(?:addr:)?(\d+\.\d+\.\d+\.\d+)', line)
            if m and current_iface:
                ip = m.group(1)
                if not ip.startswith('127.'):
                    results.append((ip, current_iface))
        if results:
            return results
    except Exception:
        pass

    # Method 2: ip addr (iproute2) — works on desktop Linux, but requires
    # netlink socket permissions not available in Termux by default.
    try:
        out = subprocess.run(
            ['ip', 'addr'], capture_output=True, text=True, timeout=3
        ).stdout
        current_iface = None
        for line in out.splitlines():
            m = re.match(r'^\d+:\s+(\S+?)[@:]', line)
            if m:
                current_iface = m.group(1)
            m = re.match(r'^\s+inet\s+(\d+\.\d+\.\d+\.\d+)/', line)
            if m and current_iface:
                ip = m.group(1)
                if not ip.startswith('127.'):
                    results.append((ip, current_iface))
    except Exception:
        pass

    return results

def get_hotspot_ip():
    ips = get_all_local_ips()
    for ip, _ in ips:
        if ip.startswith('192.168.') or ip.startswith('10.42.'):
            return ip
    return ips[0][0] if ips else '127.0.0.1'

def generate_qr_bytes(url):
    try:
        import qrcode
        img = qrcode.make(url)
        buf = io.BytesIO()
        img.save(buf, format="PNG")
        return buf.getvalue()
    except Exception:
        return b""

HOTSPOT_IP     = get_hotspot_ip()
JOIN_URL       = f"http://{HOTSPOT_IP}:5000/join"
QR_IMAGE_BYTES = generate_qr_bytes(JOIN_URL)

# ---------------------------------------------------------------------------
# 3. FLASK APP
# ---------------------------------------------------------------------------

app = Flask(__name__)
app.secret_key = os.urandom(32)

# ---------------------------------------------------------------------------
# 4. SQLITE
# ---------------------------------------------------------------------------

DB_CONN = sqlite3.connect(":memory:", check_same_thread=False)
DB_CONN.row_factory = sqlite3.Row

def init_db():
    cur = DB_CONN.cursor()
    cur.executescript("""
        CREATE TABLE IF NOT EXISTS users (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            session_label TEXT NOT NULL,
            username TEXT NOT NULL,
            profile_notes TEXT DEFAULT '',
            notes_visible INTEGER DEFAULT 1,
            post_tier TEXT DEFAULT 'DEFAULT',
            created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
        );
        CREATE TABLE IF NOT EXISTS keys (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            user_id INTEGER NOT NULL REFERENCES users(id),
            key_type TEXT NOT NULL,
            emoji_code TEXT NOT NULL
        );
        CREATE TABLE IF NOT EXISTS handshakes (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            from_user_id INTEGER NOT NULL REFERENCES users(id),
            to_user_id INTEGER NOT NULL REFERENCES users(id),
            submitted_code TEXT NOT NULL,
            created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
        );
        CREATE TABLE IF NOT EXISTS posts (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            author_id INTEGER NOT NULL REFERENCES users(id),
            content TEXT NOT NULL,
            score INTEGER DEFAULT 0,
            created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
        );
        CREATE TABLE IF NOT EXISTS votes (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            post_id INTEGER NOT NULL REFERENCES posts(id),
            user_id INTEGER NOT NULL REFERENCES users(id),
            direction INTEGER NOT NULL,
            weight INTEGER NOT NULL,
            UNIQUE(post_id, user_id)
        );
        CREATE TABLE IF NOT EXISTS reports (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            post_id INTEGER NOT NULL REFERENCES posts(id),
            reporter_id INTEGER NOT NULL REFERENCES users(id),
            created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
            UNIQUE(post_id, reporter_id)
        );
        CREATE TABLE IF NOT EXISTS notifications (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            user_id INTEGER NOT NULL REFERENCES users(id),
            message TEXT NOT NULL,
            read INTEGER DEFAULT 0,
            created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
        );
    """)
    DB_CONN.commit()

init_db()

# ---------------------------------------------------------------------------
# 5. CSS
# ---------------------------------------------------------------------------

BASE_CSS = """
* { box-sizing: border-box; margin: 0; padding: 0; }
body {
    background: #0a0a0f;
    color: #c8c8d8;
    font-family: 'Courier New', Courier, monospace;
    font-size: 15px;
    min-height: 100vh;
    padding: 8px;
}
a { color: #cc44ff; text-decoration: none; }
a:hover { text-decoration: underline; }
h1, h2, h3 { color: #ff44cc; letter-spacing: 1px; margin-bottom: 8px; }
h1 { font-size: 1.3em; border-bottom: 2px solid #cc44ff; padding-bottom: 4px; margin-bottom: 12px; }
h2 { font-size: 1.1em; }
h3 { font-size: 1em; color: #cc44ff; }
.container { max-width: 600px; margin: 0 auto; }
.card {
    border: 1px solid #442255;
    background: #0f0f1a;
    padding: 12px;
    margin-bottom: 10px;
}
.card.pinned-top { border-color: #ff44cc; border-width: 2px; }
.card.pinned-bottom { border-color: #334; border-width: 2px; }
.post-meta { font-size: 0.8em; color: #886699; margin-bottom: 6px; }
.post-content { margin-bottom: 10px; word-break: break-word; line-height: 1.5; }
.spoiler {
    background: #330033; color: #330033; cursor: pointer;
    padding: 4px; border: 1px solid #660066; user-select: none;
}
.spoiler.revealed { background: transparent; color: inherit; }
.btn {
    display: inline-block;
    min-height: 48px;
    min-width: 48px;
    padding: 10px 16px;
    border: 1px solid #cc44ff;
    background: #1a0a2e;
    color: #cc44ff;
    cursor: pointer;
    font-family: inherit;
    font-size: 0.9em;
    vertical-align: middle;
    text-align: center;
    line-height: 1.2;
}
.btn:hover { background: #2a1040; }
.btn.active { background: #440066; color: #ff88ff; }
.btn.danger { border-color: #ff4444; color: #ff4444; background: #1a0a0a; }
.btn.danger:hover { background: #2a0a0a; }
.btn.small { min-height: 36px; padding: 6px 10px; font-size: 0.8em; }
input[type=text], input[type=password], textarea {
    background: #0a0a1a;
    border: 1px solid #442255;
    color: #c8c8d8;
    font-family: inherit;
    font-size: 0.95em;
    padding: 10px;
    width: 100%;
    min-height: 48px;
    margin-bottom: 8px;
}
textarea { min-height: 80px; resize: vertical; }
input[type=text]:focus, input[type=password]:focus, textarea:focus {
    outline: none;
    border-color: #cc44ff;
}
.form-row { margin-bottom: 10px; }
label { display: block; color: #886699; margin-bottom: 4px; font-size: 0.85em; }
.banner {
    border: 1px solid #ff44cc;
    background: #1a0020;
    padding: 12px;
    margin-bottom: 12px;
    color: #ff88ff;
}
.banner.error { border-color: #ff4444; background: #1a0000; color: #ff8888; }
.banner.info { border-color: #4488ff; background: #00001a; color: #88aaff; }
.nav {
    display: flex;
    gap: 4px;
    flex-wrap: wrap;
    margin-bottom: 16px;
    border-bottom: 1px solid #332244;
    padding-bottom: 8px;
}
.score { color: #ffcc44; font-weight: bold; }
.score.neg { color: #ff4444; }
.emoji-picker { margin-bottom: 8px; }
.emoji-grid {
    display: flex;
    flex-wrap: wrap;
    gap: 2px;
    background: #0f0f1a;
    border: 1px solid #332244;
    padding: 6px;
    max-height: 160px;
    overflow-y: auto;
}
.emoji-btn {
    font-size: 1.4em;
    min-width: 44px;
    min-height: 44px;
    background: #1a0a2e;
    border: 1px solid #332244;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
}
.emoji-btn:hover { background: #2a1040; }
.emoji-display {
    font-size: 1.4em;
    background: #0a0a1a;
    border: 1px solid #442255;
    padding: 6px 10px;
    min-height: 48px;
    display: inline-block;
    min-width: 80px;
    vertical-align: middle;
    letter-spacing: 2px;
}
.tier-admin { color: #ff44cc; }
.tier-trusted { color: #44ccff; }
.tier-default { color: #c8c8d8; }
.tier-restricted { color: #ff4444; }
table { width: 100%; border-collapse: collapse; margin-bottom: 12px; font-size: 0.85em; }
th { background: #1a0a2e; color: #cc44ff; padding: 6px; border: 1px solid #332244; text-align: left; }
td { padding: 6px; border: 1px solid #221133; vertical-align: top; word-break: break-word; }
tr:nth-child(even) td { background: #0d0d18; }
.section { margin-bottom: 20px; }
.friends-toggle { margin-bottom: 12px; }
"""

# ---------------------------------------------------------------------------
# 6. BASE PAGE
# ---------------------------------------------------------------------------

def base_page(title, content, current_user=None):
    notif_html = ""
    if current_user:
        notifs = DB_CONN.execute(
            "SELECT message FROM notifications WHERE user_id=? AND read=0 ORDER BY created_at",
            (current_user["id"],)
        ).fetchall()
        if notifs:
            msgs = "".join(f"<div>{escape(n['message'])}</div>" for n in notifs)
            notif_html = f'<div class="banner">{msgs}</div>'
            DB_CONN.execute("UPDATE notifications SET read=1 WHERE user_id=? AND read=0", (current_user["id"],))
            DB_CONN.commit()

    user_info = ""
    if current_user:
        tier = current_user["post_tier"]
        tier_class = f"tier-{tier.lower()}"
        label = escape(current_user["session_label"])
        user_info = f'<span class="{tier_class}">[{label} · {tier}]</span>'

    return f"""<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<title>{escape(title)} — MESHAGORA</title>
<style>{BASE_CSS}</style>
</head>
<body>
<div class="container">
<h1>&#9670; MESHAGORA</h1>
<div class="nav">
  <a class="btn small" href="/">The Square</a>
  <a class="btn small" href="/profile">Profile</a>
  <a class="btn small" href="/admin">Admin</a>
  {user_info}
</div>
{notif_html}
{content}
</div>
<script>
function appendEmoji(fieldId, displayId, emoji, maxLen) {{
    var hidden = document.getElementById(fieldId);
    var display = document.getElementById(displayId);
    var arr = Array.from(hidden.value);
    if (arr.length < maxLen) {{
        hidden.value += emoji;
        display.textContent = hidden.value;
    }}
}}
function clearEmoji(fieldId, displayId) {{
    document.getElementById(fieldId).value = '';
    document.getElementById(displayId).textContent = '';
}}
</script>
</body>
</html>"""

# ---------------------------------------------------------------------------
# 7. EMOJI PICKER WIDGET
# ---------------------------------------------------------------------------

def emoji_picker(field_name, field_id, max_len=2, current_val=""):
    display_id = field_id + "_display"
    grid = ""
    for em in EMOJI_POOL:
        grid += f'<button type="button" class="emoji-btn" onclick="appendEmoji(\'{field_id}\',\'{display_id}\',\'{em}\',{max_len})">{em}</button>'
    return f"""
<div class="emoji-picker">
  <input type="hidden" name="{field_name}" id="{field_id}" value="{escape(current_val)}">
  <div style="margin-bottom:4px;display:flex;align-items:center;gap:8px;">
    <span class="emoji-display" id="{display_id}">{escape(current_val)}</span>
    <button type="button" class="btn small danger" onclick="clearEmoji('{field_id}','{display_id}')">CLEAR</button>
  </div>
  <div class="emoji-grid">{grid}</div>
</div>"""

# ---------------------------------------------------------------------------
# 8. HELPER FUNCTIONS
# ---------------------------------------------------------------------------

def get_current_user():
    uid = session.get("user_id")
    if not uid:
        return None
    return DB_CONN.execute("SELECT * FROM users WHERE id=?", (uid,)).fetchone()

def get_friend_key(user_id):
    row = DB_CONN.execute(
        "SELECT emoji_code FROM keys WHERE user_id=? AND key_type='FRIEND'", (user_id,)
    ).fetchone()
    return row["emoji_code"] if row else ""

def get_profile_key(user_id):
    row = DB_CONN.execute(
        "SELECT emoji_code FROM keys WHERE user_id=? AND key_type='PROFILE'", (user_id,)
    ).fetchone()
    return row["emoji_code"] if row else ""

def are_friends(a_id, b_id):
    a_key = get_friend_key(a_id)
    b_key = get_friend_key(b_id)
    code_ab = a_key + b_key
    code_ba = b_key + a_key
    ab = DB_CONN.execute(
        "SELECT id FROM handshakes WHERE from_user_id=? AND to_user_id=? AND submitted_code=?",
        (a_id, b_id, code_ab)
    ).fetchone()
    ba = DB_CONN.execute(
        "SELECT id FROM handshakes WHERE from_user_id=? AND to_user_id=? AND submitted_code=?",
        (b_id, a_id, code_ba)
    ).fetchone()
    return ab is not None and ba is not None

def get_friends(user_id):
    all_users = DB_CONN.execute("SELECT id FROM users WHERE id != ?", (user_id,)).fetchall()
    return [u["id"] for u in all_users if are_friends(user_id, u["id"])]

def restrict_user(user_id):
    global DEFAULT_POST_KEY
    DB_CONN.execute("UPDATE users SET post_tier='RESTRICTED' WHERE id=?", (user_id,))
    DB_CONN.commit()
    DEFAULT_POST_KEY = generate_emoji_key(2)

def check_auto_restrict(author_id):
    author = DB_CONN.execute("SELECT * FROM users WHERE id=?", (author_id,)).fetchone()
    if not author or author["post_tier"] == "RESTRICTED":
        return
    total_users = DB_CONN.execute("SELECT COUNT(*) FROM users").fetchone()[0]
    if total_users <= 1:
        return
    unique_reporters = DB_CONN.execute(
        """SELECT COUNT(DISTINCT r.reporter_id) FROM reports r
           JOIN posts p ON r.post_id = p.id
           WHERE p.author_id = ? AND r.reporter_id != ?""",
        (author_id, author_id)
    ).fetchone()[0]
    threshold = (total_users - 1) * 0.51
    if unique_reporters >= threshold:
        restrict_user(author_id)

def order_feed(posts):
    if not posts:
        return []
    if len(posts) == 1:
        return list(posts)
    posts = list(posts)
    max_score = max(p["score"] for p in posts)
    min_score = min(p["score"] for p in posts)
    # Find top pinned (highest score, most recent tiebreak)
    top_candidates = [p for p in posts if p["score"] == max_score]
    top_pin = max(top_candidates, key=lambda p: p["created_at"])
    # Find bottom pinned (lowest score, most recent tiebreak) — must be different from top
    remaining = [p for p in posts if p["id"] != top_pin["id"]]
    if not remaining:
        return [top_pin]
    bot_min = min(p["score"] for p in remaining)
    # Only pin bottom separately if it's actually different score from top or there are multiple posts
    bot_candidates = [p for p in remaining if p["score"] == bot_min]
    bot_pin = max(bot_candidates, key=lambda p: p["created_at"])
    middle = [p for p in remaining if p["id"] != bot_pin["id"]]
    middle.sort(key=lambda p: p["created_at"], reverse=True)
    if len(posts) == 2:
        return [top_pin, bot_pin]
    return [top_pin] + middle + [bot_pin]

def require_admin(f):
    @functools.wraps(f)
    def decorated(*args, **kwargs):
        if not session.get("admin_authed"):
            return redirect(url_for("admin"))
        return f(*args, **kwargs)
    return decorated

def vote_weight(tier):
    return {"ADMIN": 1, "TRUSTED": 2, "DEFAULT": 1, "RESTRICTED": 0}.get(tier, 0)

def tier_badge(tier):
    cls = f"tier-{tier.lower()}"
    return f'<span class="{cls}">[{escape(tier)}]</span>'

def has_reported(post_id, user_id):
    return DB_CONN.execute(
        "SELECT id FROM reports WHERE post_id=? AND reporter_id=?", (post_id, user_id)
    ).fetchone() is not None

def report_count(post_id):
    return DB_CONN.execute(
        "SELECT COUNT(*) FROM reports WHERE post_id=?", (post_id,)
    ).fetchone()[0]

def user_vote(post_id, user_id):
    row = DB_CONN.execute(
        "SELECT direction FROM votes WHERE post_id=? AND user_id=?", (post_id, user_id)
    ).fetchone()
    return row["direction"] if row else None

# ---------------------------------------------------------------------------
# 9. ROUTES
# ---------------------------------------------------------------------------

@app.route("/join", methods=["GET", "POST"])
def join():
    if session.get("user_id"):
        return redirect(url_for("square"))

    error = ""
    if request.method == "POST":
        username = request.form.get("username", "").strip()
        profile_key = request.form.get("profile_key", "")
        friend_key  = request.form.get("friend_key", "")

        if not username:
            error = "Username required."
        elif len(profile_key) < 2:
            error = "Profile Key must be 2 emoji."
        elif len(friend_key) < 2:
            error = "Friend Key must be 2 emoji."
        else:
            existing = DB_CONN.execute("SELECT id FROM users WHERE username=?", (username,)).fetchone()
            if existing:
                error = "Username taken."
            else:
                cur = DB_CONN.execute(
                    "INSERT INTO users (session_label, username) VALUES (?, ?)",
                    ("?", username)
                )
                DB_CONN.commit()
                uid = cur.lastrowid
                label = f"Anon #{uid}"
                DB_CONN.execute("UPDATE users SET session_label=? WHERE id=?", (label, uid))
                DB_CONN.execute(
                    "INSERT INTO keys (user_id, key_type, emoji_code) VALUES (?, 'PROFILE', ?)",
                    (uid, profile_key)
                )
                DB_CONN.execute(
                    "INSERT INTO keys (user_id, key_type, emoji_code) VALUES (?, 'FRIEND', ?)",
                    (uid, friend_key)
                )
                DB_CONN.commit()
                session["user_id"] = uid
                session["show_default_key"] = True
                return redirect(url_for("square"))

    error_html = f'<div class="banner error">{escape(error)}</div>' if error else ""
    content = f"""
{error_html}
<div class="card">
  <h2>JOIN THE SQUARE</h2>
  <p style="color:#886699;font-size:0.85em;margin-bottom:12px;">Choose a secret username. Pick two emoji keys.</p>
  <form method="POST">
    <div class="form-row">
      <label>USERNAME (secret — share only with friends)</label>
      <input type="text" name="username" placeholder="e.g. CoolPenguin42" autocomplete="off" autocorrect="off" autocapitalize="off">
    </div>
    <div class="form-row">
      <label>PROFILE KEY (2 emoji — protects your notes)</label>
      {emoji_picker("profile_key", "pk_field", max_len=2)}
    </div>
    <div class="form-row">
      <label>FRIEND KEY (2 emoji — for handshakes)</label>
      {emoji_picker("friend_key", "fk_field", max_len=2)}
    </div>
    <button type="submit" class="btn" style="width:100%;min-height:56px;">JOIN</button>
  </form>
</div>
"""
    return base_page("Join", content)


@app.route("/", methods=["GET", "POST"])
def square():
    user = get_current_user()
    if not user:
        return redirect(url_for("join"))

    error = ""
    success = ""

    # Show default key on first join
    if session.pop("show_default_key", False):
        success = f"Welcome, {escape(user['session_label'])}! Default Post Key: {DEFAULT_POST_KEY}"

    if request.method == "POST":
        content  = request.form.get("content", "").strip()
        post_key = request.form.get("post_key", "")
        tier     = user["post_tier"]

        valid_key = False
        if tier == "ADMIN":
            valid_key = (post_key == ADMIN_POST_KEY)
        elif tier == "TRUSTED":
            valid_key = (post_key == TRUSTED_POST_KEY)
        elif tier == "DEFAULT":
            valid_key = (post_key == DEFAULT_POST_KEY)
        elif tier == "RESTRICTED":
            admin_key = request.form.get("admin_key", "")
            valid_key = (admin_key == ADMIN_POST_KEY)

        if not content:
            error = "Post content required."
        elif not valid_key:
            error = "Post key incorrect."
        else:
            DB_CONN.execute(
                "INSERT INTO posts (author_id, content) VALUES (?, ?)",
                (user["id"], content)
            )
            DB_CONN.commit()
            return redirect(url_for("square"))

    friends_only = request.args.get("friends_only") == "1"
    friend_ids   = get_friends(user["id"])

    if friends_only:
        if not friend_ids:
            raw_posts = []
        else:
            placeholders = ",".join("?" * len(friend_ids))
            raw_posts = DB_CONN.execute(
                f"SELECT * FROM posts WHERE author_id IN ({placeholders}) ORDER BY created_at DESC",
                friend_ids
            ).fetchall()
    else:
        raw_posts = DB_CONN.execute(
            "SELECT * FROM posts ORDER BY created_at DESC"
        ).fetchall()

    ordered = order_feed(raw_posts)

    posts_html = ""
    for i, post in enumerate(ordered):
        author = DB_CONN.execute("SELECT * FROM users WHERE id=?", (post["author_id"],)).fetchone()
        is_friend = post["author_id"] in friend_ids
        is_own    = post["author_id"] == user["id"]
        display_name = escape(author["username"]) if (is_friend or is_own) else escape(author["session_label"])

        pin_class = ""
        if i == 0 and len(ordered) > 1:
            pin_class = " pinned-top"
        elif i == len(ordered) - 1 and len(ordered) > 1:
            pin_class = " pinned-bottom"

        rcount     = report_count(post["id"])
        already_reported = has_reported(post["id"], user["id"])
        my_vote    = user_vote(post["id"], user["id"])

        score_val  = post["score"]
        score_cls  = "score neg" if score_val < 0 else "score"

        content_escaped = escape(post["content"])
        if rcount > 0:
            content_html = f'<span class="spoiler" onclick="this.classList.toggle(\'revealed\')">[REPORTED — click to reveal] {content_escaped}</span>'
        else:
            content_html = str(content_escaped)

        up_active   = "active" if my_vote == 1  else ""
        down_active = "active" if my_vote == -1 else ""

        report_btn = ""
        if not is_own:
            if already_reported:
                report_btn = '<span class="btn small" style="opacity:0.4;cursor:default;">reported</span>'
            else:
                report_btn = f'''<form method="POST" action="/report" style="display:inline">
                  <input type="hidden" name="post_id" value="{post['id']}">
                  <input type="hidden" name="friends_only" value="{'1' if friends_only else '0'}">
                  <button type="submit" class="btn small danger">report</button>
                </form>'''

        posts_html += f"""
<div class="card{pin_class}">
  <div class="post-meta">{display_name} · <span class="{score_cls}">{score_val:+d}</span></div>
  <div class="post-content">{content_html}</div>
  <div style="display:flex;gap:4px;flex-wrap:wrap;align-items:center;">
    <form method="POST" action="/vote" style="display:inline">
      <input type="hidden" name="post_id" value="{post['id']}">
      <input type="hidden" name="direction" value="1">
      <input type="hidden" name="friends_only" value="{'1' if friends_only else '0'}">
      <button type="submit" class="btn small {up_active}">&#9650; up</button>
    </form>
    <form method="POST" action="/vote" style="display:inline">
      <input type="hidden" name="post_id" value="{post['id']}">
      <input type="hidden" name="direction" value="-1">
      <input type="hidden" name="friends_only" value="{'1' if friends_only else '0'}">
      <button type="submit" class="btn small {down_active}">&#9660; down</button>
    </form>
    {report_btn}
  </div>
</div>"""

    if not posts_html:
        posts_html = '<div class="card"><p style="color:#886699;">No posts yet.</p></div>'

    tier = user["post_tier"]
    if tier == "ADMIN":
        key_label = "Admin Post Key (4 emoji)"
        key_picker = emoji_picker("post_key", "postkey_field", max_len=4)
        extra_field = ""
    elif tier == "TRUSTED":
        key_label = "Trusted Post Key (2 emoji)"
        key_picker = emoji_picker("post_key", "postkey_field", max_len=2)
        extra_field = ""
    elif tier == "DEFAULT":
        key_label = "Default Post Key (2 emoji)"
        key_picker = emoji_picker("post_key", "postkey_field", max_len=2)
        extra_field = ""
    else:  # RESTRICTED
        key_label = "Admin Co-Sign Key (4 emoji — admin must approve)"
        key_picker = emoji_picker("admin_key", "postkey_field", max_len=4)
        extra_field = '<input type="hidden" name="post_key" value="">'

    fo_toggle_url = url_for("square") + ("" if friends_only else "?friends_only=1")
    fo_label = "&#9733; Friends Only (ON)" if friends_only else "&#9733; Friends Only (OFF)"
    fo_active = "active" if friends_only else ""

    success_html = f'<div class="banner">{escape(success)}</div>' if success else ""
    error_html   = f'<div class="banner error">{escape(error)}</div>' if error else ""

    content = f"""
{success_html}{error_html}
<div class="friends-toggle">
  <a href="{fo_toggle_url}" class="btn {fo_active}">{fo_label}</a>
</div>
<div class="card">
  <h2>POST TO THE SQUARE</h2>
  <form method="POST">
    {extra_field}
    <div class="form-row">
      <textarea name="content" placeholder="What's on your mind?" rows="3"></textarea>
    </div>
    <div class="form-row">
      <label>{escape(key_label)}</label>
      {key_picker}
    </div>
    <button type="submit" class="btn" style="width:100%;min-height:56px;">POST</button>
  </form>
</div>
<div class="section">
  <h2>{'FRIENDS FEED' if friends_only else 'THE SQUARE'}</h2>
  {posts_html}
</div>
"""
    return base_page("The Square", content, current_user=user)


@app.route("/vote", methods=["POST"])
def vote():
    user = get_current_user()
    if not user:
        return redirect(url_for("join"))

    post_id     = int(request.form.get("post_id", 0))
    direction   = int(request.form.get("direction", 1))
    friends_only = request.form.get("friends_only", "0")
    if direction not in (1, -1):
        return redirect(url_for("square"))

    weight = vote_weight(user["post_tier"])
    existing = DB_CONN.execute(
        "SELECT id, direction FROM votes WHERE post_id=? AND user_id=?",
        (post_id, user["id"])
    ).fetchone()

    if existing:
        if existing["direction"] == direction:
            DB_CONN.execute("DELETE FROM votes WHERE post_id=? AND user_id=?", (post_id, user["id"]))
        else:
            DB_CONN.execute(
                "UPDATE votes SET direction=?, weight=? WHERE post_id=? AND user_id=?",
                (direction, weight, post_id, user["id"])
            )
    else:
        DB_CONN.execute(
            "INSERT INTO votes (post_id, user_id, direction, weight) VALUES (?, ?, ?, ?)",
            (post_id, user["id"], direction, weight)
        )

    DB_CONN.execute(
        "UPDATE posts SET score=(SELECT COALESCE(SUM(direction*weight),0) FROM votes WHERE post_id=?) WHERE id=?",
        (post_id, post_id)
    )
    DB_CONN.commit()

    redirect_url = url_for("square") + ("?friends_only=1" if friends_only == "1" else "")
    return redirect(redirect_url)


@app.route("/report", methods=["POST"])
def report():
    user = get_current_user()
    if not user:
        return redirect(url_for("join"))

    post_id = int(request.form.get("post_id", 0))
    post    = DB_CONN.execute("SELECT * FROM posts WHERE id=?", (post_id,)).fetchone()
    if not post:
        return redirect(url_for("square"))
    if post["author_id"] == user["id"]:
        return redirect(url_for("square"))

    try:
        DB_CONN.execute(
            "INSERT INTO reports (post_id, reporter_id) VALUES (?, ?)",
            (post_id, user["id"])
        )
        DB_CONN.execute(
            "INSERT INTO notifications (user_id, message) VALUES (?, ?)",
            (post["author_id"], "One of your posts has been reported.")
        )
        DB_CONN.commit()
    except sqlite3.IntegrityError:
        pass

    check_auto_restrict(post["author_id"])
    return redirect(url_for("square"))


@app.route("/profile", methods=["GET", "POST"])
def profile():
    user = get_current_user()
    if not user:
        return redirect(url_for("join"))

    message = ""
    error   = ""

    # Consume handshake message from redirect
    hs_msg = session.pop("handshake_msg", None)
    if hs_msg:
        kind, text = hs_msg
        if kind == "error":
            error = text
        else:
            message = text

    if request.method == "POST":
        action = request.form.get("action", "")
        prof_key_submitted = request.form.get("profile_key_input", "")
        stored_key = get_profile_key(user["id"])

        if action == "edit_notes":
            notes = request.form.get("notes", "")
            if prof_key_submitted != stored_key:
                error = "Profile Key incorrect."
            else:
                DB_CONN.execute("UPDATE users SET profile_notes=? WHERE id=?", (notes, user["id"]))
                DB_CONN.commit()
                message = "Notes saved."
                user = get_current_user()

        elif action == "toggle_visibility":
            if prof_key_submitted != stored_key:
                error = "Profile Key incorrect."
            else:
                new_vis = 0 if user["notes_visible"] else 1
                DB_CONN.execute("UPDATE users SET notes_visible=? WHERE id=?", (new_vis, user["id"]))
                DB_CONN.commit()
                message = "Visibility updated."
                user = get_current_user()

    # Reload user
    user = get_current_user()
    friend_key_val   = get_friend_key(user["id"])
    profile_key_val  = get_profile_key(user["id"])

    # Confirmed friends
    friend_ids = get_friends(user["id"])
    friends_html = ""
    if friend_ids:
        for fid in friend_ids:
            fr = DB_CONN.execute("SELECT session_label, username FROM users WHERE id=?", (fid,)).fetchone()
            friends_html += f'<div class="card" style="padding:8px;">{escape(fr["username"])} ({escape(fr["session_label"])})</div>'
    else:
        friends_html = '<p style="color:#886699;">No confirmed friends yet.</p>'

    # All users for handshake dropdown (excluding self)
    all_users = DB_CONN.execute(
        "SELECT id, username, session_label FROM users WHERE id != ? ORDER BY session_label",
        (user["id"],)
    ).fetchall()
    user_options = "".join(
        f'<option value="{escape(u["username"])}">{escape(u["username"])}</option>'
        for u in all_users
    )

    vis_label = "PUBLIC" if user["notes_visible"] else "PRIVATE"
    message_html = f'<div class="banner">{escape(message)}</div>' if message else ""
    error_html   = f'<div class="banner error">{escape(error)}</div>' if error else ""

    content = f"""
{message_html}{error_html}
<div class="card section">
  <h2>YOUR IDENTITY</h2>
  <table>
    <tr><th>Session Label</th><td>{escape(user['session_label'])}</td></tr>
    <tr><th>Username</th><td>{escape(user['username'])}</td></tr>
    <tr><th>Tier</th><td>{tier_badge(user['post_tier'])}</td></tr>
    <tr><th>Friend Key</th><td style="font-size:1.4em;letter-spacing:2px;">{escape(friend_key_val)}</td></tr>
    <tr><th>Profile Key</th><td style="font-size:1.4em;letter-spacing:2px;">{escape(profile_key_val)}</td></tr>
  </table>
</div>

<div class="card section">
  <h2>YOUR NOTES <span style="font-size:0.8em;color:#886699;">[{vis_label}]</span></h2>
  <form method="POST">
    <input type="hidden" name="action" value="edit_notes">
    <textarea name="notes" rows="4">{escape(user['profile_notes'])}</textarea>
    <label>Profile Key (to save)</label>
    {emoji_picker("profile_key_input", "pk_edit_field", max_len=2)}
    <button type="submit" class="btn" style="width:100%;">SAVE NOTES</button>
  </form>
  <form method="POST" style="margin-top:8px;">
    <input type="hidden" name="action" value="toggle_visibility">
    <label>Profile Key (to toggle visibility)</label>
    {emoji_picker("profile_key_input", "pk_toggle_field", max_len=2)}
    <button type="submit" class="btn" style="width:100%;">TOGGLE VISIBILITY (now: {vis_label})</button>
  </form>
</div>

<div class="card section">
  <h2>CONFIRMED FRIENDS</h2>
  {friends_html}
</div>

<div class="card section">
  <h2>HANDSHAKE</h2>
  <p style="color:#886699;font-size:0.85em;margin-bottom:10px;">Exchange usernames and Friend Keys in person, then submit here.</p>
  <form method="POST" action="/handshake">
    <div class="form-row">
      <label>THEIR USERNAME</label>
      <select name="target_username" style="background:#0a0a1a;border:1px solid #442255;color:#c8c8d8;font-family:inherit;font-size:0.95em;padding:10px;width:100%;min-height:48px;">
        <option value="">-- select --</option>
        {user_options}
      </select>
    </div>
    <div class="form-row">
      <label>YOUR FRIEND KEY (2 emoji)</label>
      {emoji_picker("my_friend_key", "hs_my_field", max_len=2)}
    </div>
    <div class="form-row">
      <label>THEIR FRIEND KEY (2 emoji)</label>
      {emoji_picker("their_friend_key", "hs_their_field", max_len=2)}
    </div>
    <button type="submit" class="btn" style="width:100%;min-height:56px;">SUBMIT HANDSHAKE</button>
  </form>
</div>
"""
    return base_page("Profile", content, current_user=user)


@app.route("/handshake", methods=["POST"])
def handshake():
    user = get_current_user()
    if not user:
        return redirect(url_for("join"))

    target_username  = request.form.get("target_username", "")
    my_friend_key    = request.form.get("my_friend_key", "")
    their_friend_key = request.form.get("their_friend_key", "")

    target = DB_CONN.execute("SELECT * FROM users WHERE username=?", (target_username,)).fetchone()
    valid = (
        target is not None
        and target["id"] != user["id"]
        and my_friend_key == get_friend_key(user["id"])
        and their_friend_key == get_friend_key(target["id"])
    )

    if not valid:
        session["handshake_msg"] = ("error", "One or both keys are incorrect.")
        return redirect(url_for("profile"))

    submitted_code = my_friend_key + their_friend_key
    try:
        DB_CONN.execute(
            "INSERT INTO handshakes (from_user_id, to_user_id, submitted_code) VALUES (?, ?, ?)",
            (user["id"], target["id"], submitted_code)
        )
        DB_CONN.commit()
    except sqlite3.IntegrityError:
        pass

    if are_friends(user["id"], target["id"]):
        session["handshake_msg"] = ("success", f"Friendship confirmed with {target['username']}!")
    else:
        session["handshake_msg"] = ("info", f"Your side submitted. Waiting for {escape(target['session_label'])} to complete the handshake.")

    return redirect(url_for("profile"))


@app.route("/admin", methods=["GET", "POST"])
def admin():
    error = ""
    if request.method == "POST" and not session.get("admin_authed"):
        submitted = request.form.get("admin_key", "")
        if submitted == ADMIN_POST_KEY:
            session["admin_authed"] = True
        else:
            error = "Incorrect Admin Key."

    if not session.get("admin_authed"):
        error_html = f'<div class="banner error">{escape(error)}</div>' if error else ""
        content = f"""
{error_html}
<div class="card">
  <h2>ADMIN LOGIN</h2>
  <form method="POST">
    <div class="form-row">
      <label>Admin Post Key (4 emoji)</label>
      {emoji_picker("admin_key", "admin_login_key", max_len=4)}
    </div>
    <button type="submit" class="btn" style="width:100%;min-height:56px;">LOGIN</button>
  </form>
</div>"""
        return base_page("Admin Login", content)

    # Full admin panel
    users = DB_CONN.execute("SELECT * FROM users ORDER BY id").fetchall()
    posts = DB_CONN.execute("SELECT * FROM posts ORDER BY id DESC").fetchall()
    votes = DB_CONN.execute("SELECT * FROM votes ORDER BY id DESC LIMIT 50").fetchall()
    reports_all = DB_CONN.execute("SELECT * FROM reports ORDER BY id DESC LIMIT 50").fetchall()
    keys_all = DB_CONN.execute("SELECT * FROM keys ORDER BY id").fetchall()

    users_html = ""
    for u in users:
        tier = u["post_tier"]
        users_html += f"""
<tr>
  <td>{escape(u['session_label'])}</td>
  <td>{escape(u['username'])}</td>
  <td>{tier_badge(tier)}</td>
  <td style="white-space:nowrap;">
    <form method="POST" action="/admin/action" style="display:inline">
      <input type="hidden" name="target_id" value="{u['id']}">
      <input type="hidden" name="action" value="restrict">
      <button class="btn small danger">Restrict</button>
    </form>
    <form method="POST" action="/admin/action" style="display:inline">
      <input type="hidden" name="target_id" value="{u['id']}">
      <input type="hidden" name="action" value="unrestrict">
      <button class="btn small">Unrestrict</button>
    </form>
    <form method="POST" action="/admin/action" style="display:inline">
      <input type="hidden" name="target_id" value="{u['id']}">
      <input type="hidden" name="action" value="promote_trusted">
      <button class="btn small">Promote</button>
    </form>
    <form method="POST" action="/admin/action" style="display:inline">
      <input type="hidden" name="target_id" value="{u['id']}">
      <input type="hidden" name="action" value="demote_default">
      <button class="btn small">Demote</button>
    </form>
  </td>
</tr>"""

    qr_btn_label = "HIDE QR" if QR_VISIBLE else "SHOW QR"
    qr_img_html  = f'<img src="/qr" style="border:2px solid #cc44ff;max-width:200px;display:block;margin:8px 0;" alt="QR">' if QR_VISIBLE else ""

    posts_html = "".join(
        f"<tr><td>{p['id']}</td><td>{escape(p['content'][:60])}</td><td>{p['author_id']}</td><td>{p['score']}</td></tr>"
        for p in posts
    )
    votes_html = "".join(
        f"<tr><td>{v['id']}</td><td>{v['post_id']}</td><td>{v['user_id']}</td><td>{v['direction']}</td><td>{v['weight']}</td></tr>"
        for v in votes
    )
    reports_html = "".join(
        f"<tr><td>{r['id']}</td><td>{r['post_id']}</td><td>{r['reporter_id']}</td></tr>"
        for r in reports_all
    )
    keys_html = "".join(
        f"<tr><td>{k['id']}</td><td>{k['user_id']}</td><td>{k['key_type']}</td><td style='font-size:1.3em;letter-spacing:2px;'>{escape(k['emoji_code'])}</td></tr>"
        for k in keys_all
    )

    content = f"""
<div class="card section">
  <h2>CURRENT KEYS</h2>
  <table>
    <tr><th>Admin Post Key</th><td style="font-size:1.5em;letter-spacing:3px;">{escape(ADMIN_POST_KEY)}</td></tr>
    <tr><th>Trusted Post Key</th><td style="font-size:1.5em;letter-spacing:3px;">{escape(TRUSTED_POST_KEY)}</td></tr>
    <tr><th>Default Post Key</th><td style="font-size:1.5em;letter-spacing:3px;color:#ff44cc;">{escape(DEFAULT_POST_KEY)}</td></tr>
  </table>
  <div style="display:flex;gap:8px;flex-wrap:wrap;margin-top:8px;">
    <form method="POST" action="/admin/action">
      <input type="hidden" name="action" value="rotate_default_key">
      <button class="btn danger">ROTATE DEFAULT KEY</button>
    </form>
    <form method="POST" action="/admin/action">
      <input type="hidden" name="action" value="toggle_qr">
      <button class="btn">{qr_btn_label}</button>
    </form>
    <form method="POST" action="/admin/action">
      <input type="hidden" name="action" value="flood_attack">
      <button class="btn danger">FLOOD ATTACK</button>
    </form>
  </div>
  {qr_img_html}
</div>

<div class="card section">
  <h2>USERS</h2>
  <table>
    <tr><th>Label</th><th>Username</th><th>Tier</th><th>Actions</th></tr>
    {users_html}
  </table>
</div>

<div class="card section">
  <h2>RAW DB — POSTS</h2>
  <table>
    <tr><th>id</th><th>content</th><th>author</th><th>score</th></tr>
    {posts_html}
  </table>
</div>

<div class="card section">
  <h2>RAW DB — VOTES (last 50)</h2>
  <table>
    <tr><th>id</th><th>post</th><th>user</th><th>dir</th><th>wt</th></tr>
    {votes_html}
  </table>
</div>

<div class="card section">
  <h2>RAW DB — REPORTS (last 50)</h2>
  <table>
    <tr><th>id</th><th>post</th><th>reporter</th></tr>
    {reports_html}
  </table>
</div>

<div class="card section">
  <h2>RAW DB — KEYS</h2>
  <table>
    <tr><th>id</th><th>user</th><th>type</th><th>code</th></tr>
    {keys_html}
  </table>
</div>
"""

    # ── Platform View ────────────────────────────────────────────────────────
    doxx_rows = DB_CONN.execute("""
        SELECT u.id, u.session_label, u.username, u.post_tier,
               COUNT(DISTINCT p.id) AS post_count,
               COALESCE(SUM(p.score), 0) AS net_score
        FROM users u
        LEFT JOIN posts p ON p.author_id = u.id
        GROUP BY u.id
        ORDER BY u.id
    """).fetchall()

    hs_rows = DB_CONN.execute("""
        SELECT h.from_user_id, h.to_user_id,
               u1.session_label AS from_label,
               u2.session_label AS to_label,
               u1.username AS from_name,
               u2.username AS to_name
        FROM handshakes h
        JOIN users u1 ON h.from_user_id = u1.id
        JOIN users u2 ON h.to_user_id = u2.id
        ORDER BY h.id
    """).fetchall()

    # Build directed-pair set once; check both directions for CONFIRMED
    hs_pairs = {(r["from_user_id"], r["to_user_id"]) for r in hs_rows}
    seen_pairs = set()
    graph_rows = []
    for r in hs_rows:
        a, b = r["from_user_id"], r["to_user_id"]
        key = (min(a, b), max(a, b))
        if key in seen_pairs:
            continue
        seen_pairs.add(key)
        confirmed = (a, b) in hs_pairs and (b, a) in hs_pairs
        graph_rows.append((r, confirmed))

    def is_friends_pair(id_a, id_b):
        return (id_a, id_b) in hs_pairs and (id_b, id_a) in hs_pairs

    vote_corr = DB_CONN.execute("""
        SELECT v.user_id AS voter_id,
               uv.session_label AS voter_label,
               uv.username AS voter_name,
               p.author_id,
               ua.session_label AS author_label,
               ua.username AS author_name,
               SUM(v.direction * v.weight) AS net
        FROM votes v
        JOIN posts p ON v.post_id = p.id
        JOIN users uv ON v.user_id = uv.id
        JOIN users ua ON p.author_id = ua.id
        WHERE v.user_id != p.author_id
        GROUP BY v.user_id, p.author_id
        ORDER BY ABS(SUM(v.direction * v.weight)) DESC
    """).fetchall()

    report_corr = DB_CONN.execute("""
        SELECT r.reporter_id,
               ur.session_label AS reporter_label,
               ur.username AS reporter_name,
               p.author_id,
               ua.session_label AS author_label,
               ua.username AS author_name,
               COUNT(*) AS count
        FROM reports r
        JOIN posts p ON r.post_id = p.id
        JOIN users ur ON r.reporter_id = ur.id
        JOIN users ua ON p.author_id = ua.id
        WHERE r.reporter_id != p.author_id
        GROUP BY r.reporter_id, p.author_id
        ORDER BY COUNT(*) DESC
    """).fetchall()

    # Build HTML for Panel 1
    doxx_html = ""
    for r in doxx_rows:
        net = r["net_score"]
        net_cls = "neg" if net < 0 else ""
        doxx_html += f"""<tr>
  <td>{escape(r['session_label'])}</td>
  <td>{escape(r['username'])}</td>
  <td>{tier_badge(r['post_tier'])}</td>
  <td>{r['post_count']}</td>
  <td class="score {net_cls}">{net:+d}</td>
</tr>"""

    # Build HTML for Panel 2
    graph_html = ""
    for r, confirmed in graph_rows:
        status_color = "#44ccff" if confirmed else "#886699"
        status_text  = "CONFIRMED" if confirmed else "PENDING"
        graph_html += f"""<tr>
  <td>{escape(r['from_label'])} · {escape(r['from_name'])}</td>
  <td>{escape(r['to_label'])} · {escape(r['to_name'])}</td>
  <td style="color:{status_color};font-weight:bold;">{status_text}</td>
</tr>"""

    # Build HTML for Panel 3 — votes
    vote_corr_html = ""
    for r in vote_corr:
        net = r["net"]
        net_cls = "neg" if net < 0 else ""
        friends_badge = ' <span style="color:#44ccff;font-size:0.8em;">[FRIENDS]</span>' \
                        if is_friends_pair(r["voter_id"], r["author_id"]) else ""
        vote_corr_html += f"""<tr>
  <td>{escape(r['voter_label'])} · {escape(r['voter_name'])}</td>
  <td>{escape(r['author_label'])} · {escape(r['author_name'])}</td>
  <td class="score {net_cls}">{net:+d}</td>
  <td>{friends_badge}</td>
</tr>"""

    # Build HTML for Panel 3 — reports
    report_corr_html = ""
    for r in report_corr:
        friends_badge = ' <span style="color:#44ccff;font-size:0.8em;">[FRIENDS]</span>' \
                        if is_friends_pair(r["reporter_id"], r["author_id"]) else ""
        count_cls = "tier-restricted" if r["count"] >= 3 else "tier-default"
        report_corr_html += f"""<tr>
  <td>{escape(r['reporter_label'])} · {escape(r['reporter_name'])}</td>
  <td>{escape(r['author_label'])} · {escape(r['author_name'])}</td>
  <td class="{count_cls}">{r['count']}</td>
  <td>{friends_badge}</td>
</tr>"""

    platform_view_html = f"""
<h2 style="color:#ff44cc;border-top:2px solid #cc44ff;padding-top:12px;margin-top:8px;">
  &#9672; WHAT THE PLATFORM SEES
</h2>

<div class="card section">
  <h2>DEANONYMIZATION &mdash; WHO IS WHO</h2>
  <table>
    <tr><th>Session Label</th><th>Username</th><th>Tier</th><th>Posts</th><th>Net Score</th></tr>
    {doxx_html}
  </table>
</div>

<div class="card section">
  <h2>SOCIAL GRAPH &mdash; ALL CONNECTIONS</h2>
  <table>
    <tr><th>User A</th><th>User B</th><th>Status</th></tr>
    {graph_html}
  </table>
</div>

<div class="card section">
  <h2>BEHAVIORAL CORRELATIONS &mdash; WHAT THE PLATFORM INFERS</h2>
  <h3 style="color:#c8c8d8;margin-top:8px;">Votes</h3>
  <table>
    <tr><th>Voter</th><th>Author</th><th>Net Vote</th><th>Friends?</th></tr>
    {vote_corr_html}
  </table>
  <h3 style="color:#c8c8d8;margin-top:16px;">Reports</h3>
  <table>
    <tr><th>Reporter</th><th>Author</th><th>Count</th><th>Friends?</th></tr>
    {report_corr_html}
  </table>
</div>
"""
    content += platform_view_html
    return base_page("Admin Panel", content)


@app.route("/admin/action", methods=["POST"])
@require_admin
def admin_action():
    global DEFAULT_POST_KEY, QR_VISIBLE, QR_IMAGE_BYTES
    action    = request.form.get("action", "")
    target_id = request.form.get("target_id", None)

    if action == "restrict" and target_id:
        restrict_user(int(target_id))

    elif action == "unrestrict" and target_id:
        DB_CONN.execute("UPDATE users SET post_tier='DEFAULT' WHERE id=?", (int(target_id),))
        DB_CONN.commit()

    elif action == "promote_trusted" and target_id:
        DB_CONN.execute("UPDATE users SET post_tier='TRUSTED' WHERE id=?", (int(target_id),))
        DB_CONN.commit()

    elif action == "demote_default" and target_id:
        DB_CONN.execute("UPDATE users SET post_tier='DEFAULT' WHERE id=?", (int(target_id),))
        DB_CONN.commit()

    elif action == "rotate_default_key":
        DEFAULT_POST_KEY = generate_emoji_key(2)

    elif action == "toggle_qr":
        QR_VISIBLE = not QR_VISIBLE

    elif action == "flood_attack":
        for i in range(5):
            bot_name = f"Bot_{os.urandom(2).hex()}"
            cur = DB_CONN.execute(
                "INSERT INTO users (session_label, username, post_tier) VALUES (?, ?, 'DEFAULT')",
                ("?", bot_name)
            )
            DB_CONN.commit()
            bot_id    = cur.lastrowid
            bot_label = f"Anon #{bot_id}"
            DB_CONN.execute("UPDATE users SET session_label=? WHERE id=?", (bot_label, bot_id))
            flood_msgs = [
                "SYSTEM NOTICE: All users must re-register.",
                "FREE COINS click here [link removed by admin]",
                "Did you know? Your data is being collected.",
                "ALERT: Someone knows your real name.",
                "Join the resistance. Ask me how.",
            ]
            DB_CONN.execute(
                "INSERT INTO posts (author_id, content) VALUES (?, ?)",
                (bot_id, flood_msgs[i % len(flood_msgs)])
            )
            pk = generate_emoji_key(2)
            fk = generate_emoji_key(2)
            DB_CONN.execute("INSERT INTO keys (user_id, key_type, emoji_code) VALUES (?, 'PROFILE', ?)", (bot_id, pk))
            DB_CONN.execute("INSERT INTO keys (user_id, key_type, emoji_code) VALUES (?, 'FRIEND', ?)", (bot_id, fk))
        DB_CONN.commit()

    return redirect(url_for("admin"))


@app.route("/qr")
def qr():
    if not QR_VISIBLE:
        return Response("Not found", status=404)
    return Response(QR_IMAGE_BYTES, mimetype="image/png")


# ---------------------------------------------------------------------------
# 10. ENTRY POINT
# ---------------------------------------------------------------------------

if __name__ == "__main__":
    all_ips = get_all_local_ips()
    print("=" * 50)
    print("MESHAGORA v2.3 — SOCIAL SANDBOX")
    print(f"Admin Post Key   : {ADMIN_POST_KEY}")
    print(f"Trusted Post Key : {TRUSTED_POST_KEY}")
    print(f"Default Post Key : {DEFAULT_POST_KEY}")
    print("-" * 50)
    if all_ips:
        first = True
        for ip, iface in all_ips:
            label = "Join URL  :" if first else "Also try  :"
            first = False
            print(f"{label} http://{ip}:5000/join  ({iface})")
    else:
        print(f"Join URL  : {JOIN_URL}  (detection failed)")
    print(f"QR code   : http://{HOTSPOT_IP}:5000/qr")
    print("If no URL works, run:  ifconfig | grep inet")
    print("=" * 50)

    app.run(host="0.0.0.0", port=5000, debug=False, threaded=False)
