from flask import Flask, render_template, abort, request, redirect, url_for, Response
from datetime import date
from app.db.connection import get_connection
from functools import wraps
from app.config.secrets import ADMIN_USER, ADMIN_PASSWORD
from app.ai.triage_prompts import TRIAGE_VERSION

app = Flask(
    __name__,
    template_folder="../templates",
    static_folder="../static"
)

def check_auth(username: str, password: str) -> bool:
    return username == ADMIN_USER and password == ADMIN_PASSWORD


def authenticate():
    return Response(
        "Accès refusé.",
        401,
        {"WWW-Authenticate": 'Basic realm="Happy Headlines Admin"'}
    )


def requires_auth(f):
    @wraps(f)
    def decorated(*args, **kwargs):
        auth = request.authorization
        if not auth or not check_auth(auth.username, auth.password):
            return authenticate()
        return f(*args, **kwargs)
    return decorated

MIN_CONTENT_CHARS = 400


# ---------- DB helpers ----------

def fetch_candidates(limit: int = 50):
    """
    Candidats fallback pour la home publique : non publiés.
    """
    conn = get_connection()
    cur = conn.cursor(dictionary=True)

    cur.execute("""
        SELECT
          r.id AS raw_id,
          r.title,
          r.url,
          r.published_at,
          r.content,
          s.summary,
          s.wink_title,
          s.topic,
          s.source_lang,
          s.good_news_score
        FROM articles_scored s
        JOIN articles_raw r ON r.id = s.raw_id
        WHERE NOT EXISTS (
          SELECT 1 FROM articles_published p WHERE p.raw_id = r.id
        )
        ORDER BY s.good_news_score DESC, r.published_at DESC
        LIMIT %s
    """, (limit,))

    rows = cur.fetchall()
    cur.close()
    conn.close()
    return rows


def fetch_published_for(day: date):
    conn = get_connection()
    cur = conn.cursor(dictionary=True)

    cur.execute("""
        SELECT
          p.position,
          r.id AS raw_id,
          r.title,
          r.url,
          r.published_at,
          r.content,
          s.wink_title,
          s.topic,
          s.good_news_score,
          s.source_lang,
          s.summary
        FROM articles_published p
        JOIN articles_raw r ON r.id = p.raw_id
        LEFT JOIN articles_scored s ON s.raw_id = r.id
        WHERE p.published_date = %s
        ORDER BY p.position ASC
    """, (day,))

    rows = cur.fetchall()
    cur.close()
    conn.close()
    return rows

def fetch_latest_published_date():
    conn = get_connection()
    cur = conn.cursor()

    cur.execute("""
        SELECT MAX(published_date)
        FROM articles_published
        WHERE published_date <= CURDATE()
    """)

    row = cur.fetchone()
    cur.close()
    conn.close()

    return row[0] if row and row[0] else None

def save_selection_for(day: date, selections: list):
    conn = get_connection()
    cur = conn.cursor()

    cur.execute("DELETE FROM articles_published WHERE published_date = %s", (day,))
    cur.executemany("""
        INSERT INTO articles_published (raw_id, published_date, position)
        VALUES (%s, %s, %s)
    """, [(s["raw_id"], day, s["position"]) for s in selections])

    conn.commit()
    cur.close()
    conn.close()


def fetch_article_by_raw_id(raw_id: int):
    conn = get_connection()
    cur = conn.cursor(dictionary=True)

    cur.execute("""
        SELECT
          r.id AS raw_id,
          r.title,
          r.url,
          r.published_at,
          r.excerpt,
          r.content,
          s.wink_title,
          s.topic,
          s.good_news_score,
          s.summary
        FROM articles_raw r
        LEFT JOIN articles_scored s ON s.raw_id = r.id
        WHERE r.id = %s
        LIMIT 1
    """, (raw_id,))

    row = cur.fetchone()
    cur.close()
    conn.close()
    return row


