import hashlib
import hmac
import time
from pathlib import Path
from typing import Optional

import mysql.connector
from fastapi import Cookie, FastAPI, Form, Request, Response
from fastapi.responses import HTMLResponse, RedirectResponse
from fastapi.templating import Jinja2Templates

# ── Admin-Zugangsdaten (hardcoded) ────────────────────────────────────────────
# Benutzernamen und Passwörter hier anpassen.
USERS = {
    "admin":  "wetter2024!",
    "viewer": "schau2024",
}

# ── Session-Konfiguration ─────────────────────────────────────────────────────
SESSION_SECRET  = "bitte-aendern-langer-zufaelliger-string-32c"
SESSION_MAX_AGE = 60 * 60 * 24  # 24 Stunden
COOKIE_NAME     = "admin_session"

# ── Datenbank (App-Backend: request_logs) ─────────────────────────────────────
_env = {}
_env_path = Path("/var/www/app.weidewetter.de/backend/.env")
for line in _env_path.read_text().splitlines():
    if "=" in line and not line.startswith("#"):
        k, _, v = line.partition("=")
        _env[k.strip()] = v.strip()

DB_CONFIG = {
    "host":     _env.get("DB_HOST", "localhost"),
    "port":     int(_env.get("DB_PORT", 3306)),
    "user":     _env.get("DB_USER"),
    "password": _env.get("DB_PASSWORD"),
    "database": _env.get("DB_NAME"),
}

# ── Datenbank (Serve-Backend: ads, events) ────────────────────────────────────
import re as _re
_serve_env_path = Path("/var/www/serve.weidewetter.de/backend/.env")
_serve_url = ""
for line in _serve_env_path.read_text().splitlines():
    if line.startswith("DATABASE_URL="):
        _serve_url = line.partition("=")[2].strip()
        break
# Parse mysql+pymysql://user:pass@host:port/db
_m = _re.match(
    r"mysql\+pymysql://([^:]+):([^@]+)@([^:/]+):?(\d+)?/([^?]+)", _serve_url
)
SERVE_DB_CONFIG = {
    "host":     _m.group(3) if _m else "127.0.0.1",
    "port":     int(_m.group(4)) if (_m and _m.group(4)) else 3306,
    "user":     _m.group(1) if _m else None,
    "password": _m.group(2) if _m else None,
    "database": _m.group(5) if _m else None,
}

REGION_NAMES = {
    "BB": "Brandenburg",        "BE": "Berlin",
    "BW": "Baden-Württemberg",  "BY": "Bayern",
    "HB": "Bremen",             "HE": "Hessen",
    "HH": "Hamburg",            "MV": "Mecklenburg-Vorpommern",
    "NI": "Niedersachsen",      "NW": "Nordrhein-Westfalen",
    "RP": "Rheinland-Pfalz",    "SH": "Schleswig-Holstein",
    "SL": "Saarland",           "SN": "Sachsen",
    "ST": "Sachsen-Anhalt",     "TH": "Thüringen",
    "DE": "Unbekannt",
}

# ── App & Templates ───────────────────────────────────────────────────────────
app = FastAPI(docs_url=None, redoc_url=None, openapi_url=None)
templates = Jinja2Templates(directory=str(Path(__file__).parent / "templates"))


# ── Session-Hilfsfunktionen ───────────────────────────────────────────────────

def _sign(username: str, ts: int) -> str:
    msg = f"{username}:{ts}".encode()
    return hmac.new(SESSION_SECRET.encode(), msg, hashlib.sha256).hexdigest()


def make_token(username: str) -> str:
    ts = int(time.time())
    return f"{username}:{ts}:{_sign(username, ts)}"


def verify_token(token: str) -> Optional[str]:
    try:
        username, ts_str, sig = token.split(":", 2)
        ts = int(ts_str)
        if not hmac.compare_digest(sig, _sign(username, ts)):
            return None
        if time.time() - ts > SESSION_MAX_AGE:
            return None
        return username
    except Exception:
        return None


def get_current_user(request: Request) -> Optional[str]:
    token = request.cookies.get(COOKIE_NAME)
    if not token:
        return None
    return verify_token(token)


# ── Datenbank-Queries ─────────────────────────────────────────────────────────

