"""
状态反馈服务 - M23

功能: 接收oc-collab状态变更通知

依赖:
- M22 OcCollabClient

"""

import logging
import threading
from datetime import datetime
from typing import List, Optional, Callable, Dict
from dataclasses import dataclass
from enum import Enum


logger = logging.getLogger(__name__)


class ChangeType(str, Enum):
    """变更类型"""
    BUG = "bug"
    REQUIREMENT = "requirement"
    TODO = "todo"
    PROGRESS = "progress"


@dataclass
class ChangeEvent:
    """变更事件"""
    project_name: str
    change_type: ChangeType
    change_id: str
    title: str
    old_status: str
    new_status: str
    old_value: Optional[str] = None
    new_value: Optional[str] = None
    description: Optional[str] = None
    changed_at: datetime = None
    source: str = "oc_collab"


class StatusFeedbackService:
    """状态反馈服务"""
    
    def __init__(self, client=None, storage=None):
        """
        初始化
        
        Args:
            client: OcCollabClient实例
            storage: 存储接口（可选）
        """
        self.client = client
        self.storage = storage
        self.last_check_time: Optional[datetime] = None
        self.polling_interval = 60
        self._polling = False
        self._polling_thread: Optional[threading.Thread] = None
        self._callbacks: List[Callable[[ChangeEvent], None]] = []
        self.logger = logging.getLogger(__name__)
    
    def register_callback(self, callback: Callable[[ChangeEvent], None]):
        """
        注册变更回调
        
        Args:
            callback: 回调函数，接收ChangeEvent参数
        """
        self._callbacks.append(callback)
    
    def unregister_callback(self, callback: Callable[[ChangeEvent], None]):
        """注销回调"""
        if callback in self._callbacks:
            self._callbacks.remove(callback)
    
    def _notify_callbacks(self, event: ChangeEvent):
        """通知所有回调"""
        for callback in self._callbacks:
            try:
                callback(event)
            except Exception as e:
                self.logger.error(f"回调函数执行失败: {e}")
    
    def poll_changes(self, project_name: str = None) -> List[ChangeEvent]:
        """
        轮询状态变更
        
        Args:
            project_name: 项目名称（可选，不指定则轮询所有项目）
            
        Returns:
            List[ChangeEvent]: 变更事件列表
        """
        if not self.client:
            self.logger.warning("OcCollabClient未初始化")
            return []
        
        changes = []
        try:
            since = self.last_check_time.isoformat() if self.last_check_time else None
            
            if project_name:
                project_names = [project_name]
            else:
                project_names = self._get_active_project_names()
            
            for name in project_names:
                try:
                    oc_changes = self.client.get_changes(name, since=since)
                    for oc_change in oc_changes:
                        event = self._parse_change(name, oc_change)
                        if event:
                            changes.append(event)
                except Exception as e:
                    self.logger.error(f"轮询项目 '{name}' 失败: {e}")
            
            self.last_check_time = datetime.now()
            self.logger.info(f"轮询完成，发现 {len(changes)} 个变更")
            
        except Exception as e:
            self.logger.error(f"轮询变更失败: {e}")
        
        return changes
    
    def _get_active_project_names(self) -> List[str]:
        """获取活跃项目名称列表"""
        try:
            if self.storage:
                return self.storage.get_active_project_names()
            return []
        except Exception as e:
            self.logger.error(f"获取活跃项目列表失败: {e}")
            return []
    
    def _parse_change(self, project_name: str, oc_change) -> Optional[ChangeEvent]:
        """
        解析oc-collab变更
        
        Args:
            project_name: 项目名称
            oc_change: oc-collab变更对象
            
        Returns:
            ChangeEvent: 变更事件
        """
        try:
            change_type_str = getattr(oc_change, 'change_type', None) or oc_change.get('change_type', 'unknown')
            
            try:
                change_type = ChangeType(change_type_str)
            except ValueError:
                change_type = ChangeType.PROGRESS
            
            return ChangeEvent(
                project_name=project_name,
                change_type=change_type,
                change_id=getattr(oc_change, 'id', None) or oc_change.get('id', ''),
                title=getattr(oc_change, 'title', None) or oc_change.get('title', ''),
                old_status=getattr(oc_change, 'old_status', None) or oc_change.get('old_status', ''),
                new_status=getattr(oc_change, 'new_status', None) or oc_change.get('new_status', ''),
                old_value=getattr(oc_change, 'old_value', None) or oc_change.get('old_value'),
                new_value=getattr(oc_change, 'new_value', None) or oc_change.get('new_value'),
                description=getattr(oc_change, 'description', None) or oc_change.get('description'),
                changed_at=getattr(oc_change, 'changed_at', None) or oc_change.get('changed_at'),
                source='oc_collab'
            )
        except Exception as e:
            self.logger.error(f"解析变更失败: {e}")
            return None
    
    def update_local_status(self, changes: List[ChangeEvent]) -> int:
        """
        更新本地状态
        
        Args:
            changes: 变更事件列表
            
        Returns:
            int: 更新的记录数
        """
        if not self.storage:
            self.logger.warning("存储接口未初始化")
            return 0
        
        updated_count = 0
        for change in changes:
            try:
                self.storage.save_change(change)
                updated_count += 1
                self._notify_callbacks(change)
            except Exception as e:
                self.logger.error(f"保存变更失败: {e}")
        
        return updated_count
    
    def start_polling(self, interval: int = None):
        """
        启动定时轮询
        
        Args:
            interval: 轮询间隔（秒）
        """
        if self._polling:
            self.logger.warning("轮询已在运行")
            return
        
        if interval:
            self.polling_interval = interval
        
        self._polling = True
        self._polling_thread = threading.Thread(
            target=self._polling_loop,
            daemon=True
        )
        self._polling_thread.start()
        self.logger.info(f"启动轮询，间隔: {self.polling_interval}秒")
    
    def _polling_loop(self):
        """轮询循环"""
        while self._polling:
            try:
                changes = self.poll_changes()
                if changes:
                    self.update_local_status(changes)
            except Exception as e:
                self.logger.error(f"轮询循环错误: {e}")
            
            import time
            time.sleep(self.polling_interval)
    
    def stop_polling(self):
        """停止轮询"""
        self._polling = False
        if self._polling_thread:
            self._polling_thread.join(timeout=5)
            self._polling_thread = None
        self.logger.info("轮询已停止")
    
    def process_change(self, change: ChangeEvent) -> bool:
        """
        处理单条变更
        
        Args:
            change: 变更事件
            
        Returns:
            bool: 是否处理成功
        """
        try:
            if self.storage:
                self.storage.save_change(change)
            
            self._notify_callbacks(change)
            self.logger.info(f"处理变更: {change.change_type.value} - {change.title}")
            return True
        except Exception as e:
            self.logger.error(f"处理变更失败: {e}")
            return False
    
    def get_change_history(self, project_name: str = None, limit: int = 100) -> List[ChangeEvent]:
        """
        获取变更历史
        
        Args:
            project_name: 项目名称（可选）
            limit: 返回数量限制
            
        Returns:
            List[ChangeEvent]: 变更历史列表
        """
        if not self.storage:
            return []
        
        try:
            return self.storage.get_change_history(project_name, limit)
        except Exception as e:
            self.logger.error(f"获取变更历史失败: {e}")
            return []
    
    def check_status_changes(self, project_name: str) -> Dict:
        """
        检查项目状态变更
        
        Args:
            project_name: 项目名称
            
        Returns:
            Dict: 状态变更信息
        """
        if not self.client:
            return {'error': 'OcCollabClient未初始化'}
        
        try:
            status = self.client.get_project_status(project_name)
            progress = self.client.get_project_progress(project_name)
            
            return {
                'project_name': project_name,
                'status': status.status,
                'progress': status.progress,
                'requirements': {
                    'total': progress.requirements_total,
                    'completed': progress.requirements_completed,
                    'in_progress': progress.requirements_in_progress
                },
                'bugs': {
                    'total': progress.bugs_total,
                    'resolved': progress.bugs_resolved
                },
                'todos': {
                    'total': progress.todos_total,
                    'completed': progress.todos_completed
                },
                'checked_at': datetime.now().isoformat()
            }
        except Exception as e:
            self.logger.error(f"检查状态变更失败: {e}")
            return {'error': str(e)}


