"""
Fanfic Writer v2.0 - Workspace Manager
Handles workspace creation, run initialization, and directory management
"""
import os
import json
from datetime import datetime
from pathlib import Path
from typing import Dict, Any, Optional, Tuple, List
from .utils import (
    generate_run_id, generate_book_uid, to_slug, sanitize_filename,
    get_workspace_root, get_run_dir, create_directory_structure,
    get_timestamp_iso, validate_path_in_workspace, validate_run_id_consistency
)
from .atomic_io import atomic_write_json, atomic_append_jsonl


# ============================================================================
# Workspace Manager
# ============================================================================

class WorkspaceManager:
    """
    Manages novel workspace lifecycle:
    - Create new book workspace
    - Initialize new run
    - Validate workspace integrity
    - Handle resume/continue
    """
    
    def __init__(self, base_dir: Path):
        """
        Initialize WorkspaceManager
        
        Args:
            base_dir: Base directory for all novels (e.g., ~/.openclaw/novels)
        """
        self.base_dir = Path(base_dir)
        self.base_dir.mkdir(parents=True, exist_ok=True)
    
    def create_new_book(
        self,
        book_title: str,
        genre: str,
        target_words: int,
        **kwargs
    ) -> Tuple[Path, str, str, Dict[str, Any]]:
        """
        Create a new book workspace with initial run
        
        Args:
            book_title: Book title (can be Chinese)
            genre: Genre (e.g., "都市灵异")
            target_words: Target word count (<= 500000)
            **kwargs: Additional config options
            
        Returns:
            Tuple of (run_dir, book_uid, run_id, paths_dict)
        """
        # Generate IDs
        book_uid = generate_book_uid(book_title)
        title_slug = to_slug(book_title)
        run_id = generate_run_id()
        
        # Create workspace structure
        workspace_root = get_workspace_root(self.base_dir, title_slug, book_uid)
        run_dir = get_run_dir(workspace_root, run_id)
        
        # Validate no collision
        if run_dir.exists():
            raise RuntimeError(f"Run directory already exists: {run_dir}")
        
        # Create directory structure
        paths = create_directory_structure(run_dir, book_title)
        
        # Generate initial config
        config = self._generate_initial_config(
            book_title=book_title,
            title_slug=title_slug,
            book_uid=book_uid,
            run_id=run_id,
            genre=genre,
            target_words=min(target_words, 500000),  # Hard limit
            **kwargs
        )
        
        # Write config atomically
        if not atomic_write_json(paths['book_config'], config):
            raise RuntimeError("Failed to write initial config")
        
        # Create lock file
        lock_data = {
            'run_id': run_id,
            'book_uid': book_uid,
            'title_slug': title_slug,
            'start_ts': get_timestamp_iso(),
            'pid': os.getpid(),
            'host': os.environ.get('COMPUTERNAME', 'unknown'),
            'mode': kwargs.get('mode', 'manual')
        }
        lock_path = run_dir / ".lock.json"
        atomic_write_json(lock_path, lock_data)
        
        # Create initial writing state
        writing_state = self._generate_initial_writing_state(
            book_title, run_id, kwargs.get('mode', 'manual')
        )
        atomic_write_json(paths['writing_state'], writing_state)
        
        # Create empty log files
        (paths['logs_dir'] / ".gitkeep").touch()
        (paths['logs_prompts'] / ".gitkeep").touch()
        
        return run_dir, book_uid, run_id, paths
    
    def _generate_initial_config(
        self,
        book_title: str,
        title_slug: str,
        book_uid: str,
        run_id: str,
        genre: str,
        target_words: int,
        **kwargs
    ) -> Dict[str, Any]:
        """Generate initial 0-book-config.json"""
        
        chapter_target = kwargs.get('chapter_target_words', 2500)
        if chapter_target < 1500:
            chapter_target = 1500
        elif chapter_target > 8000:
            chapter_target = 8000
        
        return {
            'version': '2.0.0',
            'book': {
                'title': book_title,
                'title_slug': title_slug,
                'book_uid': book_uid,
                'subtitle': kwargs.get('subtitle', None),
                'genre': genre,
                'subgenre': kwargs.get('subgenre', None),
                'target_word_count': min(target_words, 500000),
                'chapter_target_words': chapter_target,
                'language': kwargs.get('language', 'zh'),
                'rating': kwargs.get('rating', 'PG-13'),
                'tone': kwargs.get('tone', '轻松')
            },
            'generation': {
                'model': kwargs.get('model', 'nvidia/moonshotai/kimi-k2.5'),
                'temperature_outline': kwargs.get('temperature_outline', 0.8),
                'temperature_chapter': kwargs.get('temperature_chapter', 0.75),
                'max_attempts': kwargs.get('max_attempts', 3),
                'mode': kwargs.get('mode', 'manual'),
                'auto_threshold': kwargs.get('auto_threshold', 85),
                'auto_rescue_enabled': kwargs.get('auto_rescue_enabled', True),
                'auto_rescue_max_rounds': kwargs.get('auto_rescue_max_rounds', 3)
            },
            'qc': {
                'enabled': True,
                'dimensions': [
                    'outline_adherence',
                    'main_plot',
                    'character',
                    'logic',
                    'continuity',
                    'pacing',
                    'style'
                ],
                'weights': {
                    'outline_adherence': 20,
                    'main_plot': 15,
                    'character': 15,
                    'logic': 20,
                    'continuity': 10,
                    'pacing': 10,
                    'style': 10
                },
                'pass_threshold': 85,
                'warning_threshold': 75
            },
            'backpatch': {
                'frequency_chapters': kwargs.get('backpatch_frequency', 5),
                'severity_threshold_for_block': 'high'
            },
            'time': {
                'timezone_standard': 'Asia/Shanghai',
                'timezone_offset': '+08:00',
                'timestamp_format': 'ISO8601'
            },
            'run_id': run_id,
            'created_at': get_timestamp_iso(),
            'updated_at': get_timestamp_iso()
        }
    
    def _generate_initial_writing_state(
        self,
        book_title: str,
        run_id: str,
        mode: str
    ) -> Dict[str, Any]:
        """Generate initial 4-writing-state.json"""
        return {
            'book_title': book_title,
            'run_id': run_id,
            'mode': mode,
            'current_chapter': 0,
            'completed_chapters': [],
            'current_attempt': 1,
            'qc_score': 0,
            'qc_status': 'INIT',
            'forced_streak': 0,
            'forced_streak_rules': {
                'increment_on_forced': 'forced_streak += 1',
                'reset_on_pass_warning': 'forced_streak = 0',
                'decrement_on_backpatch': 'forced_streak = max(0, forced_streak-1)',
                'threshold': 2
            },
            'flags': {
                'is_paused': False,
                'requires_backpatch': False,
                'prev_chapter_forced': False
            },
            'ending_state': 'not_ready',  # not_ready | ready_to_end | ended
            'ending_checklist': {
                'main_conflict_resolved': False,
                'core_arc_closed': False,
                'major_threads_resolved_ratio': 0.0,
                'final_hook_present': False
            },
            'last_state_commit': get_timestamp_iso(),
            'last_snapshot_id': None,
            'last_outline_summary': '',
            'warning_summary': None,
            'token_spent': 0,
            'cost_total_rmb': 0.0,
            'created_at': get_timestamp_iso(),
            'updated_at': get_timestamp_iso()
        }
    
    def detect_existing_run(
        self,
        book_title: str = None,
        book_uid: str = None,
        run_id: str = None
    ) -> Optional[Path]:
        """
        Detect if a run already exists for the given parameters
        
        Returns:
            Path to run_dir if found, None otherwise
        """
        if book_uid and run_id:
            # Direct lookup
            title_slug = to_slug(book_title) if book_title else "*"
            pattern = f"{title_slug}__{book_uid}/runs/{run_id}"
            matches = list(self.base_dir.glob(pattern))
            if matches:
                return matches[0]
        
        if book_uid:
            # Search by book_uid
            for workspace in self.base_dir.iterdir():
                if workspace.is_dir() and f"__{book_uid}" in workspace.name:
                    runs_dir = workspace / "runs"
                    if runs_dir.exists():
                        if run_id:
                            run_dir = runs_dir / run_id
                            if run_dir.exists():
                                return run_dir
                        else:
                            # Return most recent run
                            runs = sorted(runs_dir.iterdir(), key=lambda p: p.stat().st_mtime)
                            if runs:
                                return runs[-1]
        
        return None
    
    def validate_workspace_integrity(self, run_dir: Path) -> Tuple[bool, List[str]]:
        """
        Validate workspace integrity before operations
        
        Returns:
            Tuple of (is_valid, list_of_errors)
        """
        errors = []
        
        # Check run_dir exists
        if not run_dir.exists():
            errors.append(f"Run directory does not exist: {run_dir}")
            return False, errors
        
        # Check required files
        required_files = [
            run_dir / "0-config" / "0-book-config.json",
            run_dir / "4-state" / "4-writing-state.json"
        ]
        
        for req_file in required_files:
            if not req_file.exists():
                errors.append(f"Required file missing: {req_file}")
        
        # Validate lock consistency
        lock_file = run_dir / ".lock.json"
        if lock_file.exists():
            try:
                with open(lock_file, 'r', encoding='utf-8') as f:
                    lock_data = json.load(f)
                
                run_id_from_dir = run_dir.name
                run_id_from_lock = lock_data.get('run_id')
                
                if run_id_from_lock != run_id_from_dir:
                    errors.append(f"Lock file run_id mismatch: {run_id_from_lock} != {run_id_from_dir}")
                    
            except Exception as e:
                errors.append(f"Failed to read lock file: {e}")
        
        # Check no path escaping
        if not validate_path_in_workspace(run_dir, run_dir):
            errors.append("Workspace path validation failed")
        
        return len(errors) == 0, errors
    
    def get_book_list(self) -> List[Dict[str, Any]]:
        """List all books in the base directory"""
        books = []
        
        for workspace in self.base_dir.iterdir():
            if not workspace.is_dir():
                continue
            
            # Parse workspace name: {title_slug}__{book_uid}
            parts = workspace.name.rsplit('__', 1)
            if len(parts) != 2:
                continue
            
            title_slug, book_uid = parts
            
            # Find runs
            runs_dir = workspace / "runs"
            if not runs_dir.exists():
                continue
            
            for run_dir in runs_dir.iterdir():
                config_file = run_dir / "0-config" / "0-book-config.json"
                if config_file.exists():
                    try:
                        with open(config_file, 'r', encoding='utf-8') as f:
                            config = json.load(f)
                        
                        books.append({
                            'book_title': config['book']['title'],
                            'book_uid': book_uid,
                            'run_id': run_dir.name,
                            'genre': config['book']['genre'],
                            'status': config['book'].get('status', 'unknown'),
                            'path': str(run_dir),
                            'created_at': config.get('created_at', 'unknown')
                        })
                    except Exception:
                        pass
        
        return sorted(books, key=lambda x: x['created_at'], reverse=True)


