import json
from datetime import datetime, timedelta

from sqlalchemy.orm import Session

from app.models import AdminUser, OperationLog

LOG_RETENTION_DAYS = 180
HIGH_RISK_LOG_RETENTION_DAYS = 365

ACTION_LABELS = {
    "create_project": "\u65b0\u589e\u9879\u76ee",
    "update_project": "\u4fee\u6539\u9879\u76ee",
    "create_learner": "\u65b0\u589e\u5b66\u5458",
    "update_learner": "\u4fee\u6539\u5b66\u5458",
    "delete_learner": "\u5220\u9664\u5b66\u5458",
    "create_certificate": "\u65b0\u589e\u8bc1\u4e66",
    "start_pdf_pregeneration": "\u542f\u52a8PDF\u6279\u91cf\u9884\u751f\u6210",
    "void_certificate": "\u4f5c\u5e9f\u8bc1\u4e66",
    "regenerate_public_token": "\u91cd\u7f6e\u8bc1\u4e66\u94fe\u63a5",
    "upload_import_file": "\u4e0a\u4f20\u5bfc\u5165\u6587\u4ef6",
    "confirm_import_batch": "\u786e\u8ba4\u5bfc\u5165",
    "delete_import_batch": "\u5220\u9664\u5bfc\u5165\u6279\u6b21",
    "export_certificates": "\u5bfc\u51fa\u8bc1\u4e66",
    "create_database_backup": "\u521b\u5efa\u6570\u636e\u5e93\u5907\u4efd",
    "restore_database_backup": "\u56de\u6eda\u6570\u636e\u5e93\u5907\u4efd",
    "update_pdf_settings": "\u4fee\u6539PDF\u751f\u6210\u8bbe\u7f6e",
    "public_search_certificate": "\u516c\u5f00\u67e5\u8be2\u8bc1\u4e66",
    "public_download_certificate": "\u516c\u5f00\u4e0b\u8f7d\u8bc1\u4e66",
}
OBJECT_LABELS = {
    "project": "\u9879\u76ee",
    "project_course": "\u9879\u76ee",
    "learner": "\u5b66\u5458",
    "certificate": "\u8bc1\u4e66",
    "import_batch": "\u5bfc\u5165\u6279\u6b21",
    "database_backup": "\u6570\u636e\u5e93\u5907\u4efd",
    "system_setting": "\u7cfb\u7edf\u8bbe\u7f6e",
    "public_access": "\u516c\u5f00\u8bbf\u95ee",
}
HIGH_RISK_ACTIONS = {
    "void_certificate",
    "delete_import_batch",
    "regenerate_public_token",
    "delete_learner",
    "restore_database_backup",
}
MEDIUM_RISK_ACTIONS = {
    "update_project",
    "update_learner",
    "confirm_import_batch",
    "create_certificate",
    "start_pdf_pregeneration",
    "create_database_backup",
    "update_pdf_settings",
}


def log_action(
    db: Session,
    admin: AdminUser | None,
    action: str,
    object_type: str,
    object_id: int | str | None = None,
    detail: dict[str, object] | None = None,
) -> None:
    db.add(
        OperationLog(
            admin_user_id=admin.id if admin else None,
            action=action,
            object_type=object_type,
            object_id=str(object_id) if object_id is not None else None,
            detail_json=json.dumps(detail, ensure_ascii=False, default=str) if detail else None,
        )
    )


def mask_phone(phone: object) -> str:
    raw = "".join(ch for ch in str(phone or "") if ch.isdigit())
    if len(raw) >= 7:
        return raw[:3] + "****" + raw[-4:]
    return raw or "-"


def log_risk(action: str) -> str:
    if action in HIGH_RISK_ACTIONS:
        return "high"
    if action in MEDIUM_RISK_ACTIONS:
        return "medium"
    return "low"


def log_risk_text(risk: str) -> str:
    return {"high": "\u9ad8\u98ce\u9669", "medium": "\u4e2d\u98ce\u9669", "low": "\u4f4e\u98ce\u9669"}.get(risk, risk)


def diff_values(before: dict[str, object], after: dict[str, object], fields: list[str]) -> dict[str, dict[str, object]]:
    changes: dict[str, dict[str, object]] = {}
    for field in fields:
        if before.get(field) != after.get(field):
            changes[field] = {"before": before.get(field), "after": after.get(field)}
    return changes


