#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
L'interface web. Volontairement mince : le travail est fait par le worker.

Pas de comptes : un mot de passe unique, posé dans .env. L'application ne
lance jamais de génération elle-même — elle met un projet dans la file, et
c'est le cron qui s'en occupe. Aucune requête HTTP ne dure donc plus d'une
seconde.
"""

import os
import re
import unicodedata

from flask import (Flask, abort, flash, redirect, render_template, request,
                   send_file, send_from_directory, session, url_for)

import claude
import config
import livraison
import projets

app = Flask(__name__)
app.secret_key = os.environ.get("PLATEFORME_SECRET", os.urandom(32).hex())
app.config["MAX_CONTENT_LENGTH"] = 64 * 1024 * 1024  # dépôt total par envoi

MOT_DE_PASSE = os.environ.get("PLATEFORME_MOTDEPASSE", "")

ETIQUETTES = {
    projets.BROUILLON: "brouillon",
    projets.EN_ATTENTE: "en file",
    projets.EN_COURS: "en cours",
    projets.TERMINE: "terminé",
    projets.ECHEC: "échec",
    projets.INTERROMPU: "interrompu",
}

# Les champs du formulaire, dans l'ordre du gabarit.
FORMULAIRE = [
    ("client", "Structure / client", "input"),
    ("secteur", "Secteur d'activité", "input"),
    ("nature", "Nature du site", "input"),
    ("objectif", "Objectif principal", "input"),
    ("public", "Public visé", "input"),
    ("slogan", "Slogan", "input"),
    ("domaine", "Nom de domaine", "input"),
    ("coordonnees", "Coordonnées à afficher", "texte"),
    ("reseaux", "Réseaux sociaux", "input"),
    ("date_lieu", "Date / lieu de l'événement", "input"),
    ("couleurs", "Couleurs (indicatif, le PDF fait foi)", "input"),
    ("police_titres", "Police des titres", "input"),
    ("police_textes", "Police des textes", "input"),
    ("variantes_logo", "Variantes de logo", "input"),
    ("pages", "Pages à produire maintenant", "input"),
    ("arborescence", "Arborescence complète du site", "texte"),
    ("contenus", "Contenus, bloc par bloc", "grand"),
    ("references", "Sites de référence", "texte"),
    ("remarques", "Remarques du client", "texte"),
    ("brief_libre", "Brief complémentaire en texte libre", "grand"),
]

_SUR = re.compile(r"[^A-Za-z0-9._-]+")


def nom_sur(nom):
    """Ramène un nom de fichier déposé à quelque chose d'inoffensif."""
    nom = unicodedata.normalize("NFKD", nom or "")
    nom = nom.encode("ascii", "ignore").decode("ascii")
    nom = _SUR.sub("-", os.path.basename(nom)).strip("-.")
    return nom[:80] or "fichier"


def connecte():
    return not MOT_DE_PASSE or session.get("ouvert") is True


@app.before_request
def garde():
    if request.endpoint in ("connexion", "static") or connecte():
        return None
    return redirect(url_for("connexion", suite=request.path))


@app.route("/connexion", methods=["GET", "POST"])
def connexion():
    if request.method == "POST":
        if MOT_DE_PASSE and request.form.get("mot_de_passe") == MOT_DE_PASSE:
            session["ouvert"] = True
            session.permanent = True
            return redirect(request.args.get("suite") or url_for("accueil"))
        flash("Mot de passe incorrect.")
    return render_template("connexion.html")


@app.route("/deconnexion")
def deconnexion():
    session.clear()
    return redirect(url_for("connexion"))


@app.route("/")
def accueil():
    liste = projets.lister()
    for p in liste:
        p["etiquette"] = ETIQUETTES.get(p["etat"], p["etat"])
        p["cout"] = projets.cout_dollars(p)
    return render_template("accueil.html", projets=liste)


