# -*- coding: utf-8 -*-
"""
Aplicatia web Flask: serveste DOUA chestionare (vezi forms.py), salveaza
raspunsurile in tabele separate si ofera pagini /admin per chestionar.

Public:  /            -> chestionarul implicit (b12)
         /carburanti  -> al doilea chestionar
Admin:   /admin       -> raspunsuri b12   |  /admin/carburanti -> raspunsuri 6
API:     /api/<form>/raspunsuri.csv?key=...

Rulare locala:   python app.py   ->  http://localhost:5000
Rulare productie: gunicorn app:app  (sau Passenger via passenger_wsgi.py)
"""
import os
import re
import io

from flask import (
    Flask, render_template, request, redirect, url_for,
    abort, Response, session,
)

import db
import mailer
import forms

app = Flask(__name__)
app.secret_key = os.environ.get("SECRET_KEY", "schimba-aceasta-cheie-in-productie")
ADMIN_PASSWORD = os.environ.get("ADMIN_PASSWORD", "admin")
API_KEY = os.environ.get("API_KEY", "")

db.init_db()

EMAIL_RE = re.compile(r"^[^@\s]+@[^@\s]+\.[^@\s]+$")
ALTELE_FLAG = "__ALTELE__"


def _form(form_id):
    f = forms.get(form_id)
    if not f:
        abort(404)
    return f


# ------------------------------------------------------------------ navigare
def page_router(page):
    for q in page["questions"]:
        if q.get("is_router"):
            return q
    return None


def visited_pages(form, answers):
    pages = form["questions"].PAGES
    visited = []
    p = 0
    guard = 0
    while 0 <= p < len(pages) and guard < 200:
        guard += 1
        visited.append(p)
        nxt = "next"
        router = page_router(pages[p])
        if router:
            opt = next((o for o in router["options"] if o["text"] == answers.get(router["slug"])), None)
            if opt:
                nxt = opt["goto"]
        if nxt == "submit":
            break
        p = p + 1 if nxt == "next" else nxt
    return set(visited)


# ------------------------------------------------------------------ raspunsuri
def collect_answers(form, formdata):
    answers = {}
    for q in db.iter_questions(form):
        slug = q["slug"]
        if q["type"] == "grid":
            for row in q["rows"]:
                answers[row["slug"]] = formdata.get(row["slug"], "").strip()
        elif q["type"] == "checkbox":
            raw = formdata.getlist(slug)
            vals = [v.strip() for v in raw if v.strip() and v != ALTELE_FLAG]
            if q.get("other"):
                otext = formdata.get(q["other_slug"], "").strip()
                if otext:
                    vals.append(otext)
            answers[slug] = ", ".join(vals)
        else:
            answers[slug] = formdata.get(slug, "").strip()
    return answers


def validate(form, answers, visited):
    errors = []
    for idx, page in enumerate(form["questions"].PAGES):
        if idx not in visited:
            continue
        for q in page["questions"]:
            slug = q["slug"]
            if q["type"] == "grid":
                for row in q["rows"]:
                    if row.get("required") and not answers.get(row["slug"]):
                        errors.append("%s - %s" % (q["label"], row["label"]))
            elif q["type"] == "checkbox":
                if q.get("required") and not answers.get(slug):
                    errors.append(q["label"])
            else:
                val = answers.get(slug, "")
                if q.get("required") and not val:
                    errors.append(q["label"])
                    continue
                rule = q.get("validation")
                if val and rule:
                    if rule["kind"] == "email" and not EMAIL_RE.match(val):
                        errors.append("%s: adresă de email invalidă" % q["label"])
                    elif rule["kind"] == "number" and not val.isdigit():
                        errors.append("%s: %s" % (q["label"], rule.get("error", "trebuie să fie un număr")))
    return errors


def _render_form(form, errors=None, values=None):
    qm = form["questions"]
    return render_template(
        "form.html", title=qm.FORM_TITLE, description=qm.FORM_DESCRIPTION,
        pages=qm.PAGES, errors=errors, values=values or {},
        form=form, other=forms.other(form["id"]),
    )


# ------------------------------------------------------------- routes publice
def _make_form_view(form_id):
    def view():
        return _render_form(_form(form_id))
    return view