def log_summary(action: str, object_type: str, object_id: object, detail: dict[str, object]) -> str:
    label = ACTION_LABELS.get(action, action)
    if action in {"create_project", "update_project"}:
        return f"{label}\uff1a{detail.get('name') or detail.get('code') or object_id}"
    if action in {"create_learner", "update_learner", "delete_learner"}:
        phone = detail.get("phone")
        return f"{label}\uff1a{detail.get('name') or object_id}" + (f"\uff08{phone}\uff09" if phone else "")
    if action in {"create_certificate", "void_certificate", "regenerate_public_token"}:
        learner_name = detail.get("learner_name")
        return f"{label}\uff1a{detail.get('certificate_no') or object_id}" + (f"\uff08{learner_name}\uff09" if learner_name else "")
    if action == "start_pdf_pregeneration":
        return f"{label}\uff1a{detail.get('total', 0)} \u5f20\uff0c\u5e76\u53d1 {detail.get('concurrency_limit')}"
    if action in {"upload_import_file", "delete_import_batch"}:
        return f"{label}\uff1a{detail.get('filename') or object_id}"
    if action == "confirm_import_batch":
        count = detail.get("imported_rows", detail.get("valid_rows", 0))
        return f"{label}\uff1a\u6279\u6b21 {object_id}\uff0c\u5bfc\u5165 {count} \u884c"
    if action in {"create_database_backup", "restore_database_backup"}:
        filename = detail.get("filename") or object_id
        size_label = detail.get("size_label")
        return f"{label}\uff1a{filename}" + (f"\uff08{size_label}\uff09" if size_label else "")
    if action == "update_pdf_settings":
        return f"{label}\uff1a\u5e76\u53d1\u6570 {detail.get('before')} -> {detail.get('after')}"
    if action == "public_search_certificate":
        mode = "\u8bc1\u4e66\u7f16\u53f7" if detail.get("mode") == "certificate_no" else "\u624b\u673a\u53f7"
        return f"{label}\uff1a{detail.get('name') or '-'} \u7528{mode}\u67e5\u8be2\uff0c\u5339\u914d {detail.get('matched_count', 0)} \u6761"
    if action == "public_download_certificate":
        return f"{label}\uff1a{detail.get('certificate_no') or object_id}\uff08{detail.get('learner_name') or '-'}\uff09"
    return f"{label}\uff1a{OBJECT_LABELS.get(object_type, object_type)} {object_id or ''}".strip()


def format_log(log: OperationLog) -> dict[str, object]:
    detail: dict[str, object] = {}
    if log.detail_json:
        try:
            detail = json.loads(log.detail_json)
        except Exception:
            detail = {"raw": log.detail_json}
    risk = log_risk(log.action)
    return {
        "id": log.id,
        "admin_user_id": log.admin_user_id,
        "action": log.action,
        "action_label": ACTION_LABELS.get(log.action, log.action),
        "object_type": log.object_type,
        "object_label": OBJECT_LABELS.get(log.object_type, log.object_type),
        "object_id": log.object_id,
        "ip_address": log.ip_address,
        "detail_json": log.detail_json,
        "created_at": log.created_at,
        "risk": risk,
        "risk_label": log_risk_text(risk),
        "summary": log_summary(log.action, log.object_type, log.object_id, detail),
    }


def cleanup_expired_logs(db: Session) -> int:
    normal_before = datetime.utcnow() - timedelta(days=LOG_RETENTION_DAYS)
    high_before = datetime.utcnow() - timedelta(days=HIGH_RISK_LOG_RETENTION_DAYS)
    normal_deleted = (
        db.query(OperationLog)
        .filter(OperationLog.created_at < normal_before)
        .filter(OperationLog.action.notin_(HIGH_RISK_ACTIONS))
        .delete(synchronize_session=False)
    )
    high_deleted = (
        db.query(OperationLog)
        .filter(OperationLog.created_at < high_before)
        .filter(OperationLog.action.in_(HIGH_RISK_ACTIONS))
        .delete(synchronize_session=False)
    )
    return int(normal_deleted or 0) + int(high_deleted or 0)