# ============================================================================
# Intent Checklist Generator
# ============================================================================

def generate_intent_checklist(book_config: Dict[str, Any]) -> Dict[str, Any]:
    """
    Generate initial intent_checklist.json based on book config
    
    This is the 10-item alignment checklist from design doc
    """
    book = book_config.get('book', {})
    genre = book.get('genre', '')
    subgenre = book.get('subgenre', '')
    tone = book.get('tone', '轻松')
    
    return {
        'version': '1.0',
        'source': '0-book-config',
        'items': [
            {
                'id': 1,
                'name': '题材关键词',
                'description': f'必须是{genre}',
                'must_be': [genre] + ([subgenre] if subgenre else []),
                'must_not': [],
                'required': True,
                'weight': 0.1
            },
            {
                'id': 2,
                'name': '核心基调',
                'description': f'必须是{tone}',
                'must_be': tone,
                'must_not': '黑暗' if tone != '暗黑' else '轻松',
                'required': True,
                'weight': 0.1
            },
            {
                'id': 3,
                'name': '主角身份',
                'description': '主角身份设定',
                'must_be': '待设定',
                'must_not': None,
                'required': True,
                'weight': 0.1
            },
            {
                'id': 4,
                'name': '世界规则',
                'description': '核心世界观规则存在',
                'must_be': '待设定',
                'must_not': None,
                'required': True,
                'weight': 0.1
            },
            {
                'id': 5,
                'name': '主要冲突类型',
                'description': '故事主要冲突类型',
                'must_be': '待设定',
                'must_not': None,
                'required': True,
                'weight': 0.1
            },
            {
                'id': 6,
                'name': '叙事视角',
                'description': '叙事视角设定',
                'must_be': '第三人称限知',
                'must_not': ['第一人称', '上帝视角'],
                'required': True,
                'weight': 0.1
            },
            {
                'id': 7,
                'name': '目标受众',
                'description': '目标读者群体',
                'must_be': '网文',
                'must_not': None,
                'required': False,
                'weight': 0.1
            },
            {
                'id': 8,
                'name': '核心伏笔',
                'description': '主线伏笔设定',
                'must_be': '待设定',
                'must_not': None,
                'required': True,
                'weight': 0.1
            },
            {
                'id': 9,
                'name': '力量等级',
                'description': '力量/技能体系',
                'must_be': '待设定',
                'must_not': None,
                'required': False,
                'weight': 0.1
            },
            {
                'id': 10,
                'name': '结局走向',
                'description': '故事结局方向',
                'must_be': ['HE', '开放式'],
                'must_not': 'BE',
                'required': True,
                'weight': 0.1
            }
        ]
    }


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