def get_stats() -> dict:
    conn   = mysql.connector.connect(**DB_CONFIG)
    cursor = conn.cursor(dictionary=True)

    # Zusammenfassung: heute (aktueller Kalendertag)
    cursor.execute("""
        SELECT
            COUNT(*)                          AS total_calls,
            COUNT(DISTINCT user_id_hash)      AS unique_users,
            ROUND(AVG(latency_ms))            AS avg_latency,
            SUM(status_code >= 400)           AS errors
        FROM request_logs
        WHERE DATE(timestamp) = CURDATE()
    """)
    summary = cursor.fetchone()

    # Zusammenfassung: aktueller Monat
    cursor.execute("""
        SELECT
            COUNT(*)                          AS total_calls,
            COUNT(DISTINCT user_id_hash)      AS unique_users,
            ROUND(AVG(latency_ms))            AS avg_latency,
            SUM(status_code >= 400)           AS errors
        FROM request_logs
        WHERE YEAR(timestamp) = YEAR(CURDATE())
          AND MONTH(timestamp) = MONTH(CURDATE())
    """)
    summary_month = cursor.fetchone()

    # Calls + Unique Users pro Tag (letzte 14 Tage)
    cursor.execute("""
        SELECT DATE(timestamp) AS day, COUNT(*) AS calls,
               COUNT(DISTINCT user_id_hash) AS unique_users
        FROM request_logs
        WHERE timestamp >= NOW() - INTERVAL 14 DAY
        GROUP BY day
        ORDER BY day ASC
    """)
    calls_by_day = cursor.fetchall()

    # Calls pro Feature pro Tag (letzte 14 Tage)
    cursor.execute("""
        SELECT DATE(timestamp) AS day, feature, COUNT(*) AS calls
        FROM request_logs
        WHERE timestamp >= NOW() - INTERVAL 14 DAY
        GROUP BY day, feature
        ORDER BY day ASC
    """)
    calls_by_feature = cursor.fetchall()

    # Latenz pro Feature (letzte 7 Tage)
    cursor.execute("""
        SELECT feature,
               COUNT(*)               AS calls,
               ROUND(AVG(latency_ms)) AS avg_ms,
               MAX(latency_ms)        AS max_ms
        FROM request_logs
        WHERE timestamp >= NOW() - INTERVAL 7 DAY
        GROUP BY feature
        ORDER BY calls DESC
    """)
    latency = cursor.fetchall()

    # Fehlerrate pro Tag (letzte 14 Tage)
    cursor.execute("""
        SELECT DATE(timestamp)                                              AS day,
               COUNT(*)                                                     AS total,
               SUM(status_code >= 400)                                      AS errors,
               ROUND(100.0 * SUM(status_code >= 400) / COUNT(*), 1)        AS error_pct
        FROM request_logs
        WHERE timestamp >= NOW() - INTERVAL 14 DAY
        GROUP BY day
        ORDER BY day DESC
    """)
    errors_by_day = cursor.fetchall()

    # Nutzung nach Bundesland (letzte 30 Tage)
    cursor.execute("""
        SELECT region, COUNT(*) AS calls
        FROM request_logs
        WHERE timestamp >= NOW() - INTERVAL 30 DAY
          AND region != 'DE'
        GROUP BY region
        ORDER BY calls DESC
    """)
    by_region = cursor.fetchall()
    for row in by_region:
        row["name"] = REGION_NAMES.get(row["region"], row["region"])

    # Letzte 20 HTTP-Fehler
    cursor.execute("""
        SELECT
            timestamp,
            status_code,
            feature,
            region,
            latency_ms,
            app_version
        FROM request_logs
        WHERE status_code >= 400
        ORDER BY timestamp DESC
        LIMIT 20
    """)
    recent_errors = cursor.fetchall()

    cursor.close()
    conn.close()

    day_labels       = [str(r["day"]) for r in calls_by_day]
    day_values       = [int(r["calls"]) for r in calls_by_day]
    day_users_values = [int(r["unique_users"]) for r in calls_by_day]

    return {
        "summary":              summary,
        "summary_month":        summary_month,
        "calls_by_day":         calls_by_day,
        "calls_by_feature":     calls_by_feature,
        "latency":              latency,
        "errors_by_day":        errors_by_day,
        "by_region":            by_region,
        "chart_labels":         day_labels,
        "chart_values":         day_values,
        "chart_users_values":   day_users_values,
        "recent_errors":        recent_errors,
    }