for _f in forms.all_forms():
    app.add_url_rule(_f["path"], "form_" + _f["id"], _make_form_view(_f["id"]))


@app.route("/submit", methods=["POST"])
def submit():
    form = forms.get(request.form.get("form_id")) or forms.get(forms.DEFAULT)
    answers = collect_answers(form, request.form)
    visited = visited_pages(form, answers)

    # goleste raspunsurile paginilor nevizitate (sarite de ramificare)
    for idx, page in enumerate(form["questions"].PAGES):
        if idx in visited:
            continue
        for q in page["questions"]:
            if q["type"] == "grid":
                for row in q["rows"]:
                    answers[row["slug"]] = ""
            else:
                answers[q["slug"]] = ""

    errors = validate(form, answers, visited)
    if errors:
        return _render_form(form, errors=errors, values=answers), 400

    db.insert_submission(form, answers)
    mailer.send_new_submission(form, answers)
    return redirect(url_for("multumim"))


@app.route("/multumim")
def multumim():
    return render_template("multumim.html", title="Chestionar trimis",
                           home_url=url_for("form_" + forms.DEFAULT))


# --------------------------------------------------------------------- admin
def column_suggestions(form):
    sug = {}
    for q in db.iter_questions(form):
        if q["type"] in ("radio", "dropdown"):
            sug[q["slug"]] = [o["text"] for o in q["options"]]
        elif q["type"] == "grid":
            for row in q["rows"]:
                sug[row["slug"]] = q["options"]
        elif q["type"] == "checkbox":
            sug[q["slug"]] = q["options"]
    return sug


