from flask import render_template, jsonify, request, flash, redirect, url_for
from sqlalchemy.exc import SQLAlchemyError, IntegrityError, OperationalError
from werkzeug.exceptions import HTTPException, NotFound, Forbidden, BadRequest
import traceback

class ErrorHandler:
    """
    全局异常处理器
    用于注册Flask应用程序的错误处理函数
    """
    def __init__(self, app=None):
        if app:
            self.init_app(app)
    
    def init_app(self, app):
        """
        初始化应用程序的错误处理器
        :param app: Flask应用实例
        """
        # 处理SQLAlchemy错误
        app.register_error_handler(SQLAlchemyError, self.handle_db_error)
        app.register_error_handler(IntegrityError, self.handle_integrity_error)
        app.register_error_handler(OperationalError, self.handle_operational_error)
        
        # 处理HTTP错误
        app.register_error_handler(NotFound, self.handle_not_found)
        app.register_error_handler(Forbidden, self.handle_forbidden)
        app.register_error_handler(BadRequest, self.handle_bad_request)
        
        # 处理通用错误
        app.register_error_handler(Exception, self.handle_generic_error)
        
        # 记录初始化完成
        app.logger.info("错误处理器初始化完成")
    
    def handle_db_error(self, error):
        """
        处理一般数据库错误
        """
        error_details = str(error)
        traceback_details = traceback.format_exc()
        current_app = self._get_current_app()
        current_app.logger.error(f"数据库错误: {error_details}\n{traceback_details}")
        
        return self._handle_error("数据库操作错误", 
                                 "抱歉，数据库操作失败，请稍后重试。", 
                                 500)
    
    def handle_integrity_error(self, error):
        """
        处理数据完整性错误（如违反唯一约束）
        """
        error_details = str(error)
        current_app = self._get_current_app()
        current_app.logger.error(f"数据完整性错误: {error_details}")
        
        # 尝试从错误消息中提取更友好的错误消息
        friendly_message = "数据冲突，请检查您的输入。"
        if "Duplicate entry" in error_details:
            friendly_message = "数据已存在，请检查您的输入。"
        
        return self._handle_error("数据完整性错误", friendly_message, 400)
    
    def handle_operational_error(self, error):
        """
        处理数据库操作错误（如连接问题）
        """
        error_details = str(error)
        current_app = self._get_current_app()
        current_app.logger.error(f"数据库操作错误: {error_details}")
        
        return self._handle_error("数据库连接错误", 
                                 "抱歉，无法连接到数据库，请稍后重试。", 
                                 503)
    
    def handle_not_found(self, error):
        """
        处理404错误
        """
        return self._handle_error("页面未找到", 
                                 "抱歉，您请求的页面不存在。", 
                                 404)
    
    def handle_forbidden(self, error):
        """
        处理403错误
        """
        return self._handle_error("访问被拒绝", 
                                 "抱歉，您没有权限访问此页面。", 
                                 403)
    
    def handle_bad_request(self, error):
        """
        处理400错误
        """
        return self._handle_error("请求错误", 
                                 "抱歉，服务器无法理解您的请求。", 
                                 400)
    
    def handle_generic_error(self, error):
        """
        处理未被其他处理程序捕获的通用错误
        """
        error_details = str(error)
        traceback_details = traceback.format_exc()
        current_app = self._get_current_app()
        current_app.logger.error(f"未处理异常: {error_details}\n{traceback_details}")
        
        if isinstance(error, HTTPException):
            return self._handle_error("请求错误", error.description, error.code)
        
        return self._handle_error("服务器错误", 
                                 "抱歉，服务器遇到了未知错误，请稍后重试。", 
                                 500)
    
    def _handle_error(self, title, message, status_code):
        """
        根据请求类型返回适当的错误响应
        :param title: 错误标题
        :param message: 错误消息
        :param status_code: HTTP状态码
        """
        # 根据接受的内容类型决定返回JSON还是HTML
        # 检查是否是AJAX请求，is_xhr在新版Flask中已被移除
        is_ajax_request = request.headers.get('X-Requested-With') == 'XMLHttpRequest'
        if is_ajax_request or request.headers.get('Accept') == 'application/json':
            # 返回JSON格式的错误
            return jsonify({
                'error': title,
                'message': message
            }), status_code
        else:
            # 使用Flash消息并重定向到统计页面
            from flask import current_app
            flash(message)
            
            # 根据错误类型决定重定向页面
            if status_code == 404:
                return redirect(url_for('statistics'))
            elif status_code == 403:
                return redirect(url_for('statistics'))
            else:
                return redirect(url_for('statistics'))
    
    def _get_current_app(self):
        """
        获取当前Flask应用实例
        """
        from flask import current_app
        return current_app 