"""Gestão de bloqueios judiciais (RPZ)."""
from __future__ import annotations

import csv
import io
import sqlite3
from datetime import datetime

from flask import (
    Blueprint,
    Response,
    current_app,
    flash,
    redirect,
    render_template,
    request,
    url_for,
)

from .. import audit, rpz
from ..auth import login_required
from ..db import get_db

bp = Blueprint("blocks", __name__, url_prefix="/bloqueios")

PER_PAGE = 50


def _sync_and_flash(context: str) -> None:
    try:
        rpz.sync_zone()
        flash("Zona RPZ regravada e recarregada no BIND9.", "success")
    except rpz.RPZError as exc:
        flash(f"Alteração salva no banco, mas a zona não recarregou: {exc}", "error")
        audit.log("rpz_erro", f"{context}: {exc}")
    except Exception as exc:  # noqa: BLE001 - nunca derrubar a tela por causa da zona
        current_app.logger.exception("Falha inesperada ao sincronizar a zona: %s", exc)
        flash(f"Alteração salva no banco, mas a zona não foi regravada: {exc}", "error")


@bp.get("/")
@login_required
def index():
    q = (request.args.get("q") or "").strip()
    process = (request.args.get("process") or "").strip()
    try:
        page = max(int(request.args.get("page", 1) or 1), 1)
    except ValueError:
        page = 1

    where, params = ["active = 1"], []
    if q:
        where.append("domain LIKE ?")
        params.append(f"%{q.lower()}%")
    if process:
        where.append("process_no LIKE ?")
        params.append(f"%{process}%")
    clause = " AND ".join(where)

    rows, total = [], 0
    try:
        db = get_db()
        total = db.execute(
            f"SELECT COUNT(*) AS c FROM blocked_domains WHERE {clause}", params
        ).fetchone()["c"]
        rows = db.execute(
            f"SELECT * FROM blocked_domains WHERE {clause} ORDER BY id DESC LIMIT ? OFFSET ?",
            [*params, PER_PAGE, (page - 1) * PER_PAGE],
        ).fetchall()
    except sqlite3.Error as exc:
        current_app.logger.exception("Falha ao listar bloqueios: %s", exc)
        flash(
            "Não foi possível ler a lista de bloqueios no banco de dados. "
            f"Detalhe técnico: {exc}",
            "error",
        )

    pages = max((total + PER_PAGE - 1) // PER_PAGE, 1)
    return render_template(
        "blocks.html",
        rows=[dict(r) for r in rows],
        total=total,
        page=page,
        pages=pages,
        q=q,
        process=process,
    )


@bp.post("/adicionar")
@login_required
def add():
    from flask import session

    raw_domain = request.form.get("domain") or ""
    process_no = (request.form.get("process_no") or "").strip()
    notes = (request.form.get("notes") or "").strip()

    domain = rpz.normalize_domain(raw_domain)
    if not domain:
        flash(f"Domínio inválido: '{raw_domain}'.", "error")
        return redirect(url_for("blocks.index"))
    if not process_no:
        flash("Informe o número da ordem judicial/processo.", "error")
        return redirect(url_for("blocks.index"))

    user = session.get("username", "-")
    try:
        db = get_db()
        try:
            db.execute(
                "INSERT INTO blocked_domains (domain, process_no, notes, created_by) "
                "VALUES (?, ?, ?, ?)",
                (domain, process_no, notes, user),
            )
            db.commit()
        except sqlite3.IntegrityError:
            db.execute(
                "UPDATE blocked_domains SET active = 1, process_no = ?, notes = ?, "
                "created_by = ?, created_at = datetime('now') WHERE domain = ?",
                (process_no, notes, user, domain),
            )
            db.commit()
            flash(f"'{domain}' já existia e foi reativado com o novo processo.", "success")
        else:
            flash(f"Domínio '{domain}' bloqueado.", "success")
    except sqlite3.Error as exc:
        current_app.logger.exception("Falha ao gravar bloqueio %s: %s", domain, exc)
        flash(f"Não foi possível gravar o bloqueio no banco. Detalhe técnico: {exc}", "error")
        return redirect(url_for("blocks.index"))

    try:
        audit.log("bloqueio_incluido", f"{domain} | processo {process_no}")
    except sqlite3.Error as exc:
        current_app.logger.warning("Auditoria não registrada para %s: %s", domain, exc)

    _sync_and_flash(f"inclusão de {domain}")
    return redirect(url_for("blocks.index"))


@bp.post("/importar")
@login_required
def bulk_import():
    from flask import session

    from .. import parsers

    process_no = (request.form.get("process_no") or "").strip()
    list_type = (request.form.get("list_type") or "bloqueio").strip().lower()
    upload = request.files.get("file")
    if list_type not in ("bloqueio", "liberacao"):
        list_type = "bloqueio"
    if not process_no:
        flash("Informe o número do processo para o lote.", "error")
        return redirect(url_for("blocks.index"))
    if upload is None or not upload.filename:
        flash("Selecione um arquivo .txt, .csv, .xml ou .pdf.", "error")
        return redirect(url_for("blocks.index"))
    if not upload.filename.lower().endswith(parsers.SUPPORTED_EXTENSIONS):
        flash("Formato não suportado. Use .txt, .csv, .xml ou .pdf.", "error")
        return redirect(url_for("blocks.index"))

    try:
        domains, invalid = parsers.extract_domains(upload.filename, upload.read())
    except parsers.ParserError as exc:
        flash(str(exc), "error")
        return redirect(url_for("blocks.index"))

    if not domains:
        flash("Nenhum domínio válido foi encontrado no arquivo enviado.", "error")
        return redirect(url_for("blocks.index"))

    user = session.get("username", "-")
    added, duplicated = 0, 0
    db = get_db()

    if list_type == "liberacao":
        for domain in domains:
            exists = db.execute(
                "SELECT id, active FROM liberacoes WHERE domain = ?", (domain,)
            ).fetchone()
            if exists and exists["active"] == 1:
                duplicated += 1
                continue
            if exists:
                db.execute(
                    "UPDATE liberacoes SET active = 1, processo = ?, usuario = ?, "
                    "data_liberacao = datetime('now') WHERE id = ?",
                    (process_no, user, exists["id"]),
                )
            else:
                db.execute(
                    "INSERT INTO liberacoes (domain, processo, usuario, notes) "
                    "VALUES (?, ?, ?, ?)",
                    (domain, process_no, user, f"Importado de {upload.filename}"),
                )
            added += 1
    else:
        for domain in domains:
            exists = db.execute(
                "SELECT id, active FROM blocked_domains WHERE domain = ?", (domain,)
            ).fetchone()
            if exists and exists["active"] == 1:
                duplicated += 1
                continue
            if exists:
                db.execute(
                    "UPDATE blocked_domains SET active = 1, process_no = ?, created_by = ?, "
                    "origem = 'importacao', created_at = datetime('now') WHERE id = ?",
                    (process_no, user, exists["id"]),
                )
            else:
                db.execute(
                    "INSERT INTO blocked_domains (domain, process_no, created_by, origem) "
                    "VALUES (?, ?, ?, 'importacao')",
                    (domain, process_no, user),
                )
            added += 1
    db.commit()

    rotulo = "liberações" if list_type == "liberacao" else "bloqueios"
    msg = (
        f"Importação de {rotulo}: {added} incluídos, {duplicated} já existentes, "
        f"{len(invalid)} linhas ignoradas."
    )
    flash(msg, "success" if added else "error")
    if invalid:
        flash("Linhas ignoradas: " + ", ".join(invalid[:10]) + ("..." if len(invalid) > 10 else ""), "error")
    audit.log("importacao_massa", f"{upload.filename} | {rotulo} | processo {process_no} | {msg}")

    if added:
        _sync_and_flash("importação em massa")
    if list_type == "liberacao":
        return redirect(url_for("exceptions.index"))
    return redirect(url_for("blocks.index"))



@bp.post("/remover/<int:block_id>")
@login_required
def remove(block_id: int):
    db = get_db()
    row = db.execute("SELECT * FROM blocked_domains WHERE id = ?", (block_id,)).fetchone()
    if row is None:
        flash("Registro não encontrado.", "error")
        return redirect(url_for("blocks.index"))
    db.execute("DELETE FROM blocked_domains WHERE id = ?", (block_id,))
    db.commit()
    flash(f"Domínio '{row['domain']}' desbloqueado.", "success")
    audit.log("bloqueio_removido", f"{row['domain']} | processo {row['process_no']}")
    _sync_and_flash(f"remoção de {row['domain']}")
    return redirect(url_for("blocks.index"))


@bp.get("/exportar.csv")
@login_required
def export_csv():
    rows = get_db().execute(
        "SELECT id, domain, process_no, created_at, created_by FROM blocked_domains "
        "WHERE active = 1 ORDER BY id"
    ).fetchall()

    buffer = io.StringIO()
    writer = csv.writer(buffer, delimiter=";")
    writer.writerow(["ID", "Dominio", "Processo", "Data/Hora", "Usuario"])
    for r in rows:
        writer.writerow([r["id"], r["domain"], r["process_no"], r["created_at"], r["created_by"]])

    audit.log("exportacao_csv", f"{len(rows)} registros exportados")
    filename = f"bloqueios-{datetime.now().strftime('%Y%m%d-%H%M%S')}.csv"
    return Response(
        "\ufeff" + buffer.getvalue(),
        mimetype="text/csv; charset=utf-8",
        headers={"Content-Disposition": f'attachment; filename="{filename}"'},
    )


@bp.post("/ressincronizar")
@login_required
def resync():
    try:
        rpz.sync_zone()
        flash("Zona RPZ regravada e recarregada.", "success")
        audit.log("rpz_sync_manual", "Ressincronização manual da zona RPZ")
    except rpz.RPZError as exc:
        flash(str(exc), "error")
    return redirect(url_for("blocks.index"))
