"""
Web control panel for the carrot bot.

Runs as its own small Flask process, sitting next to main.py in the same
folder, and talks to the SAME game.db through the SAME db.py module the bot
uses — so anything you do here (ban a user, change a size, create a promo
code, schedule an event) is instantly the live truth for the bot too.

The one thing this process cannot do by itself is *send* Rubika messages,
because only the running bot process (main.py) holds the authenticated bot
session. So "Broadcast" here queues the message in a `broadcast_queue` table;
main.py has a small poller (process_broadcast_queue) that picks it up and
actually sends it within a few seconds, then reports back sent/failed counts.
That means main.py needs to be running for broadcasts to go out — the panel
will tell you if it looks like nothing has picked a job up.

Run it:
    pip install flask
    ADMIN_PANEL_PASSWORD="something-strong" python admin_panel.py
Then open http://localhost:8787 (or the PANEL_PORT you set).
"""

import os
import time
import datetime
import secrets

from flask import Flask, request, jsonify, session, send_from_directory

import db
import config

APP_DIR = os.path.dirname(os.path.abspath(__file__))

app = Flask(__name__, static_folder=os.path.join(APP_DIR, "panel_static"), static_url_path="")
app.secret_key = os.environ.get("PANEL_SECRET_KEY", secrets.token_hex(32))

PANEL_PASSWORD = os.environ.get("ADMIN_PANEL_PASSWORD", "333")
if not PANEL_PASSWORD:
    # Fail loudly rather than silently exposing a bot control panel with no auth.
    raise SystemExit(
        "Set ADMIN_PANEL_PASSWORD before starting the panel, e.g.:\n"
        "  ADMIN_PANEL_PASSWORD='choose-a-strong-password' python admin_panel.py"
    )


# ---------------------------------------------------------------- auth -----

def require_auth():
    if not session.get("authed"):
        return jsonify({"error": "not_authenticated"}), 401
    return None


@app.post("/api/login")
def login():
    data = request.get_json(silent=True) or {}
    if secrets.compare_digest(str(data.get("password", "")), PANEL_PASSWORD):
        session["authed"] = True
        session.permanent = True
        return jsonify({"ok": True})
    time.sleep(0.5)  # slow down brute force a little
    return jsonify({"ok": False, "error": "wrong_password"}), 401


@app.post("/api/logout")
def logout():
    session.clear()
    return jsonify({"ok": True})


@app.get("/api/session")
def check_session():
    return jsonify({"authed": bool(session.get("authed"))})


# ------------------------------------------------------------ dashboard ----

@app.get("/api/stats")
def api_stats():
    if (r := require_auth()):
        return r
    stats = db.bot_stats()
    events = db.get_active_events()
    pending = db.get_pending_broadcasts()
    return jsonify({
        "stats": stats,
        "active_events": events,
        "pending_broadcasts": len(pending),
    })


@app.get("/api/leaderboard")
def api_leaderboard():
    if (r := require_auth()):
        return r
    limit = int(request.args.get("limit", 10))
    rows = db.leaderboard(limit=limit)
    return jsonify([{"username": u, "size": s} for u, s in rows])


# ----------------------------------------------------------------- users ---

def _user_payload(user_id):
    user = db.get_user(user_id)
    rank = db.get_user_rank(user_id)
    achievements = db.get_user_achievements_map(user_id)
    current_ban = db.get_current_ban(user_id)
    history = db.get_ban_history(user_id)
    return {
        "user_id": user_id,
        "username": user["username"],
        "size": user["size"],
        "last_grow": user["last_grow"],
        "wins": user["wins"],
        "total_pvp": user["total_pvp"],
        "grows_count": user["grows_count"],
        "golden_count": user["golden_count"],
        "rank": rank,
        "achievements_unlocked": len(achievements),
        "achievements_total": len(config.ACHIEVEMENTS),
        "achievement_ids": list(achievements.keys()),
        "current_ban": current_ban,
        "ban_history": history,
    }


@app.get("/api/users/search")
def api_users_search():
    if (r := require_auth()):
        return r
    query = request.args.get("q", "").strip()
    if not query:
        return jsonify([])
    return jsonify(db.search_users(query, limit=25))


@app.get("/api/users/<user_id>")
def api_user_detail(user_id):
    if (r := require_auth()):
        return r
    resolved = db.resolve_user_id(user_id)
    if not resolved:
        return jsonify({"error": "not_found"}), 404
    return jsonify(_user_payload(resolved))


@app.post("/api/users/<user_id>/addsize")
def api_user_addsize(user_id):
    if (r := require_auth()):
        return r
    resolved = db.resolve_user_id(user_id)
    if not resolved:
        return jsonify({"error": "not_found"}), 404
    try:
        amount = int((request.get_json(silent=True) or {}).get("amount"))
    except (TypeError, ValueError):
        return jsonify({"error": "amount_must_be_int"}), 400
    if not db.update_grow(resolved, amount):
        return jsonify({"error": "not_found"}), 404
    db.log_admin_action("panel", "addsize", resolved, f"+{amount}")
    return jsonify(_user_payload(resolved))


@app.post("/api/users/<user_id>/setsize")
def api_user_setsize(user_id):
    if (r := require_auth()):
        return r
    resolved = db.resolve_user_id(user_id)
    if not resolved:
        return jsonify({"error": "not_found"}), 404
    try:
        amount = int((request.get_json(silent=True) or {}).get("amount"))
    except (TypeError, ValueError):
        return jsonify({"error": "amount_must_be_int"}), 400
    if not db.set_size(resolved, amount):
        return jsonify({"error": "not_found"}), 404
    db.log_admin_action("panel", "setsize", resolved, f"={amount}")
    return jsonify(_user_payload(resolved))