class LocalStorageAdapter:
    """本地存储适配器"""
    
    def __init__(self, db_session):
        """
        初始化
        
        Args:
            db_session: 数据库会话
        """
        self.db = db_session
    
    def save_change(self, change: ChangeEvent) -> bool:
        """保存变更到数据库"""
        from backend.models.database import ChangeHistory
        
        try:
            change_record = ChangeHistory(
                project_id=self._get_project_id(change.project_name),
                change_type=change.change_type.value,
                change_id=change.change_id,
                change_title=change.title,
                old_status=change.old_status,
                new_status=change.new_status,
                old_value=str(change.old_value) if change.old_value else None,
                new_value=str(change.new_value) if change.new_value else None,
                change_description=change.description,
                changed_at=change.changed_at or datetime.now(),
                synced_at=datetime.now(),
                source='oc_collab'
            )
            self.db.add(change_record)
            self.db.commit()
            return True
        except Exception as e:
            self.logger.error(f"保存变更失败: {e}")
            self.db.rollback()
            return False
    
    def get_change_history(self, project_name: str = None, limit: int = 100) -> List[ChangeEvent]:
        """获取变更历史"""
        from backend.models.database import ChangeHistory, Project
        
        try:
            query = self.db.query(ChangeHistory)
            
            if project_name:
                project = self.db.query(Project).filter(Project.name == project_name).first()
                if project:
                    query = query.filter(ChangeHistory.project_id == project.id)
            
            records = query.order_by(ChangeHistory.changed_at.desc()).limit(limit).all()
            
            changes = []
            for record in records:
                try:
                    change_type = ChangeType(record.change_type)
                except ValueError:
                    change_type = ChangeType.PROGRESS
                
                changes.append(ChangeEvent(
                    project_name=project_name or '',
                    change_type=change_type,
                    change_id=record.change_id,
                    title=record.change_title or '',
                    old_status=record.old_status or '',
                    new_status=record.new_status or '',
                    old_value=record.old_value,
                    new_value=record.new_value,
                    description=record.change_description,
                    changed_at=record.changed_at,
                    source=record.source or 'oc_collab'
                ))
            
            return changes
        except Exception as e:
            self.logger.error(f"获取变更历史失败: {e}")
            return []
    
    def get_active_project_names(self) -> List[str]:
        """获取活跃项目名称"""
        from backend.models.database import Project
        
        try:
            projects = self.db.query(Project).filter(
                Project.status == 'active',
                Project.oc_collab_enabled == True
            ).all()
            return [p.name for p in projects]
        except Exception as e:
            self.logger.error(f"获取活跃项目失败: {e}")
            return []
    
    def _get_project_id(self, project_name: str) -> int:
        """获取项目ID"""
        from backend.models.database import Project
        
        project = self.db.query(Project).filter(Project.name == project_name).first()
        if project:
            return project.id
        return 0