def get_ads_stats() -> list[dict]:
    conn   = mysql.connector.connect(**SERVE_DB_CONFIG)
    cursor = conn.cursor(dictionary=True)

    cursor.execute("""
        SELECT
            a.uuid,
            a.advertiser_name,
            a.ad_text,
            a.start_date,
            a.end_date,
            a.impressions_target,
            a.impressions_count,
            a.clicks_count,
            a.is_house_ad,
            a.notes,
            COALESCE(
                SUM(CASE WHEN e.event_type = 'impression'
                         AND e.occurred_at >= NOW() - INTERVAL 7 DAY
                    THEN 1 ELSE 0 END), 0
            ) AS impressions_7d,
            COALESCE(
                SUM(CASE WHEN e.event_type = 'click'
                         AND e.occurred_at >= NOW() - INTERVAL 7 DAY
                    THEN 1 ELSE 0 END), 0
            ) AS clicks_7d,
            COALESCE(
                SUM(CASE WHEN e.event_type = 'impression'
                         AND DATE(e.occurred_at) = CURDATE()
                    THEN 1 ELSE 0 END), 0
            ) AS impressions_today,
            COALESCE(
                SUM(CASE WHEN e.event_type = 'click'
                         AND DATE(e.occurred_at) = CURDATE()
                    THEN 1 ELSE 0 END), 0
            ) AS clicks_today
        FROM ads a
        LEFT JOIN events e ON e.ad_uuid = a.uuid
        WHERE a.is_active = 1
          AND a.start_date <= NOW()
          AND a.end_date   >= NOW()
        GROUP BY a.uuid
        ORDER BY a.is_house_ad ASC, a.advertiser_name ASC
    """)
    ads = cursor.fetchall()

    cursor.close()
    conn.close()

    for ad in ads:
        imp  = int(ad["impressions_count"])
        tgt  = int(ad["impressions_target"])
        imp7 = int(ad["impressions_7d"])
        clk7 = int(ad["clicks_7d"])
        ad["impressions_today"] = int(ad["impressions_today"])
        ad["clicks_today"]      = int(ad["clicks_today"])
        ad["impressions_7d"]    = imp7
        ad["clicks_7d"]         = clk7
        ad["budget_pct"] = round(100.0 * imp / tgt, 1) if tgt > 0 else None
        ad["ctr_total"]  = round(100.0 * int(ad["clicks_count"]) / imp, 2) if imp > 0 else 0.0
        ad["ctr_7d"]     = round(100.0 * clk7 / imp7, 2) if imp7 > 0 else 0.0

    return ads


# ── Routen ────────────────────────────────────────────────────────────────────

@app.get("/login", response_class=HTMLResponse)
async def login_page(request: Request, error: str = ""):
    if get_current_user(request):
        return RedirectResponse("/", status_code=302)
    return templates.TemplateResponse("login.html", {"request": request, "error": error})


@app.post("/login")
async def login(
    response: Response,
    request:  Request,
    username: str = Form(...),
    password: str = Form(...),
):
    expected = USERS.get(username)
    if expected and hmac.compare_digest(expected, password):
        token = make_token(username)
        resp  = RedirectResponse("/", status_code=302)
        resp.set_cookie(
            key      = COOKIE_NAME,
            value    = token,
            max_age  = SESSION_MAX_AGE,
            httponly = True,
            samesite = "lax",
        )
        return resp
    return RedirectResponse("/login?error=1", status_code=302)


@app.get("/logout")
async def logout():
    resp = RedirectResponse("/login", status_code=302)
    resp.delete_cookie(COOKIE_NAME)
    return resp


@app.get("/", response_class=HTMLResponse)
async def dashboard(request: Request):
    user = get_current_user(request)
    if not user:
        return RedirectResponse("/login", status_code=302)

    _stats_defaults = {
        "summary":            None,
        "summary_month":      None,
        "calls_by_day":       [],
        "calls_by_feature":   [],
        "latency":            [],
        "errors_by_day":      [],
        "by_region":          [],
        "chart_labels":       [],
        "chart_values":       [],
        "chart_users_values": [],
        "recent_errors":      [],
    }
    try:
        stats = get_stats()
        db_error = None
    except Exception as e:
        stats    = _stats_defaults
        db_error = str(e)

    try:
        active_ads = get_ads_stats()
    except Exception:
        active_ads = []

    return templates.TemplateResponse("dashboard.html", {
        "request":    request,
        "username":   user,
        "db_error":   db_error,
        "active_ads": active_ads,
        **stats,
    })
