"""
oc-collab CLI封装 - M22

功能: 调用oc-collab命令查询项目状态

依赖:
- oc-collab v2.3.3

"""

import subprocess
import json
import logging
import os
import shutil
from datetime import datetime
from typing import Optional, List, Dict, Any
from dataclasses import dataclass, field
from enum import Enum


logger = logging.getLogger(__name__)


class OcCollabError(Exception):
    """oc-collab调用异常"""
    
    def __init__(self, message: str, error_type: str = None, raw_output: str = None):
        super().__init__(message)
        self.error_type = error_type
        self.raw_output = raw_output


class OcCollabTimeoutError(OcCollabError):
    """CLI调用超时"""


class OcCollabAuthError(OcCollabError):
    """认证失败"""


class OcCollabNotFoundError(OcCollabError):
    """项目不存在"""


class OcCollabParseError(OcCollabError):
    """JSON解析失败"""


class SyncStatus(str, Enum):
    """同步状态"""
    PENDING = "pending"
    IN_PROGRESS = "in_progress"
    COMPLETED = "completed"


@dataclass
class Todo:
    """TODO项"""
    id: str
    title: str
    status: str
    priority: str
    assignee: str = None
    created_at: datetime = None
    completed_at: datetime = None
    content: Dict = field(default_factory=dict)


@dataclass
class Bug:
    """BUG项"""
    id: str
    title: str
    status: str
    severity: str
    description: str = None
    assignee: str = None
    created_at: datetime = None
    resolved_at: datetime = None
    content: Dict = field(default_factory=dict)


@dataclass
class Requirement:
    """需求项"""
    id: str
    title: str
    status: str
    priority: str
    background: str = None
    user_scenario: str = None
    estimated_hours: float = None
    created_at: datetime = None
    implemented_at: datetime = None
    content: Dict = field(default_factory=dict)


@dataclass
class Change:
    """变更记录"""
    id: str
    change_type: str
    title: str
    old_status: str
    new_status: str
    changed_at: datetime
    description: str = None
    content: Dict = field(default_factory=dict)


@dataclass
class ProjectProgress:
    """项目进度"""
    project_name: str
    requirements_total: int = 0
    requirements_completed: int = 0
    requirements_in_progress: int = 0
    bugs_total: int = 0
    bugs_resolved: int = 0
    todos_total: int = 0
    todos_completed: int = 0
    progress_percentage: int = 0
    updated_at: datetime = None


@dataclass
class ProjectStatus:
    """项目状态"""
    name: str
    status: str
    progress: int
    last_activity: datetime = None
    details: Dict = field(default_factory=dict)


