import os
from pathlib import Path
from typing import Dict, Any, Optional
import pkgutil
from .parser import RoadbookParser, RoadbookModel

class ScaffoldManager:
    """
    Centralized manager for Roadbook project structure and file generation.
    Ensures consistency between 'init' and 'run' commands.
    """

    @staticmethod
    def get_structure_paths(book_dir: Path) -> Dict[str, Path]:
        """Returns standard paths for a roadbook directory."""
        return {
            "root": book_dir,
            "config": book_dir / ".rb",
            "scripts": book_dir / "scripts",
            "runtime": book_dir / "runtime",
            "outputs": book_dir / "outputs",
            "roadbook_file": book_dir / "roadbook.md"
        }

    @staticmethod
    def create_roadbook_scaffold(book_dir: Path, rb_id: str, name: str, description: str, entry_url: str = "https://www.example.com"):
        """Creates the full roadbook scaffold (directories + roadbook.md)."""
        paths = ScaffoldManager.get_structure_paths(book_dir)
        
        # Create directories
        paths["config"].mkdir(parents=True, exist_ok=True)
        paths["scripts"].mkdir(parents=True, exist_ok=True)
        paths["runtime"].mkdir(parents=True, exist_ok=True)
        paths["outputs"].mkdir(parents=True, exist_ok=True)
        
        # Create roadbook.md
        if not paths["roadbook_file"].exists():
            content = ScaffoldManager._generate_roadbook_md_content(rb_id, name, description, entry_url)
            with open(paths["roadbook_file"], "w", encoding="utf-8") as f:
                f.write(content)

        # Create .gitignore
        gitignore_file = book_dir / ".gitignore"
        if not gitignore_file.exists():
            with open(gitignore_file, "w", encoding="utf-8") as f:
                f.write(".rb/profile/\noutputs/\nruntime/\n__pycache__/\n*.pyc\n")
        
        return paths

    @staticmethod
    def create_script_scaffold(book_dir: Path, language: str = "python", rb_id: str = "roadbook", name: str = "Roadbook", roadbook_content: str = "") -> Path:
        """Creates the script scaffold if it doesn't exist."""
        paths = ScaffoldManager.get_structure_paths(book_dir)
        scripts_dir = paths["scripts"]
        scripts_dir.mkdir(parents=True, exist_ok=True)
        
        script_path = None
        
        # Try to parse roadbook content to model if provided
        roadbook_model = None
        if roadbook_content:
            try:
                parser = RoadbookParser()
                roadbook_model = parser.parse(roadbook_content)
            except Exception:
                pass

        if language == "python":
            # 1. Create subdirectories
            tests_dir = scripts_dir / "tests"
            tests_dir.mkdir(parents=True, exist_ok=True)
            (scripts_dir / "utils").mkdir(parents=True, exist_ok=True)

            # 1.5 Create sample test file with Agent Instructions
            sample_test_py = tests_dir / "sample_test.py"
            if not sample_test_py.exists():
                with open(sample_test_py, "w", encoding="utf-8") as f:
                    f.write(
                        "\"\"\"\n"
                        "[AGENT INSTRUCTION - MICRO TESTING]\n"
                        "Before modifying the main script.py, use this directory to write minimal tests.\n"
                        "Focus on ONE single element or interaction (e.g., just clicking a complex dropdown).\n"
                        "Once the logic is verified here, merge it back into the main script.py.\n"
                        "\"\"\"\n"
                        "from playwright.sync_api import sync_playwright\n\n"
                        "def test_single_element():\n"
                        "    with sync_playwright() as p:\n"
                        "        browser = p.chromium.launch(headless=False)\n"
                        "        page = browser.new_page()\n"
                        "        # Add your micro-test logic here\n"
                        "        browser.close()\n\n"
                        "if __name__ == '__main__':\n"
                        "    test_single_element()\n"
                    )

            # 2. Create __init__.py for utils
            init_py = scripts_dir / "utils" / "__init__.py"
            if not init_py.exists():
                with open(init_py, "w") as f:
                    pass

            # 3. Create script.py
            script_path = scripts_dir / "script.py"
            if not script_path.exists():
                content = ScaffoldManager._generate_python_script_template(rb_id, name, roadbook_model)
                with open(script_path, "w", encoding="utf-8") as f:
                    f.write(content)
                    
            # 4. Create local INPUT.json (JSON Schemas are now auto-generated by SDK on run)
            local_input_path = paths["config"] / "INPUT.json"
            if not local_input_path.exists():
                import json
                input_data = {}
                inputs_meta = roadbook_model.meta.get("inputs") if (roadbook_model and roadbook_model.meta) else None
                if isinstance(inputs_meta, dict) and inputs_meta:
                    for key in inputs_meta.keys():
                        input_data[key] = "example_value"
                else:
                    input_data = {"search_query": "example", "max_items_to_fetch": 10}
                with open(local_input_path, "w", encoding="utf-8") as f:
                    json.dump(input_data, f, indent=4, ensure_ascii=False)
                    
            # 5. Create requirements.txt
            req_path = scripts_dir / "requirements.txt"
            if not req_path.exists():
                with open(req_path, "w", encoding="utf-8") as f:
                    f.write("playwright\njsonschema\npydantic\n")
        
        return script_path



    @staticmethod
    def _generate_roadbook_md_content(rb_id, name, description, entry_url="https://www.example.com"):
        import uuid
        template_bytes = pkgutil.get_data(__package__, "templates/roadbook.md.tpl")
        if not template_bytes:
            raise RuntimeError("Could not find roadbook.md.tpl template")
        template_str = template_bytes.decode("utf-8")
        
        return template_str.replace("{rb_id}", rb_id)\
                           .replace("{name}", name)\
                           .replace("{description}", description)\
                           .replace("{entry_url}", entry_url)\
                           .replace("{uuid_1}", uuid.uuid4().hex[:5])\
                           .replace("{uuid_2}", uuid.uuid4().hex[:5])\
                           .replace("{uuid_3}", uuid.uuid4().hex[:5])\
                           .replace("{uuid_4}", uuid.uuid4().hex[:5])

    @staticmethod
    def _generate_python_script_template(rb_id, name, roadbook_model: Optional[RoadbookModel] = None):
        import datetime
        date_str = datetime.datetime.now().strftime("%Y-%m-%d")
        
        # Pydantic models generation
        input_model_fields = []
        output_model_fields = []
        
        if roadbook_model and roadbook_model.meta:
            # Inputs
            inputs = roadbook_model.meta.get("inputs", {})
            if isinstance(inputs, dict):
                for key, desc in inputs.items():
                    if key != "entry_url":
                        input_model_fields.append(f'    {key}: Optional[str] = Field(default=None, description="{desc}")')
            
            # Outputs
            outputs = roadbook_model.meta.get("outputs", {})
            if isinstance(outputs, dict):
                for key, desc in outputs.items():
                    output_model_fields.append(f'    {key}: str = Field(description="{desc}")')
                    
        if not input_model_fields:
            input_model_fields.append('    # Add your input fields here, e.g.:')
            input_model_fields.append('    # search_query: str = Field(description="Search keyword")')
            
        if not output_model_fields:
            output_model_fields.append('    # Add your output fields here, e.g.:')
            output_model_fields.append('    # title: str = Field(description="Item title")')
            
        input_model_fields_str = "\n".join(input_model_fields)
        output_model_fields_str = "\n".join(output_model_fields)

        # Logic Generation
        phase_functions = []
        phase_calls = []
        
        if roadbook_model:
            # Group sheets by type
            setup_sheets = [s for s in roadbook_model.sheets if s.type == "setup"]
            process_sheets = [s for s in roadbook_model.sheets if s.type == "process" or not s.type]
            delivery_sheets = [s for s in roadbook_model.sheets if s.type == "delivery"]
            
            all_sheets = []
            if setup_sheets: all_sheets.append(("setup", setup_sheets))
            if process_sheets: all_sheets.append(("process", process_sheets))
            if delivery_sheets: all_sheets.append(("delivery", delivery_sheets))
            
            for phase_type, sheets in all_sheets:
                for i, sheet in enumerate(sheets, 1):
                    func_name = sheet.id.replace('-', '_').replace(' ', '_').lower() if sheet.id else f"{phase_type}_sheet_{i}"
                    
                    func_def = [
                        f"def phase_{func_name}(rb: RoadbookContext, inputs: TaskInput):",
                        f'    with rb.sheet("{sheet.title}"):  # ID: {sheet.id}',
                        f'        page = rb.page'
                    ]
                    
                    if sheet.url:
                        func_def.append(f'        # URL: {sheet.url}')
                        func_def.append(f'        # page.goto("{sheet.url}")')
                        
                    if sheet.description:
                        func_def.append(f'        # {sheet.description}')
                        
                    if sheet.steps:
                        for s in sheet.steps:
                            func_def.append(f'        # {s.original_text or s.action}')
                            
                    func_def.append(f'        pass')
                    
                    phase_functions.append("\n".join(func_def))
                    phase_calls.append(f"            phase_{func_name}(rb, inputs)")
                
        else:
            # Default Template if no model
            func_def = [
                "def phase_initialization(rb: RoadbookContext, inputs: TaskInput):",
                '    with rb.sheet("Initialization"):',
                '        page = rb.page',
                '        # page.goto("https://www.example.com")',
                '        pass'
            ]
            phase_functions.append("\n".join(func_def))
            phase_calls.append("            phase_initialization(rb, inputs)")

        phase_functions_str = "\n\n".join(phase_functions)
        phase_calls_str = "\n".join(phase_calls)
        
        # Build site_constraints
        site_constraints_dict = {}
        if roadbook_model:
            for s in roadbook_model.sheets:
                if s.type == "setup" and s.constraints:
                    site_constraints_dict.update(s.constraints)
        
        import json
        constraints_str = json.dumps(site_constraints_dict, indent=4)
        # Convert JSON booleans/null to Python syntax
        constraints_str = constraints_str.replace(": false", ": False").replace(": true", ": True").replace(": null", ": None")
        
        # Indent everything by 4 spaces
        constraints_indented = ""
        for line in constraints_str.split("\n"):
            constraints_indented += f"    {line}\n"
            
        site_constraints_block = f"    # Agent extracted site constraints from setup sheet\n    site_constraints = {constraints_indented.strip()}"
        
        template_bytes = pkgutil.get_data(__package__, "templates/script.py.tpl")
        if not template_bytes:
            raise RuntimeError("Could not find script.py.tpl template")
        template_str = template_bytes.decode("utf-8")
        
        return template_str.replace("{name}", name)\
                           .replace("{rb_id}", rb_id)\
                           .replace("{date_str}", date_str)\
                           .replace("{input_model_fields}", input_model_fields_str)\
                           .replace("{output_model_fields}", output_model_fields_str)\
                           .replace("{phase_functions}", phase_functions_str)\
                           .replace("{phase_calls}", phase_calls_str)\
                           .replace("{site_constraints_block}", site_constraints_block)
