from datetime import datetime
import json
from .models import db, ChallengeRecord, User
from typing import Dict, List, Optional, Any, Union
import os
import glob
from flask import current_app
from sqlalchemy.orm.attributes import flag_modified


class ChallengeManager:
    """
    题目管理类：处理题目记录的增删改查操作
    """
    
    @staticmethod
    def save_challenge_record(data: Dict[str, Any], user_id: Optional[int] = None) -> int:
        """
        保存题目生成记录到数据库
        
        Args:
            data: 包含题目详细信息的字典
            user_id: 用户ID，如果提供则关联到此用户
            
        Returns:
            int: 新创建或更新的记录ID
        """
        # 检查是否有challenge_info字段，如果有，提取名称和描述
        if 'challenge_info' in data:
            name = data['challenge_info'].get('name', '未命名题目')
            description = data['challenge_info'].get('description', '')
        else:
            name = data.get('name', '未命名题目')
            description = data.get('description', '')
        
        # 检查是否已存在同名题目
        existing_challenge = ChallengeRecord.query.filter_by(name=name).first()
        
        if existing_challenge:
            # 更新现有记录（包括名称）
            challenge = existing_challenge
            challenge.name = name  # 更新名称
        else:
            # 创建新记录
            challenge = ChallengeRecord()
            challenge.name = name
        
        # 更新基本信息
        challenge.description = description
        challenge.output_dir = data.get('output_dir')
        challenge.log_file = data.get('log_file')
        challenge.estimated_time = data.get('estimated_time', '1-2小时')
        challenge.generation_status = data.get('generation_status', 'completed')
        challenge.generation_warning = data.get('generation_warning')
        
        # 设置部署能力（如果提供）
        if 'deployable' in data:
            challenge.deployable = data['deployable']

        # 设置用户ID
        if user_id:
            challenge.user_id = user_id

        # 保存到数据库
        try:
            if not existing_challenge:
                db.session.add(challenge)
            db.session.commit()
            return challenge.id
        except Exception as e:
            db.session.rollback()
            raise e

    @staticmethod
    def get_challenge_record(challenge_id: int) -> Optional[Dict[str, Any]]:
        """
        根据ID获取题目记录

        Args:
            challenge_id: 题目记录ID

        Returns:
            Optional[Dict]: 题目详细信息字典，如果不存在则返回None
        """
        try:
            challenge = ChallengeRecord.query.get(challenge_id)
            if challenge:
                import logging
                logger = logging.getLogger(__name__)
                logger.debug(f"找到题目记录: id={challenge.id}, name={challenge.name}")
                logger.debug(f"output_dir={challenge.output_dir}, log_file={challenge.log_file}")
                result = challenge.to_dict()
                logger.debug(f"to_dict() 返回的 keys: {result.keys()}")
                logger.debug(f"to_dict() 中的 output_dir: {result.get('output_dir')}")
                return result
            return None
        except Exception as e:
            print(f"获取题目记录失败: {str(e)}")
            import traceback
            traceback.print_exc()
            return None

    @staticmethod
    def get_challenge_by_name(name: str) -> Optional[Dict[str, Any]]:
        """
        根据名称获取题目记录
        
        Args:
            name: 题目名称
            
        Returns:
            Optional[Dict]: 题目详细信息字典，如果不存在则返回None
        """
        challenge = ChallengeRecord.query.filter_by(name=name).first()
        if not challenge:
            return None
        
        return challenge.to_dict()

    @staticmethod
    def get_latest_challenge() -> Optional[Dict[str, Any]]:
        """
        获取最新生成的题目记录
        
        Returns:
            Optional[Dict]: 最新题目详细信息字典，如果不存在则返回None
        """
        challenge = ChallengeRecord.query.order_by(ChallengeRecord.created_at.desc()).first()
        if not challenge:
            return None
        
        return challenge.to_dict()

    @staticmethod
    def list_challenges(page: int = 1, per_page: int = 10) -> Dict[str, Any]:
        """
        获取题目列表（分页）
        
        Args:
            page: 页码
            per_page: 每页记录数
            
        Returns:
            Dict: 包含分页信息和题目列表的字典
        """
        pagination = ChallengeRecord.query.order_by(ChallengeRecord.created_at.desc()).paginate(
            page=page, per_page=per_page, error_out=False
        )
        
        challenges = []
        for challenge in pagination.items:
            challenges.append({
                'id': challenge.id,
                'name': challenge.name,
                'description': challenge.description,
                'difficulty': challenge.difficulty_level.name if challenge.difficulty_level else None,
                'created_at': challenge.created_at.isoformat() if challenge.created_at else None
            })
        
        return {
            'total': pagination.total,
            'pages': pagination.pages,
            'current_page': pagination.page,
            'per_page': pagination.per_page,
            'has_next': pagination.has_next,
            'has_prev': pagination.has_prev,
            'challenges': challenges
        }

    @staticmethod
    def delete_challenge(challenge_id: int) -> bool:
        """
        删除题目记录（同时删除相关文件）

        Args:
            challenge_id: 题目记录ID

        Returns:
            bool: 是否成功删除
        """
        challenge = ChallengeRecord.query.get(challenge_id)
        if not challenge:
            return False

        try:
            import os
            from app.services.storage import challenge_file_manager

            # 使用文件管理器删除输出目录
            if challenge.output_dir:
                if os.path.exists(challenge.output_dir):
                    success = challenge_file_manager.delete_directory(challenge.output_dir)
                    if success:
                        print(f"✅ 已删除输出目录: {challenge.output_dir}")
                    else:
                        print(f"⚠️ 删除输出目录失败: {challenge.output_dir}")
                else:
                    print(f"⚠️ 输出目录不存在: {challenge.output_dir}")

            # 删除日志文件
            if challenge.log_file and os.path.exists(challenge.log_file):
                try:
                    os.remove(challenge.log_file)
                    print(f"✅ 已删除日志文件: {challenge.log_file}")
                except Exception as e:
                    print(f"⚠️ 删除日志文件失败: {str(e)}")

            # 删除数据库记录
            db.session.delete(challenge)
            db.session.commit()
            print(f"✅ 已删除题目记录: ID={challenge_id}, Name={challenge.name}")
            return True
        except Exception as e:
            db.session.rollback()
            raise e
            
    @staticmethod
    def update_challenge(challenge_id: int, data: Dict[str, Any]) -> bool:
        """
        更新题目记录
        
        Args:
            challenge_id: 题目记录ID
            data: 更新的数据
            
        Returns:
            bool: 是否成功更新
        """
        challenge = ChallengeRecord.query.get(challenge_id)
        if not challenge:
            return False
        
        try:
            # 更新字段
            for key, value in data.items():
                if hasattr(challenge, key):
                    setattr(challenge, key, value)
            
            # 如果更新了JSON字段，标记为已修改
            for field in ['tech_stack', 'core_functions', 'auxiliary_functions', 
                         'non_vulnerable_functions', 'project_structure', 
                         'code_files', 'binary_files', 'file_list', 'evaluation_result']:
                if field in data:
                    flag_modified(challenge, field)
            
            db.session.commit()
            return True
        except Exception as e:
            db.session.rollback()
            raise e


