"""
Knowledge management system for Squad AI Compound Interest.

Provides CRUD operations for knowledge items with value assessment integration.
"""

from pathlib import Path
from typing import Dict, Any, Optional, List
from datetime import datetime

from .file_utils import (
    get_knowledge_base_path,
    read_knowledge_file,
    write_knowledge_file,
    list_knowledge_files,
    ensure_dir
)
from .value_assessor import ValueAssessor
from .templates import create_template


class KnowledgeManager:
    """Manage knowledge items with CRUD operations and value assessment."""

    # Mapping from knowledge type to directory name
    TYPE_TO_DIR = {
        'skill': 'skills',
        'rule': 'rules',
        'pattern': 'patterns',
        'domain': 'domain',
        'user': 'user',
        'lesson': 'lessons'
    }

    def __init__(self):
        """Initialize knowledge manager."""
        self.base_path = get_knowledge_base_path()
        self.assessor = ValueAssessor()

        # Ensure directory structure exists
        self._ensure_directories()

    def _ensure_directories(self) -> None:
        """Ensure all knowledge type directories exist."""
        for subdir in self.TYPE_TO_DIR.values():
            ensure_dir(self.base_path / subdir)

    def create(
        self,
        knowledge_type: str,
        title: str,
        content: str,
        tags: Optional[List[str]] = None,
        keywords: Optional[List[str]] = None,
        factors: Optional[Dict[str, float]] = None,
        metadata: Optional[Dict[str, Any]] = None,
        source: str = "manual"
    ) -> str:
        """
        Create a new knowledge item.

        Args:
            knowledge_type: Type (skill, rule, pattern, domain, user, lesson)
            title: Brief title
            content: Markdown content
            tags: List of tags
            keywords: List of keywords
            factors: Optional pre-computed factor scores
            metadata: Optional metadata for special rules
            source: Source of knowledge (conversation, manual, import)

        Returns:
            str: ID of created knowledge item

        Raises:
            ValueError: If knowledge_type is invalid
        """
        # Create template
        template = create_template(
            knowledge_type=knowledge_type,
            title=title,
            content=content,
            tags=tags,
            keywords=keywords,
            source=source
        )

        # Generate frontmatter and content
        frontmatter, content_body = template.to_dict()

        # Assess value if factors provided
        if factors:
            score = self.assessor.assess(factors, metadata)
            frontmatter['score'] = score
            frontmatter['factors'] = factors

            # Add metadata if provided
            if metadata:
                frontmatter['metadata'] = metadata

        # Determine file path
        knowledge_id = frontmatter['id']
        dir_name = self.TYPE_TO_DIR.get(knowledge_type)
        if not dir_name:
            raise ValueError(f"Invalid knowledge type: {knowledge_type}")

        file_path = self.base_path / dir_name / f"{knowledge_id}.md"

        # Write file
        write_knowledge_file(file_path, frontmatter, content_body)

        return knowledge_id

    def read(self, knowledge_id: str) -> Optional[Dict[str, Any]]:
        """
        Read a knowledge item by ID.

        Args:
            knowledge_id: Knowledge item ID

        Returns:
            Optional[Dict[str, Any]]: Knowledge item data or None if not found
                Contains: frontmatter, content, file_path
        """
        # Search for file across all types
        for file_path in list_knowledge_files():
            if knowledge_id in file_path.name:
                frontmatter, content = read_knowledge_file(file_path)
                return {
                    'frontmatter': frontmatter,
                    'content': content,
                    'file_path': str(file_path)
                }

        return None

    def update(
        self,
        knowledge_id: str,
        title: Optional[str] = None,
        content: Optional[str] = None,
        tags: Optional[List[str]] = None,
        keywords: Optional[List[str]] = None,
        factors: Optional[Dict[str, float]] = None,
        metadata: Optional[Dict[str, Any]] = None
    ) -> bool:
        """
        Update an existing knowledge item.

        Args:
            knowledge_id: Knowledge item ID
            title: New title (optional)
            content: New content (optional)
            tags: New tags (optional)
            keywords: New keywords (optional)
            factors: New factor scores (optional)
            metadata: New metadata (optional)

        Returns:
            bool: True if updated, False if not found
        """
        # Read existing item
        item = self.read(knowledge_id)
        if not item:
            return False

        frontmatter = item['frontmatter']
        content_body = item['content']
        file_path = item['file_path']

        # Update fields
        if title is not None:
            frontmatter['title'] = title

        if content is not None:
            content_body = content

        if tags is not None:
            frontmatter['tags'] = tags

        if keywords is not None:
            frontmatter['keywords'] = keywords

        # Update timestamp
        frontmatter['updated'] = datetime.now().isoformat()

        # Reassess value if factors changed
        if factors is not None:
            existing_metadata = frontmatter.get('metadata', {})
            if metadata:
                existing_metadata.update(metadata)

            score = self.assessor.assess(factors, existing_metadata)
            frontmatter['score'] = score
            frontmatter['factors'] = factors

            if existing_metadata:
                frontmatter['metadata'] = existing_metadata
        elif metadata is not None:
            # Update metadata only
            existing_metadata = frontmatter.get('metadata', {})
            existing_metadata.update(metadata)
            frontmatter['metadata'] = existing_metadata

            # Recalculate score with new metadata
            if 'factors' in frontmatter:
                score = self.assessor.assess(
                    frontmatter['factors'],
                    existing_metadata
                )
                frontmatter['score'] = score

        # Write updated file
        write_knowledge_file(file_path, frontmatter, content_body)

        return True

    def delete(self, knowledge_id: str) -> bool:
        """
        Delete a knowledge item.

        Args:
            knowledge_id: Knowledge item ID

        Returns:
            bool: True if deleted, False if not found
        """
        item = self.read(knowledge_id)
        if not item:
            return False

        file_path = Path(item['file_path'])
        file_path.unlink()

        return True

    def list(
        self,
        knowledge_type: Optional[str] = None,
        min_score: Optional[float] = None,
        tags: Optional[List[str]] = None,
        limit: Optional[int] = None
    ) -> List[Dict[str, Any]]:
        """
        List knowledge items with optional filters.

        Args:
            knowledge_type: Filter by type (optional)
            min_score: Minimum score threshold (optional)
            tags: Filter by tags (must match at least one, optional)
            limit: Maximum number of items to return (optional)

        Returns:
            List[Dict[str, Any]]: List of knowledge items
        """
        # Get all files
        files = list_knowledge_files(knowledge_type)

        items = []
        for file_path in files:
            try:
                frontmatter, content = read_knowledge_file(file_path)

                # Apply filters
                if min_score is not None:
                    if frontmatter.get('score', 0.0) < min_score:
                        continue

                if tags:
                    item_tags = set(frontmatter.get('tags', []))
                    if not any(tag in item_tags for tag in tags):
                        continue

                # Add to results
                items.append({
                    'frontmatter': frontmatter,
                    'content': content,
                    'file_path': str(file_path)
                })

            except Exception:
                # Skip invalid files
                continue

        # Sort by score (descending)
        items.sort(key=lambda x: x['frontmatter'].get('score', 0.0), reverse=True)

        # Apply limit
        if limit:
            items = items[:limit]

        return items

    def search(
        self,
        query: str,
        knowledge_type: Optional[str] = None,
        search_content: bool = True
    ) -> List[Dict[str, Any]]:
        """
        Search knowledge items by keyword.

        Args:
            query: Search query
            knowledge_type: Filter by type (optional)
            search_content: Whether to search in content (default: True)

        Returns:
            List[Dict[str, Any]]: List of matching knowledge items
        """
        query_lower = query.lower()
        items = []

        for file_path in list_knowledge_files(knowledge_type):
            try:
                frontmatter, content = read_knowledge_file(file_path)

                # Check title
                if query_lower in frontmatter.get('title', '').lower():
                    items.append({
                        'frontmatter': frontmatter,
                        'content': content,
                        'file_path': str(file_path),
                        'match_type': 'title'
                    })
                    continue

                # Check keywords
                keywords = frontmatter.get('keywords', [])
                if any(query_lower in kw.lower() for kw in keywords):
                    items.append({
                        'frontmatter': frontmatter,
                        'content': content,
                        'file_path': str(file_path),
                        'match_type': 'keyword'
                    })
                    continue

                # Check tags
                tags = frontmatter.get('tags', [])
                if any(query_lower in tag.lower() for tag in tags):
                    items.append({
                        'frontmatter': frontmatter,
                        'content': content,
                        'file_path': str(file_path),
                        'match_type': 'tag'
                    })
                    continue

                # Check content
                if search_content and query_lower in content.lower():
                    items.append({
                        'frontmatter': frontmatter,
                        'content': content,
                        'file_path': str(file_path),
                        'match_type': 'content'
                    })

            except Exception:
                continue

        # Sort by score
        items.sort(key=lambda x: x['frontmatter'].get('score', 0.0), reverse=True)

        return items

    def increment_usage(self, knowledge_id: str) -> bool:
        """
        Increment usage count and update last_used timestamp.

        Args:
            knowledge_id: Knowledge item ID

        Returns:
            bool: True if updated, False if not found
        """
        item = self.read(knowledge_id)
        if not item:
            return False

        frontmatter = item['frontmatter']

        # Update metadata
        metadata = frontmatter.get('metadata', {})
        metadata['usage_count'] = metadata.get('usage_count', 0) + 1
        metadata['last_used'] = datetime.now().isoformat()

        # Recalculate score
        factors = frontmatter.get('factors')
        if factors:
            score = self.assessor.assess(factors, metadata)
            frontmatter['score'] = score
            frontmatter['metadata'] = metadata
            frontmatter['updated'] = datetime.now().isoformat()

            # Write updated file
            write_knowledge_file(
                item['file_path'],
                frontmatter,
                item['content']
            )

        return True
