"""
文档拉取服务 - M26

功能: 从oc-collab项目拉取开发文档

"""

import os
import logging
from datetime import datetime
from typing import List, Dict, Optional
from dataclasses import dataclass


logger = logging.getLogger(__name__)


@dataclass
class Document:
    """文档"""
    name: str
    path: str
    category: str
    file_type: str
    size: int
    content: str = None
    modified_at: datetime = None
    metadata: Dict = None


@dataclass
class DocumentCategory:
    """文档分类"""
    name: str
    path: str
    document_count: int = 0
    subcategories: List['DocumentCategory'] = None


class DocumentFetcher:
    """文档拉取服务"""
    
    def __init__(self, base_path: str = None):
        """
        初始化
        
        Args:
            base_path: 项目仓库根目录
        """
        self.base_path = base_path
        self.logger = logging.getLogger(__name__)
        self._supported_extensions = {
            '.md', '.txt', '.rst', '.json', '.yaml', '.yml',
            '.py', '.js', '.ts', '.html', '.css'
        }
        self._ignore_patterns = {
            '.git', '.gitignore', '.gitattributes',
            'node_modules', '__pycache__', '.venv', 'venv',
            '*.pyc', '*.pyo', '*.so', '*.egg-info',
            '.DS_Store', 'Thumbs.db'
        }
    
    def fetch_docs(self, project_path: str, recursive: bool = True) -> List[Document]:
        """
        从项目仓库拉取文档
        
        Args:
            project_path: 项目路径
            recursive: 是否递归搜索
            
        Returns:
            List[Document]: 文档列表
        """
        if not os.path.exists(project_path):
            self.logger.warning(f"项目路径不存在: {project_path}")
            return []
        
        docs = []
        
        for root, dirs, files in os.walk(project_path):
            if not recursive:
                break
            
            dirs[:] = [d for d in dirs if not self._should_ignore(d)]
            
            for file in files:
                if self._should_ignore(file):
                    continue
                
                file_path = os.path.join(root, file)
                ext = os.path.splitext(file)[1].lower()
                
                if ext in self._supported_extensions:
                    doc = self._create_document(file_path, project_path)
                    if doc:
                        docs.append(doc)
        
        self.logger.info(f"从 {project_path} 拉取 {len(docs)} 个文档")
        return docs
    
    def _should_ignore(self, name: str) -> bool:
        """判断是否应该忽略"""
        if name.startswith('.'):
            return True
        if name in self._ignore_patterns:
            return True
        for pattern in self._ignore_patterns:
            if pattern.startswith('*') and name.endswith(pattern[1:]):
                return True
        return False
    
    def _create_document(self, file_path: str, project_path: str) -> Optional[Document]:
        """创建文档对象"""
        try:
            rel_path = os.path.relpath(file_path, project_path)
            
            ext = os.path.splitext(file_path)[1].lower()
            file_type = ext[1:] if ext else 'text'
            
            category = self._get_category(rel_path)
            
            stat = os.stat(file_path)
            modified_at = datetime.fromtimestamp(stat.st_mtime)
            
            content = None
            if stat.st_size < 1024 * 1024:
                try:
                    with open(file_path, 'r', encoding='utf-8', errors='ignore') as f:
                        content = f.read()
                except Exception as e:
                    self.logger.debug(f"读取文件失败: {file_path}, {e}")
            
            return Document(
                name=os.path.basename(file_path),
                path=rel_path,
                category=category,
                file_type=file_type,
                size=stat.st_size,
                content=content,
                modified_at=modified_at
            )
        except Exception as e:
            self.logger.error(f"创建文档对象失败: {file_path}, {e}")
            return None
    
    def _get_category(self, path: str) -> str:
        """获取文档分类"""
        parts = path.split(os.sep)
        
        if len(parts) > 1:
            category_map = {
                'docs': '文档',
                'README': '文档',
                'readme': '文档',
                'src': '源代码',
                'backend': '后端',
                'frontend': '前端',
                'tests': '测试',
                'test': '测试',
                'config': '配置',
                'scripts': '脚本',
                'scripts': '脚本'
            }
            
            if parts[0] in category_map:
                return category_map[parts[0]]
            
            if 'docs' in parts:
                idx = parts.index('docs')
                if idx + 1 < len(parts):
                    return parts[idx + 1]
        
        if path.endswith('.md') or path.endswith('.txt'):
            return '文档'
        elif path.endswith('.py'):
            return '源代码'
        elif path.endswith(('.js', '.ts', '.vue')):
            return '前端'
        elif path.endswith(('.json', '.yaml', '.yml')):
            return '配置'
        
        return '其他'
    
    def search(self, keyword: str, project_name: str = None, doc_type: str = None) -> List[Document]:
        """
        搜索文档
        
        Args:
            keyword: 关键字
            project_name: 项目名称（可选）
            doc_type: 文档类型过滤
            
        Returns:
            List[Document]: 匹配的文档
        """
        if not project_name:
            return []
        
        project_path = self.base_path
        if project_name and self.base_path:
            project_path = os.path.join(self.base_path, project_name)
        
        if not os.path.exists(project_path):
            return []
        
        docs = self.fetch_docs(project_path)
        
        results = []
        keyword_lower = keyword.lower()
        
        for doc in docs:
            if doc_type and doc.category != doc_type:
                continue
            
            if keyword_lower in (doc.name or '').lower():
                results.append(doc)
            elif keyword_lower in (doc.path or '').lower():
                results.append(doc)
            elif doc.content and keyword_lower in doc.content.lower():
                results.append(doc)
        
        self.logger.info(f"搜索 '{keyword}' 在项目 '{project_name}' 中找到 {len(results)} 个结果")
        return results
    
    def get_doc_content(self, project_name: str, doc_path: str) -> Optional[str]:
        """
        获取文档内容
        
        Args:
            project_name: 项目名称
            doc_path: 文档路径
            
        Returns:
            str: 文档内容
        """
        if not self.base_path:
            return None
        
        project_path = os.path.join(self.base_path, project_name)
        full_path = os.path.join(project_path, doc_path)
        
        if not os.path.exists(full_path):
            self.logger.warning(f"文档不存在: {full_path}")
            return None
        
        try:
            with open(full_path, 'r', encoding='utf-8', errors='ignore') as f:
                return f.read()
        except Exception as e:
            self.logger.error(f"读取文档失败: {full_path}, {e}")
            return None
    
    def list_doc_categories(self, project_path: str) -> List[DocumentCategory]:
        """
        列出文档分类
        
        Args:
            project_path: 项目路径
            
        Returns:
            List[DocumentCategory]: 文档分类列表
        """
        if not os.path.exists(project_path):
            return []
        
        categories = {}
        
        docs = self.fetch_docs(project_path)
        
        for doc in docs:
            if doc.category not in categories:
                categories[doc.category] = DocumentCategory(
                    name=doc.category,
                    path=doc.path.split(os.sep)[0] if os.sep in doc.path else '',
                    document_count=0,
                    subcategories=[]
                )
            categories[doc.category].document_count += 1
        
        return list(categories.values())
    
    def list_api_docs(self, project_path: str) -> List[Document]:
        """
        列出API文档
        
        Args:
            project_path: 项目路径
            
        Returns:
            List[Document]: API文档列表
        """
        if not os.path.exists(project_path):
            return []
        
        api_patterns = ['api', 'API', 'doc', 'DOC', 'docs', 'DOCS']
        
        docs = self.fetch_docs(project_path)
        
        api_docs = []
        for doc in docs:
            path_lower = doc.path.lower()
            for pattern in api_patterns:
                if pattern.lower() in path_lower:
                    api_docs.append(doc)
                    break
        
        return api_docs
    
    def list_requirement_docs(self, project_path: str) -> List[Document]:
        """
        列出需求文档
        
        Args:
            project_path: 项目路径
            
        Returns:
            List[Document]: 需求文档列表
        """
        if not os.path.exists(project_path):
            return []
        
        req_patterns = ['requirements', 'requirement', 'REQ', 'req', '需求']
        
        docs = self.fetch_docs(project_path)
        
        req_docs = []
        for doc in docs:
            path_lower = doc.path.lower()
            for pattern in req_patterns:
                if pattern.lower() in path_lower:
                    req_docs.append(doc)
                    break
        
        return req_docs
    
    def list_design_docs(self, project_path: str) -> List[Document]:
        """
        列出设计文档
        
        Args:
            project_path: 项目路径
            
        Returns:
            List[Document]: 设计文档列表
        """
        if not os.path.exists(project_path):
            return []
        
        design_patterns = ['design', 'DESIGN', '设计', '架构', 'architecture']
        
        docs = self.fetch_docs(project_path)
        
        design_docs = []
        for doc in docs:
            path_lower = doc.path.lower()
            for pattern in design_patterns:
                if pattern.lower() in path_lower:
                    design_docs.append(doc)
                    break
        
        return design_docs
    
    def sync_to_database(self, project_name: str, db_session=None):
        """
        同步文档到数据库
        
        Args:
            project_name: 项目名称
            db_session: 数据库会话
        """
        if not db_session:
            return
        
        try:
            from backend.models.database import Project, Material
            
            project = db_session.query(Project).filter(Project.name == project_name).first()
            if not project:
                return
            
            project_path = os.path.join(self.base_path, project_name) if self.base_path else None
            if not project_path or not os.path.exists(project_path):
                return
            
            docs = self.fetch_docs(project_path)
            
            for doc in docs:
                existing = db_session.query(Material).filter(
                    Material.project_id == project.id,
                    Material.file_name == doc.name
                ).first()
                
                if not existing:
                    import uuid
                    material = Material(
                        id=str(uuid.uuid4()),
                        project_id=project.id,
                        file_name=doc.name,
                        file_type=doc.file_type,
                        file_hash='',
                        file_size=doc.size,
                        storage_path=doc.path,
                        status='processed',
                        processed_content=doc.content,
                        created_at=datetime.now()
                    )
                    db_session.add(material)
            
            db_session.commit()
            self.logger.info(f"同步 {len(docs)} 个文档到数据库")
            
        except Exception as e:
            self.logger.error(f"同步文档到数据库失败: {e}")
            db_session.rollback()