# 以下是为了保持向后兼容性的函数别名
# 题目记录操作
def save_challenge_record(data: Dict[str, Any], user_id: Optional[int] = None) -> int:
    """保存题目生成记录到数据库（向后兼容函数）"""
    return ChallengeManager.save_challenge_record(data, user_id)

def get_challenge_record(challenge_id: int) -> Optional[Dict[str, Any]]:
    """根据ID获取题目记录（向后兼容函数）"""
    return ChallengeManager.get_challenge_record(challenge_id)

def get_challenge_by_name(name: str) -> Optional[Dict[str, Any]]:
    """根据名称获取题目记录（向后兼容函数）"""
    return ChallengeManager.get_challenge_by_name(name)

def get_latest_challenge() -> Optional[Dict[str, Any]]:
    """获取最新生成的题目记录（向后兼容函数）"""
    return ChallengeManager.get_latest_challenge()

def list_challenges(page: int = 1, per_page: int = 10) -> Dict[str, Any]:
    """获取题目列表（向后兼容函数）"""
    return ChallengeManager.list_challenges(page, per_page)

def delete_challenge(challenge_id: int) -> bool:
    """删除题目记录（向后兼容函数）"""
    return ChallengeManager.delete_challenge(challenge_id)

def update_challenge(challenge_id: int, data: Dict[str, Any]) -> bool:
    """更新题目记录（向后兼容函数）"""
    return ChallengeManager.update_challenge(challenge_id, data)