@app.route("/nouveau", methods=["GET", "POST"])
def nouveau():
    if request.method == "GET":
        return render_template("nouveau.html", champs=FORMULAIRE)

    brief = {cle: (request.form.get(cle) or "").strip() for cle, _, _ in FORMULAIRE}
    if not brief.get("client"):
        flash("Le nom du client est nécessaire.")
        return render_template("nouveau.html", champs=FORMULAIRE, valeurs=brief)

    projet_id = projets.creer(brief["client"])
    entrees = projets.dossier_entrees(projet_id)
    deposes = 0
    for fichier in request.files.getlist("fichiers"):
        if not fichier or not fichier.filename:
            continue
        fichier.save(os.path.join(entrees, nom_sur(fichier.filename)))
        deposes += 1
    projets.journaliser(projet_id, "depot", "%d fichier(s)" % deposes)

    import prompt  # importé ici : il lit les gabarits au premier usage
    prompt.preparer(projet_id, brief)
    return redirect(url_for("projet", projet_id=projet_id))


@app.route("/projet/<projet_id>")
def projet(projet_id):
    p = projets.lire(projet_id)
    if not p:
        abort(404)
    p["etiquette"] = ETIQUETTES.get(p["etat"], p["etat"])
    p["cout"] = projets.cout_dollars(p)
    site = projets.dossier_site(projet_id)
    accueil_site = next(
        (n for n in ("index.html", "index.php", "accueil.html")
         if os.path.isfile(os.path.join(site, n))), None)
    return render_template(
        "projet.html",
        p=p,
        journal=projets.journal(projet_id),
        anomalies=livraison.verifier(projet_id) if p["etat"] == projets.TERMINE else [],
        archive=os.path.isfile(projets.chemin_archive(projet_id)),
        accueil_site=accueil_site,
        actif=p["etat"] in projets.ETATS_ACTIFS,
    )


@app.route("/projet/<projet_id>/journal.json")
def journal_json(projet_id):
    """Sondé par la page de suivi : évite de tout recharger."""
    p = projets.lire(projet_id)
    if not p:
        abort(404)
    depuis = request.args.get("depuis", 0, type=int)
    return {
        "etat": p["etat"],
        "etiquette": ETIQUETTES.get(p["etat"], p["etat"]),
        "tours": p["tours"],
        "cout": projets.cout_dollars(p),
        "actif": p["etat"] in projets.ETATS_ACTIFS,
        "lignes": [
            {"id": j["id"], "evenement": j["evenement"], "detail": j["detail"] or ""}
            for j in projets.journal(projet_id, depuis=depuis)
        ],
    }


@app.route("/projet/<projet_id>/consigne", methods=["POST"])
def consigne(projet_id):
    if not projets.lire(projet_id):
        abort(404)
    texte = (request.form.get("consigne") or "").strip()
    if not texte:
        flash("Consigne vide.")
    else:
        claude.relancer(projet_id, texte)
        flash("Consigne transmise, le worker la prend dans la minute.")
    return redirect(url_for("projet", projet_id=projet_id))


@app.route("/projet/<projet_id>/interrompre", methods=["POST"])
def interrompre(projet_id):
    if not projets.lire(projet_id):
        abort(404)
    projets.changer_etat(projet_id, projets.INTERROMPU)
    flash("Génération interrompue.")
    return redirect(url_for("projet", projet_id=projet_id))


@app.route("/projet/<projet_id>/zip")
def telecharger(projet_id):
    chemin = projets.chemin_archive(projet_id)
    if not os.path.isfile(chemin):
        if not os.path.isdir(projets.dossier_site(projet_id)):
            abort(404)
        livraison.archiver(projet_id)
    return send_file(chemin, as_attachment=True, download_name="%s.zip" % projet_id)


@app.route("/apercu/<projet_id>/")
@app.route("/apercu/<projet_id>/<path:chemin>")
def apercu(projet_id, chemin="index.html"):
    """Aperçu statique. Le PHP n'est pas exécuté ici : pour l'essayer,
    faire pointer un sous-domaine sur projets/<id>/site/ (voir INSTALLATION)."""
    if not projets.lire(projet_id):
        abort(404)
    site = projets.dossier_site(projet_id)
    if os.path.isdir(os.path.join(site, chemin)):
        chemin = os.path.join(chemin, "index.html")
    return send_from_directory(site, chemin)


@app.template_filter("horodatage")
def horodatage(valeur):
    import time
    return time.strftime("%d/%m %H:%M", time.localtime(valeur or 0))


projets.initialiser()

if __name__ == "__main__":
    app.run(host="127.0.0.1", port=int(os.environ.get("PORT", "5000")), debug=True)
