"""Lifecycle trimiteri blocate: sterge / re-pune in coada.

Inchide lacuna: un rand `error` (creds RAR gresite) ar ramane altfel permanent si
nereparabil. Aceste helpere adauga DOUA tranzitii controlate — stergere de randuri
ne-sent si `blocate -> queued` (re-clasificat) — fara a atinge logica de trimitere
a worker-ului.

Invariante:
  - Opereaza DOAR pe `error`/`needs_data`/`needs_mapping`. `sent` (dovada de trimitere
    la RAR, audit) si `sending` (lease worker in zbor) sunt INTERZISE.
  - Scope-ul (apartenenta la cont) se evalueaza INAINTEA starii: un rand inexistent SAU
    al altui cont -> SubmissionNotFound (404, nu confirmam existenta). Doar pe randuri
    proprii in stare gresita -> SubmissionStateConflict (409).
  - Ambele emit eveniment in jurnal: `submission_sters` / `submission_repus`.

Functii cu `conn` (persistenta).
"""

from __future__ import annotations

import json

from .accounts import held_for_account
from .mapping import (
    account_or_default,
    account_scope_clause,
    classify_prezentare,
    load_mapping_meta,
    load_nomenclator_codes,
)
from .observ import log_event

# Stari pe care le putem sterge / re-pune in coada (ne-sent, ne-in-zbor).
_GESTIONABILE = ("error", "needs_data", "needs_mapping")


class SubmissionNotFound(Exception):
    """Randul nu exista SAU apartine altui cont (acelasi mesaj — nu confirmam existenta)."""


class SubmissionStateConflict(Exception):
    """Randul exista si e al contului, dar e intr-o stare protejata (sent/sending)."""

    def __init__(self, status: str):
        super().__init__(f"stare protejata: {status}")
        self.status = status


def _fetch_scoped(conn, account_id: int, sid: int):
    scope_sql, scope_params = account_scope_clause(account_id)
    return conn.execute(
        f"SELECT * FROM submissions WHERE id=? AND {scope_sql}",
        [sid] + scope_params,
    ).fetchone()


def delete_submission(conn, account_id: int, sid: int) -> dict:
    """Sterge un rand ne-sent al contului. Ridica SubmissionNotFound / SubmissionStateConflict.

    Intoarce {"submission_id", "status_anterior"} la succes.
    """
    row = _fetch_scoped(conn, account_id, sid)
    if row is None:
        raise SubmissionNotFound()
    status = row["status"]
    if status not in _GESTIONABILE:
        raise SubmissionStateConflict(status)
    conn.execute("DELETE FROM submissions WHERE id=?", (sid,))
    log_event(
        "submission_sters",
        account_id=account_or_default(account_id),
        mesaj=f"trimitere #{sid} stearsa din {status}",
        context={"submission_id": sid, "status_anterior": status},
        conn=conn,
    )
    return {"submission_id": sid, "status_anterior": status}


def requeue_submission(conn, account_id: int, sid: int) -> dict:
    """Re-pune in coada un rand blocat al contului: re-ruleaza classify pe payload.

    `error -> queued` (cand continutul e valid) sau ramane `needs_data`/`needs_mapping`
    daca clasificarea o cere. Reseteaza retry_count/next_attempt_at/sending_since si
    CURATA `purge_after` (randul redevine activ, nu mai e candidat la purjare).
    Ridica SubmissionNotFound / SubmissionStateConflict. Intoarce
    {"submission_id", "status_anterior", "status_nou"}.
    """
    row = _fetch_scoped(conn, account_id, sid)
    if row is None:
        raise SubmissionNotFound()
    status = row["status"]
    if status not in _GESTIONABILE:
        raise SubmissionStateConflict(status)

    try:
        content = json.loads(row["payload_json"]) if row["payload_json"] else {}
        if not isinstance(content, dict):
            content = {}
    except (ValueError, TypeError):
        content = {}

    mapping_meta = load_mapping_meta(conn, account_id)
    mapping = {op: m["cod_prestatie"] for op, m in mapping_meta.items()}
    valid_codes = load_nomenclator_codes(conn) or None
    cl = classify_prezentare(content, mapping, mapping_meta, valid_codes)

    # PRD 5.19 US-002: re-snapshot `held` din comutatorul contului la re-punere in coada
    # (paritate cu create_prezentari/reresolve_account). Fara asta un rand repus pastra
    # `held` VECHI si s-ar auto-trimite desi contul e Auto OFF (sau ar ramane blocat cand e ON).
    held_val = held_for_account(conn, account_or_default(account_id))
    conn.execute(
        "UPDATE submissions SET status=?, payload_json=?, rar_error=?, retry_count=0, "
        "next_attempt_at=NULL, sending_since=NULL, purge_after=NULL, held=?, updated_at=datetime('now') "
        "WHERE id=?",
        (cl["status"], json.dumps(cl["content"], ensure_ascii=False), cl["rar_error"], held_val, sid),
    )
    log_event(
        "submission_repus",
        account_id=account_or_default(account_id),
        mesaj=f"trimitere #{sid} re-pusa: {status} -> {cl['status']}",
        context={"submission_id": sid, "status_anterior": status, "status_nou": cl["status"]},
        conn=conn,
    )
    return {"submission_id": sid, "status_anterior": status, "status_nou": cl["status"]}