def fetch_latest_admin_date():
    conn = get_connection()
    cur = conn.cursor()

    cur.execute("""
        SELECT DATE(r.published_at) AS d
        FROM articles_scored s
        JOIN articles_raw r ON r.id = s.raw_id
        ORDER BY d DESC
        LIMIT 1
    """)
    row = cur.fetchone()

    cur.close()
    conn.close()

    return row[0] if row and row[0] else date.today()


def fetch_admin_candidates(selected_day: date, limit: int = 50, sort_by: str = "score"):
    """
    Admin : on affiche aussi les articles déjà publiés, mais on les badge/désactive.
    """
    sort_map = {
        "score": "s.good_news_score DESC, r.published_at DESC",
        "topic": "s.topic ASC, s.good_news_score DESC, r.published_at DESC",
        "date": "r.published_at DESC",
    }
    order_sql = sort_map.get(sort_by, sort_map["score"])

    conn = get_connection()
    cur = conn.cursor(dictionary=True)

    query = f"""
        SELECT
          r.id AS raw_id,
          r.title,
          r.url,
          r.published_at,
          r.content,
          s.summary,
          s.wink_title,
          s.topic,
          s.source_lang,
          s.good_news_score,
          CASE
            WHEN CHAR_LENGTH(TRIM(COALESCE(r.content, ''))) >= %s THEN 1
            ELSE 0
          END AS has_scrap,
          CASE
            WHEN p_last.raw_id IS NOT NULL THEN 1
            ELSE 0
          END AS already_published,
          p_last.published_date AS last_published_date
        FROM articles_scored s
        JOIN articles_raw r ON r.id = s.raw_id
        LEFT JOIN (
            SELECT raw_id, MAX(published_date) AS published_date
            FROM articles_published
            GROUP BY raw_id
        ) p_last ON p_last.raw_id = r.id
        WHERE DATE(r.published_at) >= %s
        ORDER BY {order_sql}
        LIMIT %s
    """

    cur.execute(query, (MIN_CONTENT_CHARS, selected_day, limit))
    rows = cur.fetchall()

    for row in rows:
        published_at = row.get("published_at")
        if published_at:
            row["published_short"] = published_at.strftime("%d/%m · %H:%M")
        else:
            row["published_short"] = ""    

    cur.close()
    conn.close()
    return rows



def fetch_admin_draft_candidates(selected_day: date, limit: int = 50):
    conn = get_connection()
    cur = conn.cursor(dictionary=True)

    query = """
        SELECT
          r.id AS raw_id,
          r.title,
          r.url,
          r.published_at,
          r.excerpt,
          r.content,
          tr.triage_score,
          tr.triage_label,
          tr.triage_reason,
          tr.source_lang,
          CASE
            WHEN CHAR_LENGTH(TRIM(COALESCE(r.content, ''))) >= %s THEN 1
            ELSE 0
          END AS has_scrap
        FROM articles_triage tr
        JOIN articles_raw r ON r.id = tr.raw_id
        WHERE DATE(r.published_at) >= %s
          AND tr.triage_label IN ('hot', 'maybe')
          AND tr.triage_version = %s
        ORDER BY tr.triage_score DESC, r.published_at DESC
        LIMIT %s
    """

    cur.execute(query, (MIN_CONTENT_CHARS, selected_day, TRIAGE_VERSION, limit))
    rows = cur.fetchall()

    for row in rows:
        published_at = row.get("published_at")
        if published_at:
            row["published_short"] = published_at.strftime("%d/%m · %H:%M")
        else:
            row["published_short"] = ""

    cur.close()
    conn.close()
    return rows

# ---------- Routes ----------

@app.route("/mentions-legales")
def mentions_legales():
    return render_template("mentions_legales.html")

@app.get("/")
def home():
    today = date.today()

    published = fetch_published_for(today)

    if not published:
        latest_day = fetch_latest_published_date()
        if latest_day:
            published = fetch_published_for(latest_day)
            display_day = latest_day
        else:
            published = []
            display_day = today
    else:
        display_day = today

    featured = published[0] if published else None
    others = published[1:] if len(published) > 1 else []

    return render_template(
        "index.html",
        featured=featured,
        others=others,
        today=str(display_day),
        mode="published" if published else "empty"
    )


