"""
Audit Log Events
"""

import logging

from sqlalchemy import event, inspect
from sqlalchemy.orm import Session

from api.v1.modules.a76.general_catalogs.company.models import Company
from api.v1.modules.a76.invoices.models import InvoiceHeader
from core.database import (
    RLS_COMPANY_KEY,
    RLS_TENANT_KEY,
    rls_company_var,
    rls_tenant_var,
)

from .services.service import AuditService
from .utils.serialization import serialize_for_json
from core.context import get_user_context

logger = logging.getLogger(__name__)


def register_audit_listeners(models_to_audit):
    """
    Register SQLAlchemy listeners for given models
    """
    for model in models_to_audit:
        event.listen(model, "after_insert", after_insert_listener)
        event.listen(model, "after_update", after_update_listener)
        event.listen(model, "after_delete", after_delete_listener)


def _get_current_username():
    try:
        context = get_user_context()
        if context:
            # Token usually has 'preferred_username' or 'name' or 'sub'
            return (
                context.get("preferred_username")
                or context.get("email")
                or context.get("sub")
                or "System"
            )
    except Exception:
        pass
    return "System"


def _resolve_audit_company_tenant(session: Session, target) -> tuple:
    """
    company_id / tenant_id desde la fila ORM, ContextVars RLS (petición HTTP),
    o ``Company.tenant_id`` por ``company_id``.
    """
    resolution_source = "target"
    company_id = getattr(target, "company_id", None)
    if company_id is None and getattr(target, "__tablename__", None) == "company":
        company_id = getattr(target, "id", None)
        if company_id is not None:
            resolution_source = "company_self_id"
    if company_id is None:
        company_id = session.info.get(RLS_COMPANY_KEY)
        if company_id is None:
            company_id = rls_company_var.get()
        if company_id is not None:
            resolution_source = "rls_context"

    tenant_id = getattr(target, "tenant_id", None)
    if tenant_id is None:
        tenant_id = session.info.get(RLS_TENANT_KEY)
        if tenant_id is None:
            tenant_id = rls_tenant_var.get()
        if tenant_id is not None and resolution_source == "target":
            resolution_source = "rls_context"

    # Common fallback for invoice child tables where invoice_id points to header scope.
    if (company_id is None or tenant_id is None) and hasattr(target, "invoice_id"):
        invoice_id = getattr(target, "invoice_id", None)
        if invoice_id is not None:
            invoice_scope = (
                session.query(InvoiceHeader.company_id, InvoiceHeader.tenant_id)
                .filter(InvoiceHeader.id == invoice_id, InvoiceHeader.deleted_at.is_(None))
                .first()
            )
            if invoice_scope:
                if company_id is None:
                    company_id = invoice_scope[0]
                if tenant_id is None:
                    tenant_id = invoice_scope[1]
                resolution_source = "invoice_header_lookup"

    if tenant_id is None and company_id is not None:
        row = (
            session.query(Company.tenant_id)
            .filter(Company.id == company_id, Company.deleted_at.is_(None))
            .first()
        )
        if row:
            tenant_id = int(row[0])
            resolution_source = "company_lookup"

    return company_id, tenant_id, resolution_source


def after_insert_listener(mapper, connection, target):
    """
    Listener for INSERT operations
    """
    table_name = target.__tablename__
    record_data = {c.name: getattr(target, c.name) for c in mapper.columns}
    username = _get_current_username()

    session = Session(bind=connection)
    try:
        company_id, tenant_id, resolution_source = _resolve_audit_company_tenant(
            session, target
        )
        if company_id is None or tenant_id is None:
            logger.warning(
                "Audit skip INSERT table=%s record_id=%s company_id=%s tenant_id=%s source=%s",
                table_name,
                getattr(target, "id", None),
                company_id,
                tenant_id,
                resolution_source,
            )
            return

        AuditService.log_crud_operation(
            db=session,
            table_name=table_name,
            operation_type="CREATE",
            record_data=record_data,
            username=username,
            record_id=str(getattr(target, "id", "")),
            company_id=company_id,
            tenant_id=tenant_id,
        )
    except Exception as e:
        logger.warning("Error logging insert for %s: %s", table_name, e)
    finally:
        session.close()


def after_update_listener(mapper, connection, target):
    """
    Listener for UPDATE operations
    """
    table_name = target.__tablename__

    state = inspect(target)
    changes = {}
    old_values = {}
    new_values = {}

    for attr in state.attrs:
        hist = attr.history
        if hist.has_changes():
            changes[attr.key] = hist.added[0] if hist.added else None
            old_values[attr.key] = hist.deleted[0] if hist.deleted else None
            new_values[attr.key] = hist.added[0] if hist.added else None

    if not changes:
        return

    record_data = {c.name: getattr(target, c.name) for c in mapper.columns}
    username = _get_current_username()

    session = Session(bind=connection)
    try:
        company_id, tenant_id, resolution_source = _resolve_audit_company_tenant(
            session, target
        )
        if company_id is None or tenant_id is None:
            logger.warning(
                "Audit skip UPDATE table=%s record_id=%s company_id=%s tenant_id=%s source=%s",
                table_name,
                getattr(target, "id", None),
                company_id,
                tenant_id,
                resolution_source,
            )
            return

        AuditService.log_crud_operation(
            db=session,
            table_name=table_name,
            operation_type="UPDATE",
            record_data=record_data,
            username=username,
            record_id=str(getattr(target, "id", "")),
            old_values=serialize_for_json(old_values),
            new_values=serialize_for_json(new_values),
            company_id=company_id,
            tenant_id=tenant_id,
        )
    except Exception as e:
        logger.warning("Error logging update for %s: %s", table_name, e)
    finally:
        session.close()


def after_delete_listener(mapper, connection, target):
    """
    Listener for DELETE operations
    """
    table_name = target.__tablename__
    record_data = {c.name: getattr(target, c.name) for c in mapper.columns}
    username = _get_current_username()

    session = Session(bind=connection)
    try:
        company_id, tenant_id, resolution_source = _resolve_audit_company_tenant(
            session, target
        )
        if company_id is None or tenant_id is None:
            logger.warning(
                "Audit skip DELETE table=%s record_id=%s company_id=%s tenant_id=%s source=%s",
                table_name,
                getattr(target, "id", None),
                company_id,
                tenant_id,
                resolution_source,
            )
            return

        AuditService.log_crud_operation(
            db=session,
            table_name=table_name,
            operation_type="DELETE",
            record_data=record_data,
            username=username,
            record_id=str(getattr(target, "id", "")),
            company_id=company_id,
            tenant_id=tenant_id,
        )
    except Exception as e:
        logger.warning("Error logging delete for %s: %s", table_name, e)
    finally:
        session.close()
