#!/usr/bin/env python
# -*- coding: UTF-8 -*-
"""
Django 中间件：Trace-Id 注入、API 操作日志记录、就绪/存活健康检查。
- TraceIdMiddleware: 为请求生成或透传 X-Trace-Id，写入上下文供日志使用。
- ApiLoggingMiddleware: 在 process_view 创建 OperationLog，process_response 时回填结果。
- HealthCheckMiddleware: /healthy 存活、/readiness 依赖（DB/cache）检查。
"""

import json
import logging
import uuid

from django.conf import settings
from django.contrib.auth.models import AnonymousUser
from django.http import HttpResponse, HttpResponseServerError
from django.utils.deprecation import MiddlewareMixin

from django_base_ai.system.models import OperationLog
from django_base_ai.utils.log_filters import trace_id_ctx
from django_base_ai.utils.request_util import (
    get_browser,
    get_os,
    get_request_data,
    get_request_ip,
    get_request_path,
    get_request_user,
    get_verbose_name,
)

logger = logging.getLogger(__name__)


class TraceIdMiddleware(MiddlewareMixin):
    """
    为每个请求生成或透传 Trace-Id，写入 request.trace_id 与 log_filters.trace_id_ctx，供日志输出。
    请求头 X-Trace-Id 若存在则复用，否则生成 UUID。
    """

    def process_request(self, request):
        trace_id = request.META.get("HTTP_X_TRACE_ID", "").strip() or uuid.uuid4().hex
        request.trace_id = trace_id
        trace_id_ctx.set(trace_id)
        logger.debug("Trace-Id 注入 trace_id=%s path=%s", trace_id, request.path)

    def process_response(self, request, response):
        if getattr(response, "headers", None) is not None and hasattr(request, "trace_id"):
            response.headers.setdefault("X-Trace-Id", request.trace_id)
        try:
            trace_id_ctx.set("")
        except LookupError:
            pass
        return response


class ApiLoggingMiddleware(MiddlewareMixin):
    """
    用于记录API访问日志中间件
    """

    def __init__(self, get_response=None):
        super().__init__(get_response)
        self.enable = getattr(settings, "API_LOG_ENABLE", None) or False
        self.methods = getattr(settings, "API_LOG_METHODS", None) or set()
        self.operation_log_id = None
        logger.debug("ApiLoggingMiddleware 初始化 enable=%s methods=%s", self.enable, self.methods)

    @classmethod
    def __handle_request(cls, request):
        request.request_ip = get_request_ip(request)
        request.request_data = get_request_data(request)
        request.request_path = get_request_path(request)

    def __handle_response(self, request, response):
        # request_data,request_ip由PermissionInterfaceMiddleware中间件中添加的属性
        body = getattr(request, "request_data", {})
        # 请求含有password则用*替换掉(暂时先用于所有接口的password请求参数)
        if isinstance(body, dict) and body.get("password", ""):
            body["password"] = "*" * len(body["password"])
        if not hasattr(response, "data") or not isinstance(response.data, dict):
            response.data = {}
        try:
            if not response.data and response.content:
                content = json.loads(response.content.decode())
                response.data = content if isinstance(content, dict) else {}
        except Exception:
            return
        user = get_request_user(request)
        info = {
            "request_ip": getattr(request, "request_ip", "unknown"),
            "creator": user.id if not isinstance(user, AnonymousUser) else None,
            "dept_belong_id": getattr(request.user, "dept_id", None),
            "request_method": request.method,
            "request_path": request.request_path,
            "request_body": body,
            "response_code": response.data.get("code"),
            "request_os": get_os(request),
            "request_browser": get_browser(request),
            "request_msg": request.session.get("request_msg"),
            "status": True
            if response.data.get("code")
            in [
                2000,
            ]
            else False,
            "json_result": {"code": response.data.get("code"), "msg": response.data.get("msg")},
        }
        operation_log, created = OperationLog.objects.update_or_create(defaults=info, id=self.operation_log_id)
        if not operation_log.request_modular and settings.API_MODEL_MAP.get(request.request_path, None):
            operation_log.request_modular = settings.API_MODEL_MAP[request.request_path]
            operation_log.save()
        logger.debug("API 操作日志已记录 id=%s path=%s code=%s", operation_log.id, request.request_path, info.get("response_code"))

    def process_view(self, request, view_func, view_args, view_kwargs):
        if hasattr(view_func, "cls") and hasattr(view_func.cls, "queryset"):
            if self.enable:
                if self.methods == "ALL" or request.method in self.methods:
                    log = OperationLog(request_modular=get_verbose_name(view_func.cls.queryset))
                    log.save()
                    self.operation_log_id = log.id

        return

    def process_request(self, request):
        self.__handle_request(request)

    def process_response(self, request, response):
        """
        主要请求处理完之后记录
        :param request:
        :param response:
        :return:
        """
        if (
            self.enable
            and (self.methods == "ALL" or request.method in self.methods)
            and request.path
            and not any(request.path in url for url in getattr(settings, "API_LOG_EXCLUDE", []))
        ):
            self.__handle_response(request, response)
        return response


health_logger = logging.getLogger("healthy")


class HealthCheckMiddleware:
    """
    存活检查中间件
    """

    def __init__(self, get_response):
        self.get_response = get_response
        # One-time configuration and initialization.

    def __call__(self, request):
        if request.method == "GET":
            if "/readiness" in request.path:
                return self.readiness(request)
            elif "/healthy" in request.path:
                return self.healthy(request)
        return self.get_response(request)

    def healthy(self, request):
        """
        Returns that the server is alive.
        """
        return HttpResponse("OK")

    def readiness(self, request):
        # Connect to each database and do a generic standard SQL query
        # that doesn't write any data and doesn't depend on any tables
        # being present.
        try:
            from django.db import connections

            for name in connections:
                cursor = connections[name].cursor()
                cursor.execute("SELECT 1;")
                row = cursor.fetchone()
                if row is None:
                    return HttpResponseServerError("db: invalid response")
        except Exception as e:
            health_logger.exception("readiness db 检查失败: %s", e)
            return HttpResponseServerError("db: cannot connect to database.")

        # Call get_stats() to connect to each memcached instance and get it's stats.
        # This can effectively check if each is online.
        try:
            from django.core.cache import caches
            from django.core.cache.backends.memcached import BaseMemcachedCache

            for cache in caches.all():
                logger.debug("readiness 检查 cache backend: %s", type(cache).__name__)
                if isinstance(cache, BaseMemcachedCache):
                    stats = cache._cache.get_stats()
                    if len(stats) != len(cache._servers):
                        return HttpResponseServerError("cache: cannot connect to cache.")
        except Exception as e:
            health_logger.exception("readiness cache 检查失败: %s", e)
            return HttpResponseServerError("cache: cannot connect to cache.")

        return HttpResponse("OK")
