"""
Fanfic Writer v2.0 - Prompt Registry
Manages prompt templates with versioning and audit trail
"""
import json
from pathlib import Path
from typing import Dict, Any, Optional, List, Tuple
from dataclasses import dataclass

from .atomic_io import atomic_write_json
from .utils import get_timestamp_iso


@dataclass
class PromptTemplate:
    """A registered prompt template"""
    name: str
    path: Path
    version: str
    description: str
    source: str  # 'v1', 'v2_addons', or 'builtin'
    
    def to_dict(self) -> Dict[str, Any]:
        return {
            'name': self.name,
            'path': str(self.path),
            'version': self.version,
            'description': self.description,
            'source': self.source
        }


class PromptRegistry:
    """
    Manages prompt template registry
    
    Ensures v1.0 prompts are used for core functions (chapter_outline, chapter_draft)
    as required by design spec.
    """
    
    # Required templates with their expected sources
    REQUIRED_TEMPLATES = {
        'chapter_outline': {'source': 'v1', 'required': True},
        'chapter_draft': {'source': 'v1', 'required': True},
        'critic_editor': {'source': 'v2_addons', 'required': True},
        'critic_logic': {'source': 'v2_addons', 'required': True},
        'critic_continuity': {'source': 'v2_addons', 'required': True},
        'backpatch_plan': {'source': 'v2_addons', 'required': True},
        'sanitizer': {'source': 'v2_addons', 'required': True},
        'main_outline': {'source': 'v1', 'required': True},
        'chapter_plan': {'source': 'v1', 'required': True},
        'world_building': {'source': 'v1', 'required': True},
        'style_guide': {'source': 'v1', 'required': False},
        'qc_evaluate': {'source': 'v2_addons', 'required': True}
    }
    
    def __init__(self, run_dir: Path, skill_dir: Path):
        """
        Initialize PromptRegistry
        
        Args:
            run_dir: Current run directory
            skill_dir: Skill root directory (for accessing prompts/)
        """
        self.run_dir = Path(run_dir)
        self.skill_dir = Path(skill_dir)
        self.registry_path = self.run_dir / "4-state" / "prompt_registry.json"
        self.prompts_dir = self.skill_dir / "prompts"
        
        self._registry: Optional[Dict[str, Any]] = None
        self._templates: Dict[str, PromptTemplate] = {}
    
    def initialize(self, run_id: str, skill_version: str = "2.0.0") -> bool:
        """
        Initialize registry for a new run
        
        Returns:
            True on success
            
        Raises:
            RuntimeError: If required v1 templates are missing (blocking error)
        """
        # Discover available templates
        templates = self._discover_templates()
        
        # Validate required templates
        missing_required = []
        for name, spec in self.REQUIRED_TEMPLATES.items():
            if spec['required'] and name not in templates:
                missing_required.append(name)
        
        if missing_required:
            raise RuntimeError(
                f"Missing required prompt templates: {missing_required}. "
                f"Auto mode requires v1.0 templates for chapter_outline and chapter_draft."
            )
        
        # Verify audit chain capability (logs/prompts/ must be writable)
        audit_dir = self.run_dir / "logs" / "prompts"
        try:
            audit_dir.mkdir(parents=True, exist_ok=True)
            # Test write access
            test_file = audit_dir / ".write_test"
            test_file.write_text("test")
            test_file.unlink()
        except Exception as e:
            raise RuntimeError(
                f"CRITICAL: Cannot create prompt audit directory: {audit_dir}. "
                f"Audit chain is mandatory per design spec. Error: {e}"
            )
        
        # Build registry
        self._registry = {
            'run_id': run_id,
            'skill_version': skill_version,
            'initialized_at': get_timestamp_iso(),
            'templates': {name: tmpl.to_dict() for name, tmpl in templates.items()},
            'audit_chain': {
                'enabled': True,
                'directory': str(audit_dir),
                'mandatory': True  # Design spec: missing audit = fatal
            }
        }
        
        self._templates = templates
        
        # Save registry
        return atomic_write_json(self.registry_path, self._registry)
    
    def load(self) -> Dict[str, Any]:
        """Load existing registry"""
        if self._registry is not None:
            return self._registry
        
        if not self.registry_path.exists():
            raise FileNotFoundError(f"Prompt registry not found: {self.registry_path}")
        
        with open(self.registry_path, 'r', encoding='utf-8') as f:
            self._registry = json.load(f)
        
        # Rebuild template objects
        self._templates = {}
        for name, data in self._registry.get('templates', {}).items():
            self._templates[name] = PromptTemplate(
                name=data['name'],
                path=Path(data['path']),
                version=data['version'],
                description=data['description'],
                source=data['source']
            )
        
        return self._registry
    
    def _discover_templates(self) -> Dict[str, PromptTemplate]:
        """Discover all available prompt templates"""
        templates = {}
        
        # Check v1 prompts
        v1_dir = self.prompts_dir / "v1"
        if v1_dir.exists():
            for md_file in v1_dir.glob("*.md"):
                name = md_file.stem
                templates[name] = PromptTemplate(
                    name=name,
                    path=md_file,
                    version="1.0.0",
                    description=f"v1.0 template: {name}",
                    source="v1"
                )
        
        # Check v2 addons
        v2_dir = self.prompts_dir / "v2_addons"
        if v2_dir.exists():
            for md_file in v2_dir.glob("*.md"):
                name = md_file.stem
                # v2 overrides v1 if same name
                templates[name] = PromptTemplate(
                    name=name,
                    path=md_file,
                    version="2.0.0",
                    description=f"v2.0 addon: {name}",
                    source="v2_addons"
                )
        
        return templates
    
    def get_template(self, name: str) -> Optional[PromptTemplate]:
        """Get a template by name"""
        self.load()
        return self._templates.get(name)
    
    def get_template_content(self, name: str) -> Optional[str]:
        """Get the actual content of a template"""
        template = self.get_template(name)
        if not template:
            return None
        
        if not template.path.exists():
            return None
        
        with open(template.path, 'r', encoding='utf-8') as f:
            return f.read()
    
    def validate_for_auto_mode(self) -> Tuple[bool, List[str]]:
        """
        Validate that registry is ready for auto mode
        
        Returns:
            (is_valid, list_of_errors)
        """
        errors = []
        
        try:
            self.load()
        except FileNotFoundError as e:
            return False, [str(e)]
        
        # Check v1 templates for auto mode
        for name, spec in self.REQUIRED_TEMPLATES.items():
            if spec['source'] == 'v1' and spec['required']:
                template = self._templates.get(name)
                if not template:
                    errors.append(f"Missing required v1 template: {name}")
                elif template.source != 'v1':
                    errors.append(
                        f"Template {name} must be from v1 source for auto mode, "
                        f"got: {template.source}"
                    )
                elif not template.path.exists():
                    errors.append(f"Template file missing: {template.path}")
        
        return len(errors) == 0, errors
    
    def list_templates(self) -> List[PromptTemplate]:
        """List all registered templates"""
        self.load()
        return list(self._templates.values())
    
    def get_template_path(self, name: str) -> Optional[Path]:
        """Get the file path for a template"""
        template = self.get_template(name)
        return template.path if template else None


