import re
from typing import NoReturn

import oracledb
from fastapi import HTTPException
from shared.database.oracle_pool import oracle_pool
from ..schemas.comanda import ComandaRequest, ComandaResponse
from .. import logger


def _handle_oracle_error(e: Exception) -> NoReturn:
    """
    Map Oracle error codes to FastAPI HTTPExceptions.  Always raises.

    Code ranges:
      20001-20999  → 422 Unprocessable (business rule errors from RAISE_APPLICATION_ERROR)
      12541/12170/12154/12560 → 503 Service Unavailable (Oracle unreachable / network)
      1017         → 500 + CRITICAL log (bad credentials — config error)
      942          → 500 + CRITICAL log (missing object/grant — deployment error)
      *            → 500 + ERROR log (unexpected)
    """
    err = e.args[0]
    code = getattr(err, "code", 0)
    raw_message = getattr(err, "message", str(e))

    if 20001 <= code <= 20999:
        # Strip "ORA-2xxxx: " prefix injected by Oracle; expose the business message only
        clean = re.sub(r"^ORA-\d+:\s*", "", raw_message).strip()
        raise HTTPException(status_code=422, detail=clean)

    if code in (12541, 12170, 12154, 12560):
        raise HTTPException(
            status_code=503,
            detail="Serviciul bazei de date e temporar indisponibil",
        )

    if code == 1017:
        logger.critical("Oracle credentials rejected (ORA-01017)", exc_info=True)
        raise HTTPException(
            status_code=500,
            detail="Eroare de configurare. Contactați administratorul.",
        )

    if code == 942:
        logger.critical("Oracle object not found or grant missing (ORA-00942)", exc_info=True)
        raise HTTPException(
            status_code=500,
            detail="Eroare internă. Contactați administratorul.",
        )

    logger.error("Unexpected Oracle error ORA-%05d", code, exc_info=True)
    raise HTTPException(status_code=500, detail="Eroare internă neașteptată")


class ComandaService:
    @staticmethod
    async def creeaza_comanda(
        data: ComandaRequest,
        username: str,
    ) -> ComandaResponse:
        logger.info(
            "service_auto.create_comanda START",
            extra={
                "user": username,
                "tip": data.tip_id,
                "client_id": data.id_masiniclient,
                "id_firma": data.id_firma,
            },
        )

        async with oracle_pool.get_connection("mariusm_test") as connection:
            try:
                with connection.cursor() as cursor:
                    out_id_ordl = cursor.var(oracledb.NUMBER)
                    out_nrord = cursor.var(oracledb.STRING)

                    cursor.callproc(
                        "MARIUSM_AUTO.SP_CREEAZA_COMANDA_PROTOTIP",
                        [
                            data.tip_id,          # p_tip IN NUMBER
                            data.id_masiniclient, # p_id_masiniclient IN NUMBER
                            data.solicitari,      # p_solicitari IN VARCHAR2
                            data.id_firma,        # p_id_firma IN NUMBER
                            out_id_ordl,          # p_id_ordl OUT NUMBER
                            out_nrord,            # p_nrord OUT VARCHAR2
                        ],
                    )

                    connection.commit()

                    id_ordl = int(out_id_ordl.getvalue())
                    nrord = out_nrord.getvalue() or ""
            except oracledb.DatabaseError as e:
                try:
                    connection.rollback()
                except Exception:
                    pass  # connection may be dead on network errors; ignore
                _handle_oracle_error(e)

        logger.info(
            "service_auto.create_comanda OK",
            extra={"user": username, "id_ordl": id_ordl, "nrord": nrord},
        )

        return ComandaResponse(
            id_ordl=id_ordl,
            nrord=nrord,
            mesaj=f"Comanda {nrord} creata cu succes.",
        )