@app.post("/api/users/<user_id>/ban")
def api_user_ban(user_id):
    if (r := require_auth()):
        return r
    resolved = db.resolve_user_id(user_id)
    if not resolved:
        return jsonify({"error": "not_found"}), 404
    reason = (request.get_json(silent=True) or {}).get("reason", "") or ""
    db.ban_user(resolved, reason)
    db.log_admin_action("panel", "ban", resolved, reason or None)
    return jsonify(_user_payload(resolved))


@app.post("/api/users/<user_id>/unban")
def api_user_unban(user_id):
    if (r := require_auth()):
        return r
    resolved = db.resolve_user_id(user_id)
    if not resolved:
        return jsonify({"error": "not_found"}), 404
    db.unban_user(resolved)
    db.log_admin_action("panel", "unban", resolved)
    return jsonify(_user_payload(resolved))


# ---------------------------------------------------------------- promos ---

@app.get("/api/promos")
def api_promos_list():
    if (r := require_auth()):
        return r
    return jsonify(db.list_promos())


@app.post("/api/promos")
def api_promos_create():
    if (r := require_auth()):
        return r
    data = request.get_json(silent=True) or {}
    code = str(data.get("code", "")).strip()
    try:
        amount = int(data.get("amount"))
        days = int(data.get("days"))
    except (TypeError, ValueError):
        return jsonify({"error": "amount_and_days_must_be_int"}), 400
    if not code or amount <= 0 or days <= 0:
        return jsonify({"error": "invalid_input"}), 400
    if db.create_promo(code, amount, days):
        db.log_admin_action("panel", "create_promo", None, f"{code} +{amount} for {days}d")
        return jsonify({"ok": True})
    return jsonify({"error": "code_exists"}), 409


# ---------------------------------------------------------------- events ---

@app.get("/api/event_templates")
def api_event_templates():
    if (r := require_auth()):
        return r
    out = []
    for tmpl in config.EVENT_TEMPLATES:
        tid = tmpl["id"]
        out.append({
            "id": tid,
            "params": tmpl["params"],
        })
    return jsonify(out)


@app.get("/api/events")
def api_events_list():
    if (r := require_auth()):
        return r
    now = int(time.time())
    out = []
    for e in db.list_events():
        if e["start_at"] <= now <= e["end_at"]:
            status = "active"
        elif now < e["start_at"]:
            status = "upcoming"
        else:
            status = "finished"
        out.append({**e, "status": status})
    return jsonify(out)


@app.post("/api/events")
def api_events_create():
    if (r := require_auth()):
        return r
    data = request.get_json(silent=True) or {}
    template_id = data.get("template_id")
    name = (data.get("name") or "").strip()
    start_str = data.get("start")  # "YYYY-MM-DD HH:MM"
    end_str = data.get("end")

    if template_id not in config.EVENT_TEMPLATES_BY_ID:
        return jsonify({"error": "unknown_template"}), 400
    try:
        start_dt = datetime.datetime.strptime(start_str, "%Y-%m-%d %H:%M")
        end_dt = datetime.datetime.strptime(end_str, "%Y-%m-%d %H:%M")
    except (ValueError, TypeError):
        return jsonify({"error": "bad_date_format"}), 400

    start_at, end_at = int(start_dt.timestamp()), int(end_dt.timestamp())
    if end_at <= start_at:
        return jsonify({"error": "end_before_start"}), 400

    if not name:
        name = template_id

    event_id = db.create_event(template_id, name, start_at, end_at, "panel")
    if event_id is None:
        return jsonify({"error": "create_failed"}), 400
    db.log_admin_action("panel", "create_event", None, f"#{event_id} {template_id}")
    return jsonify({"ok": True, "id": event_id})


@app.delete("/api/events/<int:event_id>")
def api_events_delete(event_id):
    if (r := require_auth()):
        return r
    if db.delete_event(event_id):
        db.log_admin_action("panel", "delete_event", None, f"#{event_id}")
        return jsonify({"ok": True})
    return jsonify({"error": "not_found"}), 404


# ------------------------------------------------------------- broadcast ---

@app.post("/api/broadcast")
def api_broadcast():
    if (r := require_auth()):
        return r
    data = request.get_json(silent=True) or {}
    target = data.get("target", "all")
    text = (data.get("text") or "").strip()
    if target not in ("all", "active", "banned"):
        return jsonify({"error": "invalid_target"}), 400
    if not text:
        return jsonify({"error": "empty_text"}), 400
    job_id = db.queue_broadcast(target, text, created_by="panel")
    db.log_admin_action("panel", "broadcast_queued", None, f"#{job_id} target={target}")
    return jsonify({"ok": True, "id": job_id})


@app.get("/api/broadcasts")
def api_broadcasts_list():
    if (r := require_auth()):
        return r
    return jsonify(db.list_broadcasts())


# --------------------------------------------------------------- adminlog --

@app.get("/api/adminlog")
def api_adminlog():
    if (r := require_auth()):
        return r
    logs = db.get_admin_log(limit=50)
    return jsonify([
        {"admin_id": a, "action": act, "target_id": tgt, "details": det, "at": at}
        for a, act, tgt, det, at in logs
    ])


# ------------------------------------------------------------ achievements -

@app.get("/api/achievements")
def api_achievements():
    if (r := require_auth()):
        return r
    return jsonify(config.ACHIEVEMENTS)


# ------------------------------------------------------------- static UI ---

@app.get("/")
def index():
    return send_from_directory(app.static_folder, "index.html")


if __name__ == "__main__":
    port = int(os.environ.get("PANEL_PORT", 8787))
    app.run(host="0.0.0.0", port=port, debug=False, threaded=False)