def _admin_view(form_id):
    form = _form(form_id)
    PER_PAGE = 100
    q = request.args.get("q", "").strip()
    col = request.args.get("col", "").strip()
    try:
        page = max(1, int(request.args.get("page", 1)))
    except ValueError:
        page = 1
    total = db.count_filtered(form, q, col)
    total_pages = max(1, (total + PER_PAGE - 1) // PER_PAGE)
    page = min(page, total_pages)
    rows = db.fetch_page(form, q, col, page, PER_PAGE)
    other = forms.other(form["id"])
    return render_template(
        "admin.html", logged_in=True, rows=rows, cols=db.columns(form), total=total,
        suggestions=column_suggestions(form), q=q, col=col, page=page,
        total_pages=total_pages, per_page=PER_PAGE,
        import_msg=session.pop("import_msg", None),
        form=form, other=other, admin_base=_admin_url(form["id"]),
        other_admin_url=_admin_url(other["id"]),
    )


def _admin_url(form_id):
    return url_for("admin") if form_id == forms.DEFAULT else url_for("admin_form", form_id=form_id)


@app.route("/admin", methods=["GET", "POST"])
def admin():
    if request.method == "POST" and request.form.get("parola") is not None:
        if request.form.get("parola") == ADMIN_PASSWORD:
            session["admin"] = True
        else:
            return render_template("admin.html", logged_in=False, error="Parola gresita.")
    if not session.get("admin"):
        return render_template("admin.html", logged_in=False)
    return _admin_view(forms.DEFAULT)


@app.route("/admin/<form_id>")
def admin_form(form_id):
    _form(form_id)
    if not session.get("admin"):
        return render_template("admin.html", logged_in=False)
    return _admin_view(form_id)


@app.route("/admin/logout")
def admin_logout():
    session.pop("admin", None)
    return redirect(url_for("admin"))


# ---- API admin (editare ca Excel), per chestionar -------------------------
def _require_admin():
    if not session.get("admin"):
        abort(403)


@app.route("/admin/<form_id>/row/new", methods=["POST"])
def admin_row_new(form_id):
    _require_admin()
    form = _form(form_id)
    new_id = db.insert_blank(form)
    return {"ok": True, "row": db.get_submission(form, new_id)}


@app.route("/admin/<form_id>/row/<int:sub_id>/update", methods=["POST"])
def admin_row_update(form_id, sub_id):
    _require_admin()
    form = _form(form_id)
    values = request.get_json(silent=True) or request.form.to_dict()
    n = db.update_submission(form, sub_id, values)
    return {"ok": True, "updated": n}


@app.route("/admin/<form_id>/row/<int:sub_id>/duplicate", methods=["POST"])
def admin_row_duplicate(form_id, sub_id):
    _require_admin()
    form = _form(form_id)
    new_id = db.duplicate_submission(form, sub_id)
    if not new_id:
        abort(404)
    return {"ok": True, "row": db.get_submission(form, new_id)}


@app.route("/admin/<form_id>/row/<int:sub_id>/delete", methods=["POST"])
def admin_row_delete(form_id, sub_id):
    _require_admin()
    db.delete_submission(_form(form_id), sub_id)
    return {"ok": True}


@app.route("/admin/<form_id>/row/<int:sub_id>/toggle-struck", methods=["POST"])
def admin_row_toggle_struck(form_id, sub_id):
    _require_admin()
    state = db.toggle_struck(_form(form_id), sub_id)
    if state is None:
        abort(404)
    return {"ok": True, "struck": state}


@app.route("/admin/<form_id>/import", methods=["POST"])
def admin_import(form_id):
    _require_admin()
    form = _form(form_id)
    f = request.files.get("file")
    if not f or not f.filename:
        return redirect(url_for("admin_form", form_id=form_id))
    label_to_key = {label: key for key, label in db.columns(form)}
    try:
        rows = _parse_import(f, label_to_key)
        n = db.import_rows(form, rows)
        session["import_msg"] = "Importate %d răspunsuri." % n
    except Exception as exc:
        session["import_msg"] = "Eroare la import: %s" % exc
    return redirect(url_for("admin_form", form_id=form_id))


def _parse_import(file_storage, label_to_key):
    """Parseaza CSV sau XLSX -> lista de dict {slug: valoare}, dupa etichetele coloanelor."""
    name = (file_storage.filename or "").lower()
    raw = file_storage.read()
    if name.endswith(".xlsx"):
        from openpyxl import load_workbook
        wb = load_workbook(io.BytesIO(raw), read_only=True, data_only=True)
        ws = wb.active
        it = ws.iter_rows(values_only=True)
        header = [str(h) if h is not None else "" for h in next(it, [])]
        data_rows = [[("" if v is None else str(v)) for v in row] for row in it]
    else:
        import csv as _csv
        text = raw.decode("utf-8-sig", errors="replace")
        try:
            dialect = _csv.Sniffer().sniff(text[:4096], delimiters=",;\t")
        except _csv.Error:
            dialect = _csv.excel
        reader = list(_csv.reader(io.StringIO(text), dialect))
        if not reader:
            return []
        header, data_rows = reader[0], reader[1:]

    keys = [label_to_key.get((h or "").strip()) for h in header]
    out = []
    for row in data_rows:
        if not any((c or "").strip() for c in row):
            continue
        rec = {k: (val or "").strip() for k, val in zip(keys, row) if k}
        out.append(rec)
    return out


@app.route("/admin/<form_id>/export.<fmt>")
def admin_export(form_id, fmt):
    if not session.get("admin"):
        abort(403)
    form = _form(form_id)
    import export
    if fmt == "csv":
        return Response(
            export.to_csv_bytes(form), content_type="text/csv; charset=utf-8",
            headers={"Content-Disposition": "attachment; filename=raspunsuri_%s.csv" % form_id},
        )
    if fmt == "xlsx":
        return Response(
            export.to_xlsx_bytes(form),
            mimetype="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
            headers={"Content-Disposition": "attachment; filename=raspunsuri_%s.xlsx" % form_id},
        )
    abort(404)


# ---- API extern per chestionar (ex. pd.read_csv) --------------------------
@app.route("/api/<form_id>/raspunsuri.csv")
def api_csv(form_id):
    """pd.read_csv("https://site.ro/api/<form>/raspunsuri.csv?key=CHEIE")."""
    key = request.args.get("key") or request.headers.get("X-API-Key", "")
    if not API_KEY or key != API_KEY:
        abort(403)
    form = _form(form_id)
    import export
    # BOM UTF-8: diacriticele apar corect si in Excel/vizualizatoare; pandas il ignora automat.
    return Response(export.to_csv_bytes(form, delimiter=",", bom=True),
                    content_type="text/csv; charset=utf-8")


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