def get_challenges_by_user(user_id: int) -> List[Dict[str, Any]]:
    """
    获取指定用户的所有题目记录

    Args:
        user_id: 用户ID

    Returns:
        List[Dict]: 题目列表
    """
    try:
        challenges = ChallengeRecord.query.filter_by(user_id=user_id).order_by(ChallengeRecord.created_at.desc()).all()
        return [challenge.to_dict() for challenge in challenges]
    except Exception as e:
        print(f"获取用户题目列表失败: {str(e)}")
        return []

# 部署记录相关操作
def create_deployment_record(deployment_data):
    """创建部署记录
    
    Args:
        deployment_data: 部署数据字典
        
    Returns:
        DeploymentRecord: 创建的部署记录对象
    """
    try:
        from app.models.database.models import DeploymentRecord, db
        
        # 处理部署ID
        if 'deployment_uuid' not in deployment_data and 'id' in deployment_data:
            deployment_data['deployment_uuid'] = deployment_data['id']
        
        # 创建记录
        record = DeploymentRecord.from_dict(deployment_data)
        
        # 保存到数据库
        db.session.add(record)
        db.session.commit()
        
        return record
    except Exception as e:
        db.session.rollback()
        print(f"创建部署记录失败: {str(e)}")
        import traceback
        traceback.print_exc()
        return None

def get_deployment_by_uuid(deployment_uuid):
    """通过UUID获取部署记录
    
    Args:
        deployment_uuid: 部署UUID
        
    Returns:
        Dict: 部署记录字典
    """
    try:
        from app.models.database.models import DeploymentRecord
        
        record = DeploymentRecord.query.filter_by(deployment_uuid=deployment_uuid).first()
        if record:
            return record.to_dict()
        return None
    except Exception as e:
        print(f"获取部署记录失败: {str(e)}")
        return None

def get_deployments_by_user(user_id, limit=10, offset=0):
    """获取用户的部署记录列表
    
    Args:
        user_id: 用户ID
        limit: 返回结果数量限制
        offset: 结果偏移量
        
    Returns:
        List[Dict]: 部署记录字典列表
    """
    try:
        from app.models.database.models import DeploymentRecord
        
        records = DeploymentRecord.query.filter_by(user_id=user_id) \
            .order_by(DeploymentRecord.created_at.desc()) \
            .offset(offset).limit(limit).all()
            
        return [record.to_dict() for record in records]
    except Exception as e:
        print(f"获取用户部署记录失败: {str(e)}")
        return []

def get_deployments_by_challenge(challenge_id, limit=10, offset=0):
    """获取题目的部署记录列表
    
    Args:
        challenge_id: 题目ID
        limit: 返回结果数量限制
        offset: 结果偏移量
        
    Returns:
        List[Dict]: 部署记录字典列表
    """
    try:
        from app.models.database.models import DeploymentRecord
        
        records = DeploymentRecord.query.filter_by(challenge_id=challenge_id) \
            .order_by(DeploymentRecord.created_at.desc()) \
            .offset(offset).limit(limit).all()
            
        return [record.to_dict() for record in records]
    except Exception as e:
        print(f"获取题目部署记录失败: {str(e)}")
        return []

def update_deployment_status(deployment_uuid, status, **kwargs):
    """更新部署状态
    
    Args:
        deployment_uuid: 部署UUID
        status: 新状态
        **kwargs: 其他要更新的字段（包括message用于日志）
        
    Returns:
        bool: 更新是否成功
    """
    try:
        from app.models.database.models import DeploymentRecord, db
        
        # 查找记录
        record = DeploymentRecord.query.filter_by(deployment_uuid=deployment_uuid).first()
        if not record:
            return False
        
        # 更新状态
        old_status = record.status
        record.status = status
        
        # 提取message（如果存在），用于日志
        log_message = kwargs.pop('message', None)
        
        # 更新其他字段
        for key, value in kwargs.items():
            if hasattr(record, key):
                setattr(record, key, value)
        
        # 添加状态日志
        if old_status != status:
            # 如果提供了message，使用它；否则使用默认消息
            if not log_message:
                log_message = f"状态从 {old_status} 变更为 {status}"
            # 添加状态日志（使用新的 JSON 字段）
            record.add_status_log(status, log_message)
        
        # 提交更改
        db.session.commit()
        return True
    except Exception as e:
        db.session.rollback()
        print(f"更新部署状态失败: {str(e)}")
        return False