@app.get("/article/<int:raw_id>")
def article(raw_id: int):
    a = fetch_article_by_raw_id(raw_id)
    if not a:
        abort(404)
    return render_template("article.html", a=a)


@app.get("/admin")
@requires_auth
def admin():
    selected_day_str = request.args.get("day")
    limit_str = request.args.get("limit", "50")
    sort_by = request.args.get("sort", "score")

    default_day = fetch_latest_admin_date()

    try:
        selected_day = date.fromisoformat(selected_day_str) if selected_day_str else default_day
    except ValueError:
        selected_day = default_day

    try:
        limit = int(limit_str)
    except ValueError:
        limit = 50

    if limit not in (20, 50, 100, 200):
        limit = 50

    candidates = fetch_admin_candidates(selected_day, limit, sort_by)

    return render_template(
        "admin.html",
        candidates=candidates,
        selected_day=selected_day.isoformat(),
        publication_day=date.today().isoformat(),
        limit=limit,
        sort_by=sort_by
    )

@app.get("/admin_draft")
@requires_auth
def admin_draft():
    selected_day_str = request.args.get("day")
    limit_str = request.args.get("limit", "50")

    default_day = fetch_latest_admin_date()

    try:
        selected_day = date.fromisoformat(selected_day_str) if selected_day_str else default_day
    except ValueError:
        selected_day = default_day

    try:
        limit = int(limit_str)
    except ValueError:
        limit = 50

    if limit not in (20, 50, 100, 200):
        limit = 50

    candidates = fetch_admin_draft_candidates(selected_day, limit)

    return render_template(
        "admin_draft.html",
        candidates=candidates,
        selected_day=selected_day.isoformat(),
        limit=limit
    )


@app.post("/admin/publish")
@requires_auth
def admin_publish():
    publication_day_str = request.form.get("publication_day", "").strip()
    if not publication_day_str:
        return "Erreur : date de publication manquante.", 400

    try:
        publication_day = date.fromisoformat(publication_day_str)
    except ValueError:
        return "Erreur : date de publication invalide.", 400

    selections = []
    positions = set()

    for key, value in request.form.items():
        if not key.startswith("pos_"):
            continue

        pos_str = (value or "").strip()
        if not pos_str:
            continue

        raw_id = int(key.replace("pos_", ""))
        pos = int(pos_str)

        if pos < 1 or pos > 5:
            return f"Erreur : position invalide pour raw_id={raw_id}", 400
        if pos in positions:
            return f"Erreur : la position {pos} est utilisée plusieurs fois.", 400

        positions.add(pos)
        selections.append({"raw_id": raw_id, "position": pos})

    if len(selections) != 5:
        return "Erreur : tu dois attribuer exactement les positions 1 à 5.", 400

    save_selection_for(publication_day, selections)
    return redirect(url_for("home"))

@app.route("/admin_draft/generate", methods=["POST"])
@requires_auth
def admin_draft_generate():
    raw_ids = request.form.getlist("raw_ids")
    selected_day = request.form.get("selected_day", "").strip()
    limit_str = request.form.get("limit", "50").strip()

    if not raw_ids:
        return "Erreur : aucun article sélectionné.", 400

    try:
        limit = int(limit_str)
    except ValueError:
        limit = 50

    if limit not in (20, 50, 100, 200):
        limit = 50

    from app.workflow.analyze_v2 import analyze_selected
    result = analyze_selected(raw_ids)

    if not selected_day:
        selected_day = date.today().isoformat()

    print(
        f"📌 Génération demandée depuis admin_draft : "
        f"requested={result['requested']} inserted={result['inserted']} "
        f"failed={result['failed']} skipped={result['skipped']}"
    )

    return redirect(
        url_for(
            "admin",
            day=selected_day,
            limit=limit,
            sort="score"
        )
    )


if __name__ == "__main__":
    app.run(debug=True, host="127.0.0.1", port=5000)