# ============================================================================
# Template Validation Helpers
# ============================================================================

def validate_template_content(content: str, template_type: str) -> Tuple[bool, List[str]]:
    """
    Validate that a template contains required placeholders
    
    Args:
        content: Template content
        template_type: Type of template (chapter_outline, chapter_draft, etc.)
        
    Returns:
        (is_valid, list_of_warnings)
    """
    warnings = []
    
    # Common placeholders all templates should have
    common_placeholders = ['{', '}']  # Basic check for placeholder syntax
    
    # Type-specific required placeholders
    type_placeholders = {
        'chapter_outline': [
            'previous_chapter_content',
            'chapter_summary',
            'chapter_title',
            'target_words'
        ],
        'chapter_draft': [
            'previous_chapter_content',
            'detailed_outline',
            'chapter_title',
            'segment_summary',
            'segment_words'
        ]
    }
    
    # Check for placeholder syntax
    if '{' not in content or '}' not in content:
        warnings.append("Template may be missing placeholder syntax {}")
    
    # Check type-specific placeholders
    required = type_placeholders.get(template_type, [])
    for placeholder in required:
        if placeholder not in content:
            warnings.append(f"Template may be missing required placeholder: {placeholder}")
    
    return len(warnings) == 0, warnings


# ============================================================================
# Default Template Generation (for initial setup)
# ============================================================================

def generate_default_templates(skill_dir: Path) -> bool:
    """
    Generate default prompt templates if they don't exist
    
    This is called during skill installation to ensure prompts/ directory
    is populated with default templates.
    """
    prompts_dir = Path(skill_dir) / "prompts"
    prompts_dir.mkdir(parents=True, exist_ok=True)
    
    v1_dir = prompts_dir / "v1"
    v1_dir.mkdir(parents=True, exist_ok=True)
    
    v2_dir = prompts_dir / "v2_addons"
    v2_dir.mkdir(parents=True, exist_ok=True)
    
    # Note: Actual template content should be written separately
    # This function just ensures directory structure
    
    return True


# ============================================================================
# Module Test
# ============================================================================

if __name__ == "__main__":
    import tempfile
    
    print("=== Prompt Registry Test ===\n")
    
    with tempfile.TemporaryDirectory() as tmpdir:
        # Create mock skill structure
        skill_dir = Path(tmpdir) / "skill"
        prompts_v1 = skill_dir / "prompts" / "v1"
        prompts_v1.mkdir(parents=True)
        
        # Create test templates
        (prompts_v1 / "chapter_outline.md").write_text("# Chapter Outline\n{chapter_title}")
        (prompts_v1 / "chapter_draft.md").write_text("# Chapter Draft\n{previous_chapter_content}")
        
        prompts_v2 = skill_dir / "prompts" / "v2_addons"
        prompts_v2.mkdir(parents=True)
        (prompts_v2 / "critic_editor.md").write_text("# Critic Editor\nAnalyze:")
        
        # Create run directory
        run_dir = Path(tmpdir) / "run"
        state_dir = run_dir / "4-state"
        state_dir.mkdir(parents=True)
        
        # Test registry initialization
        registry = PromptRegistry(run_dir, skill_dir)
        
        try:
            registry.initialize("20260215_224500_TEST", "2.0.0")
            print("[Test] Registry initialized: PASS")
        except RuntimeError as e:
            print(f"[Test] Registry initialized: FAIL - {e}")
        
        # Test load
        data = registry.load()
        print(f"[Test] Registry loaded: {len(data['templates'])} templates")
        
        # Test get template
        tmpl = registry.get_template("chapter_outline")
        print(f"[Test] Get template: {'PASS' if tmpl else 'FAIL'}")
        if tmpl:
            print(f"  Source: {tmpl.source}")
        
        # Test get content
        content = registry.get_template_content("chapter_outline")
        print(f"[Test] Get content: {'PASS' if content else 'FAIL'}")
        
        # Test validation
        is_valid, errors = registry.validate_for_auto_mode()
        print(f"[Test] Auto mode validation: {'PASS' if is_valid else 'FAIL'}")
        if errors:
            for err in errors:
                print(f"  Error: {err}")
        
    print("\n=== All tests completed ===")