def delete_deployment_record(deployment_uuid):
    """删除部署记录
    
    Args:
        deployment_uuid: 部署UUID
        
    Returns:
        bool: 删除是否成功
    """
    try:
        from app.models.database.models import DeploymentRecord, db
        
        # 查找记录
        record = DeploymentRecord.query.filter_by(deployment_uuid=deployment_uuid).first()
        if not record:
            return False
        
        # 删除记录
        db.session.delete(record)
        db.session.commit()
        return True
    except Exception as e:
        db.session.rollback()
        print(f"删除部署记录失败: {str(e)}")
        return False

def add_deployment_log(deployment_uuid, status, message=None):
    """添加部署状态日志
    
    Args:
        deployment_uuid: 部署UUID
        status: 状态
        message: 消息
        
    Returns:
        DeploymentStatusLog: 创建的日志对象，如果失败则返回None
    """
    try:
        from app.models.database.models import DeploymentRecord, db
        
        # 查找记录
        record = DeploymentRecord.query.filter_by(deployment_uuid=deployment_uuid).first()
        if not record:
            return None
        
        # 添加状态日志（使用新的 JSON 字段）
        record.add_status_log(status, message)
        db.session.commit()
        
        # 返回最后一条日志
        history = record.get_status_history()
        return history[-1] if history else None
    except Exception as e:
        db.session.rollback()
        print(f"添加部署日志失败: {str(e)}")
        return None

def get_deployment_logs(deployment_uuid, limit=20):
    """获取部署日志
    
    Args:
        deployment_uuid: 部署UUID
        limit: 返回结果数量限制
        
    Returns:
        List[Dict]: 日志字典列表
    """
    try:
        from app.models.database.models import DeploymentRecord
        from datetime import datetime
        
        # 查找记录
        record = DeploymentRecord.query.filter_by(deployment_uuid=deployment_uuid).first()
        if not record:
            return []
        
        # 获取状态历史
        history = record.get_status_history()
        
        # 转换为字典格式（兼容旧格式）
        logs = []
        for i, log_entry in enumerate(history):
            # 解析时间戳
            timestamp = log_entry.get('timestamp')
            if isinstance(timestamp, str):
                try:
                    timestamp = datetime.fromisoformat(timestamp.replace('Z', '+00:00'))
                except:
                    timestamp = datetime.now()
            elif not isinstance(timestamp, datetime):
                timestamp = datetime.now()
            
            logs.append({
                'id': i + 1,  # 使用索引作为ID
                'deployment_id': record.id,
                'status': log_entry.get('status', ''),
                'message': log_entry.get('message', ''),
                'timestamp': timestamp
            })
        
        # 按时间倒序排列，并限制数量
        logs.sort(key=lambda x: x['timestamp'], reverse=True)
        return logs[:limit]
    except Exception as e:
        print(f"获取部署日志失败: {str(e)}")
        import traceback
        traceback.print_exc()
        return []

def cleanup_expired_deployments():
    """清理过期的部署记录
    
    Returns:
        int: 清理的记录数量
    """
    try:
        from app.models.database.models import DeploymentRecord, db
        from datetime import datetime, timezone

        # 查找过期记录
        now = datetime.now(timezone.utc)
        expired = DeploymentRecord.query.filter(
            DeploymentRecord.expires_at < now,
            DeploymentRecord.status != 'deleted'
        ).all()
        
        # 更新状态为已删除
        count = 0
        for record in expired:
            record.status = 'deleted'
            count += 1
        
        db.session.commit()
        return count
    except Exception as e:
        db.session.rollback()
        print(f"清理过期部署记录失败: {str(e)}")
        return 0 