if __name__ == "__main__":
    import tempfile
    
    print("=== Workspace Manager Test ===\n")
    
    with tempfile.TemporaryDirectory() as tmpdir:
        base_dir = Path(tmpdir) / "novels"
        
        # Create manager
        mgr = WorkspaceManager(base_dir)
        
        # Test create new book
        run_dir, book_uid, run_id, paths = mgr.create_new_book(
            book_title="阴间外卖",
            genre="都市灵异",
            target_words=250000,
            subgenre="系统流",
            mode="manual"
        )
        
        print(f"[Test] Book created: {run_dir.exists()}")
        print(f"  book_uid: {book_uid}")
        print(f"  run_id: {run_id}")
        
        # Test directory structure
        print(f"\n[Test] Directory structure:")
        print(f"  0-config exists: {paths['config_dir'].exists()}")
        print(f"  4-state exists: {paths['state_dir'].exists()}")
        print(f"  chapters exists: {paths['chapters_dir'].exists()}")
        print(f"  logs exists: {paths['logs_dir'].exists()}")
        
        # Test config written
        config_path = paths['book_config']
        print(f"\n[Test] Config file exists: {config_path.exists()}")
        
        with open(config_path, 'r', encoding='utf-8') as f:
            config = json.load(f)
        print(f"  Title: {config['book']['title']}")
        print(f"  Genre: {config['book']['genre']}")
        print(f"  Target words: {config['book']['target_word_count']}")
        print(f"  Mode: {config['generation']['mode']}")
        
        # Test writing state
        state_path = paths['writing_state']
        print(f"\n[Test] Writing state exists: {state_path.exists()}")
        
        with open(state_path, 'r', encoding='utf-8') as f:
            state = json.load(f)
        print(f"  Current chapter: {state['current_chapter']}")
        print(f"  QC status: {state['qc_status']}")
        
        # Test detect existing run
        detected = mgr.detect_existing_run(book_uid=book_uid)
        print(f"\n[Test] Detect existing run: {detected == run_dir}")
        
        # Test validate integrity
        is_valid, errors = mgr.validate_workspace_integrity(run_dir)
        print(f"\n[Test] Workspace integrity: {'PASS' if is_valid else 'FAIL'}")
        if errors:
            for err in errors:
                print(f"  Error: {err}")
        
        # Test book list
        books = mgr.get_book_list()
        print(f"\n[Test] Book list: {len(books)} book(s)")
        if books:
            print(f"  First book: {books[0]['book_title']}")
        
        # Test intent checklist generation
        checklist = generate_intent_checklist(config)
        print(f"\n[Test] Intent checklist generated: {len(checklist['items'])} items")
        
    print("\n=== All tests completed ===")