class OcCollabClient:
    """oc-collab CLI封装"""
    
    def __init__(self, timeout: int = 30, max_retries: int = 3):
        """
        初始化
        
        Args:
            timeout: CLI调用超时时间（秒）
            max_retries: 最大重试次数
        """
        self.env = {"OC_COLLAB_INTERNAL": "PM-Agent"}
        self.timeout = timeout
        self.max_retries = max_retries
        self.logger = logging.getLogger(__name__)
        self._cli_path: Optional[str] = None
    
    def _find_cli(self) -> Optional[str]:
        """查找oc-collab CLI路径"""
        if self._cli_path:
            return self._cli_path
        
        cli_names = ['oc-collab', 'oc_collab']
        for name in cli_names:
            cli_path = shutil.which(name)
            if cli_path:
                self._cli_path = cli_path
                return cli_path
        
        return None
    
    def is_cli_available(self) -> bool:
        """检查CLI是否可用"""
        return self._find_cli() is not None
    
    def _call_cli(self, args: List[str], timeout: int = None) -> Dict:
        """
        调用CLI并处理异常
        
        Args:
            args: CLI参数列表
            timeout: 超时时间（秒）
            
        Returns:
            Dict: 解析后的JSON响应
            
        Raises:
            OcCollabNotFoundError: CLI未找到
            OcCollabTimeoutError: 调用超时
            OcCollabAuthError: 认证失败
            OcCollabParseError: JSON解析失败
            OcCollabError: 其他错误
        """
        cli_path = self._find_cli()
        if not cli_path:
            raise OcCollabNotFoundError(
                "oc-collab CLI未安装或不在PATH中",
                error_type="not_found"
            )
        
        cmd = [cli_path] + args
        timeout = timeout or self.timeout
        
        self.logger.debug(f"执行命令: {' '.join(cmd)}")
        
        try:
            result = subprocess.run(
                cmd,
                capture_output=True,
                text=True,
                timeout=timeout,
                env={**self.env, **os.environ.copy()},
                cwd=None
            )
            
            self.logger.debug(f"命令返回码: {result.returncode}")
            self.logger.debug(f"stdout: {result.stdout[:500] if result.stdout else '(empty)'}")
            if result.stderr:
                self.logger.debug(f"stderr: {result.stderr[:500]}")
            
            if result.returncode != 0:
                error_msg = result.stderr.strip() if result.stderr else "未知错误"
                
                if "not found" in error_msg.lower() or "not installed" in error_msg.lower():
                    raise OcCollabNotFoundError(error_msg, error_type="not_found", raw_output=error_msg)
                elif "authentication" in error_msg.lower() or "auth" in error_msg.lower():
                    raise OcCollabAuthError(error_msg, error_type="auth", raw_output=error_msg)
                elif "timeout" in error_msg.lower():
                    raise OcCollabTimeoutError(error_msg, error_type="timeout", raw_output=error_msg)
                else:
                    raise OcCollabError(error_msg, error_type="execution", raw_output=error_msg)
            
            try:
                if result.stdout.strip():
                    return json.loads(result.stdout)
                return {}
            except json.JSONDecodeError as e:
                raise OcCollabParseError(
                    f"JSON解析失败: {e}",
                    error_type="parse",
                    raw_output=result.stdout
                )
                
        except subprocess.TimeoutExpired:
            raise OcCollabTimeoutError(
                f"命令执行超时 ({timeout}秒)",
                error_type="timeout"
            )
        except FileNotFoundError:
            raise OcCollabNotFoundError(
                f"CLI命令未找到: {cmd[0]}",
                error_type="not_found"
            )
    
    def get_project_status(self, project_name: str) -> ProjectStatus:
        """
        获取项目状态
        
        调用: oc-collab project <name> status --json
        
        Args:
            project_name: 项目名称
            
        Returns:
            ProjectStatus: 项目状态
        """
        args = ['project', project_name, 'status', '--json']
        
        try:
            result = self._call_cli(args)
            return ProjectStatus(
                name=project_name,
                status=result.get('status', 'unknown'),
                progress=result.get('progress', 0),
                last_activity=self._parse_datetime(result.get('last_activity')),
                details=result
            )
        except OcCollabNotFoundError:
            raise
        except OcCollabError as e:
            if e.error_type == "execution" and "not found" in str(e).lower():
                raise OcCollabNotFoundError(f"项目 '{project_name}' 不存在", error_type="not_found")
            raise
    
    def get_project_todos(self, project_name: str, status: str = None) -> List[Todo]:
        """
        获取TODO列表
        
        调用: oc-collab project <name> todos --json
        
        Args:
            project_name: 项目名称
            status: 过滤状态（pending/completed/all）
            
        Returns:
            List[Todo]: TODO列表
        """
        args = ['project', project_name, 'todos', '--json']
        if status and status != 'all':
            args.extend(['--status', status])
        
        result = self._call_cli(args)
        todos_data = result.get('todos', [])
        
        todos = []
        for item in todos_data:
            todos.append(Todo(
                id=item.get('id', ''),
                title=item.get('title', ''),
                status=item.get('status', 'pending'),
                priority=item.get('priority', 'medium'),
                assignee=item.get('assignee'),
                created_at=self._parse_datetime(item.get('created_at')),
                completed_at=self._parse_datetime(item.get('completed_at')),
                content=item
            ))
        
        return todos
    
    def get_project_progress(self, project_name: str) -> ProjectProgress:
        """
        获取项目进度
        
        调用: oc-collab project <name> progress --json
        
        Args:
            project_name: 项目名称
            
        Returns:
            ProjectProgress: 项目进度
        """
        args = ['project', project_name, 'progress', '--json']
        
        result = self._call_cli(args)
        
        return ProjectProgress(
            project_name=project_name,
            requirements_total=result.get('requirements_total', 0),
            requirements_completed=result.get('requirements_completed', 0),
            requirements_in_progress=result.get('requirements_in_progress', 0),
            bugs_total=result.get('bugs_total', 0),
            bugs_resolved=result.get('bugs_resolved', 0),
            todos_total=result.get('todos_total', 0),
            todos_completed=result.get('todos_completed', 0),
            progress_percentage=result.get('progress_percentage', 0),
            updated_at=datetime.now()
        )
    
    def get_changes(self, project_name: str, since: str = None) -> List[Change]:
        """
        获取变更记录
        
        调用: oc-collab project <name> changes --since=<timestamp> --json
        
        Args:
            project_name: 项目名称
            since: ISO格式时间戳
            
        Returns:
            List[Change]: 变更列表
        """
        args = ['project', project_name, 'changes', '--json']
        if since:
            args.extend([f'--since={since}'])
        
        result = self._call_cli(args)
        changes_data = result.get('changes', [])
        
        changes = []
        for item in changes_data:
            changes.append(Change(
                id=item.get('id', ''),
                change_type=item.get('type', ''),
                title=item.get('title', ''),
                old_status=item.get('old_status', ''),
                new_status=item.get('new_status', ''),
                changed_at=self._parse_datetime(item.get('changed_at')),
                description=item.get('description'),
                content=item
            ))
        
        return changes
    
    def get_project_bugs(self, project_name: str, status: str = None) -> List[Bug]:
        """
        获取项目BUG列表
        
        Args:
            project_name: 项目名称
            status: 过滤状态
            
        Returns:
            List[Bug]: BUG列表
        """
        args = ['project', project_name, 'bugs', '--json']
        if status:
            args.extend(['--status', status])
        
        result = self._call_cli(args)
        bugs_data = result.get('bugs', [])
        
        bugs = []
        for item in bugs_data:
            bugs.append(Bug(
                id=item.get('id', ''),
                title=item.get('title', ''),
                status=item.get('status', 'open'),
                severity=item.get('severity', 'medium'),
                description=item.get('description'),
                assignee=item.get('assignee'),
                created_at=self._parse_datetime(item.get('created_at')),
                resolved_at=self._parse_datetime(item.get('resolved_at')),
                content=item
            ))
        
        return bugs
    
    def get_project_requirements(self, project_name: str, status: str = None) -> List[Requirement]:
        """
        获取项目需求列表
        
        Args:
            project_name: 项目名称
            status: 过滤状态
            
        Returns:
            List[Requirement]: 需求列表
        """
        args = ['project', project_name, 'requirements', '--json']
        if status:
            args.extend(['--status', status])
        
        result = self._call_cli(args)
        reqs_data = result.get('requirements', [])
        
        requirements = []
        for item in reqs_data:
            requirements.append(Requirement(
                id=item.get('id', ''),
                title=item.get('title', ''),
                status=item.get('status', 'draft'),
                priority=item.get('priority', 'medium'),
                background=item.get('background'),
                user_scenario=item.get('user_scenario'),
                estimated_hours=item.get('estimated_hours'),
                created_at=self._parse_datetime(item.get('created_at')),
                implemented_at=self._parse_datetime(item.get('implemented_at')),
                content=item
            ))
        
        return requirements
    
    def list_projects(self) -> List[Dict]:
        """
        获取项目列表
        
        调用: oc-collab project list --json
        
        Returns:
            List[Dict]: 项目列表
        """
        args = ['project', 'list', '--json']
        result = self._call_cli(args)
        return result.get('projects', [])
    
    def sync_project(self, project_name: str) -> Dict:
        """
        触发项目同步
        
        Args:
            project_name: 项目名称
            
        Returns:
            Dict: 同步结果
        """
        args = ['project', project_name, 'sync', '--json']
        return self._call_cli(args)
    
    def _parse_datetime(self, value: str) -> Optional[datetime]:
        """解析datetime字符串"""
        if not value:
            return None
        try:
            if 'Z' in value:
                value = value.replace('Z', '+00:00')
            return datetime.fromisoformat(value)
        except (ValueError, AttributeError):
            return None


