"""
Project Scaffold — generates project structure from a one-line description.

Creates:
  - project.toon — TOON spec with project architecture
  - ROADMAP.md — development roadmap with milestones
  - pyproject.toml / package.json — dependency config
  - src/ skeleton with placeholder files
  - tests/ skeleton
  - README.md
"""

from __future__ import annotations

import json
import logging
import os
import textwrap
from dataclasses import dataclass, field
from pathlib import Path
from typing import Any, Dict, List, Optional

logger = logging.getLogger("toonic.autopilot.scaffold")


@dataclass
class ProjectSpec:
    """Parsed project specification."""
    name: str
    description: str
    language: str = "python"
    framework: str = ""
    project_type: str = "cli"  # cli, api, web, library, data-pipeline
    features: List[str] = field(default_factory=list)
    dependencies: List[str] = field(default_factory=list)
    structure: Dict[str, Any] = field(default_factory=dict)


# ── Templates ────────────────────────────────────────────────────────

TOON_TEMPLATE = textwrap.dedent("""\
    # {name} — Project Specification (TOON Format)
    # Generated by: toonic init
    # Type: {project_type} | Language: {language}

    P[{name}] {{
      desc: "{description}"
      type: {project_type}
      lang: {language}
      {framework_line}

      ## Architecture
      M[src] {{
        {modules}
      }}

      ## Tests
      M[tests] {{
        {test_modules}
      }}

      ## Dependencies
      D[deps] {{
        {deps}
      }}

      ## Features (MVP)
      F[features] {{
        {features}
      }}
    }}
""")

ROADMAP_TEMPLATE = textwrap.dedent("""\
    # {name} — Development Roadmap

    > {description}

    ## Phase 1: Foundation (Sprint 1)
    - [x] Project scaffolding
    - [ ] Core data models
    - [ ] Basic {project_type} structure
    - [ ] Unit test framework
    {phase1_tasks}

    ## Phase 2: Core Features (Sprint 2)
    {phase2_tasks}

    ## Phase 3: Polish & Deploy (Sprint 3)
    - [ ] Error handling & validation
    - [ ] Documentation
    - [ ] CI/CD pipeline
    - [ ] Docker deployment
    - [ ] Performance optimization

    ## Quality Gates
    - Test coverage > 80%
    - All linting passes
    - README with usage examples
    - Docker build succeeds

    ---
    *Auto-generated by `toonic init` — edit freely.*
""")

README_TEMPLATE = textwrap.dedent("""\
    # {name}

    > {description}

    ## Quick Start

    ```bash
    # Install
    {install_cmd}

    # Run
    {run_cmd}

    # Test
    {test_cmd}
    ```

    ## Project Structure

    ```
    {tree}
    ```

    ## Development

    This project is managed by [toonic](https://github.com/wronai/toonic).

    ```bash
    # Start autonomous development
    toonic autopilot . --goal "build MVP"

    # Watch progress
    open http://localhost:8900
    ```

    ## License

    MIT
""")


# ── Language-specific generators ─────────────────────────────────────

def _python_project(spec: ProjectSpec, project_dir: Path) -> Dict[str, str]:
    """Generate Python project files."""
    files = {}
    pkg = spec.name.replace("-", "_").replace(" ", "_").lower()

    # pyproject.toml
    deps_str = ", ".join(f'"{d}"' for d in spec.dependencies) if spec.dependencies else ""
    files["pyproject.toml"] = textwrap.dedent(f"""\
        [build-system]
        requires = ["setuptools>=68.0", "wheel"]
        build-backend = "setuptools.build_meta"

        [project]
        name = "{spec.name}"
        version = "0.1.0"
        description = "{spec.description}"
        requires-python = ">=3.10"
        dependencies = [{deps_str}]

        [project.scripts]
        {spec.name} = "{pkg}.cli:main"

        [tool.pytest.ini_options]
        testpaths = ["tests"]
    """)

    # Package init
    files[f"{pkg}/__init__.py"] = f'"""{ spec.name} — {spec.description}"""\n\n__version__ = "0.1.0"\n'

    # Main models
    files[f"{pkg}/models.py"] = textwrap.dedent(f"""\
        \"\"\"Data models for {spec.name}.\"\"\"

        from __future__ import annotations
        from dataclasses import dataclass, field
        from typing import Any, Dict, List, Optional


        # TODO: Define core data models based on project requirements
        @dataclass
        class BaseModel:
            \"\"\"Base model — extend with project-specific fields.\"\"\"
            id: str = ""
            name: str = ""
            metadata: Dict[str, Any] = field(default_factory=dict)
    """)

    # CLI entry point
    if spec.project_type in ("cli", "api"):
        files[f"{pkg}/cli.py"] = textwrap.dedent(f"""\
            \"\"\"CLI entry point for {spec.name}.\"\"\"

            from __future__ import annotations
            import argparse
            import sys


            def main(args=None):
                parser = argparse.ArgumentParser(
                    prog="{spec.name}",
                    description="{spec.description}",
                )
                # TODO: Add subcommands and options
                parser.add_argument("--version", action="version", version="0.1.0")
                parsed = parser.parse_args(args)
                parser.print_help()


            if __name__ == "__main__":
                main()
        """)

    # API-specific
    if spec.project_type == "api":
        if "fastapi" not in [d.lower().split(">=")[0] for d in spec.dependencies]:
            spec.dependencies.append("fastapi>=0.110")
            spec.dependencies.append("uvicorn>=0.27")

        files[f"{pkg}/api.py"] = textwrap.dedent(f"""\
            \"\"\"API routes for {spec.name}.\"\"\"

            from __future__ import annotations
            from fastapi import FastAPI, HTTPException

            app = FastAPI(title="{spec.name}", description="{spec.description}")


            @app.get("/")
            async def root():
                return {{"name": "{spec.name}", "version": "0.1.0", "status": "ok"}}


            @app.get("/health")
            async def health():
                return {{"status": "healthy"}}


            # TODO: Add CRUD endpoints based on models
        """)

        files[f"{pkg}/server.py"] = textwrap.dedent(f"""\
            \"\"\"Server entry point.\"\"\"

            import uvicorn
            from {pkg}.api import app


            def run(host="0.0.0.0", port=8000):
                uvicorn.run(app, host=host, port=port)


            if __name__ == "__main__":
                run()
        """)

    # Tests
    files["tests/__init__.py"] = ""
    files["tests/conftest.py"] = textwrap.dedent(f"""\
        \"\"\"Shared test fixtures for {spec.name}.\"\"\"

        import pytest


        @pytest.fixture
        def sample_data():
            return {{"id": "test-1", "name": "Test Item"}}
    """)

    files[f"tests/test_{pkg}.py"] = textwrap.dedent(f"""\
        \"\"\"Tests for {spec.name} core functionality.\"\"\"

        import pytest
        from {pkg}.models import BaseModel


        class TestBaseModel:
            def test_create(self):
                m = BaseModel(id="1", name="test")
                assert m.id == "1"
                assert m.name == "test"

            def test_defaults(self):
                m = BaseModel()
                assert m.id == ""
                assert m.metadata == {{}}
    """)

    return files


def _node_project(spec: ProjectSpec, project_dir: Path) -> Dict[str, str]:
    """Generate Node.js project files."""
    files = {}

    deps = {}
    for d in spec.dependencies:
        name = d.split(">=")[0].split("@")[0]
        deps[name] = "*"

    if spec.project_type == "api" and "express" not in deps:
        deps["express"] = "^4.18"

    files["package.json"] = json.dumps({
        "name": spec.name,
        "version": "0.1.0",
        "description": spec.description,
        "main": "src/index.js",
        "scripts": {
            "start": "node src/index.js",
            "test": "jest",
            "dev": "node --watch src/index.js",
        },
        "dependencies": deps,
        "devDependencies": {"jest": "^29.0"},
    }, indent=2) + "\n"

    files["src/index.js"] = textwrap.dedent(f"""\
        // {spec.name} — {spec.description}

        console.log("{spec.name} v0.1.0");

        // TODO: Implement core logic
    """)

    files["tests/index.test.js"] = textwrap.dedent(f"""\
        // Tests for {spec.name}

        test("placeholder", () => {{
          expect(true).toBe(true);
        }});
    """)

    return files


# ── Main scaffold class ──────────────────────────────────────────────

LANGUAGE_GENERATORS = {
    "python": _python_project,
    "javascript": _node_project,
    "nodejs": _node_project,
    "node": _node_project,
    "js": _node_project,
}

# Heuristics to detect project type and language from description
TYPE_KEYWORDS = {
    "api": ["api", "rest", "endpoint", "crud", "backend", "server", "http"],
    "web": ["web", "frontend", "ui", "dashboard", "spa", "react", "vue"],
    "cli": ["cli", "command", "tool", "utility", "script"],
    "library": ["library", "lib", "package", "module", "sdk"],
    "data-pipeline": ["pipeline", "etl", "data", "transform", "ingest", "stream"],
}

LANG_KEYWORDS = {
    "python": ["python", "py", "django", "flask", "fastapi", "pytest"],
    "javascript": ["javascript", "js", "node", "express", "react", "vue", "npm"],
}

FRAMEWORK_KEYWORDS = {
    "fastapi": ["fastapi", "fast api"],
    "flask": ["flask"],
    "django": ["django"],
    "express": ["express"],
    "react": ["react"],
    "vue": ["vue"],
}


class ProjectScaffold:
    """Generate a full project scaffold from a description."""

    @staticmethod
    def detect_spec(description: str, name: str = "", language: str = "") -> ProjectSpec:
        """Parse a natural-language description into ProjectSpec."""
        desc_lower = description.lower()

        # Detect language
        if not language:
            for lang, keywords in LANG_KEYWORDS.items():
                if any(k in desc_lower for k in keywords):
                    language = lang
                    break
            else:
                language = "python"  # default

        # Detect project type
        project_type = "cli"
        for ptype, keywords in TYPE_KEYWORDS.items():
            if any(k in desc_lower for k in keywords):
                project_type = ptype
                break

        # Detect framework
        framework = ""
        for fw, keywords in FRAMEWORK_KEYWORDS.items():
            if any(k in desc_lower for k in keywords):
                framework = fw
                break

        # Auto-name from description
        if not name:
            words = description.split()[:3]
            name = "-".join(w.lower().strip(",.!?") for w in words if w.isalpha())
            if not name:
                name = "my-project"

        # Extract features from description
        features = [f.strip() for f in description.split(",") if len(f.strip()) > 3]

        # Default dependencies
        deps = []
        if language == "python":
            if project_type == "api":
                deps = ["fastapi>=0.110", "uvicorn>=0.27"]
            elif project_type == "cli":
                deps = []
        elif language in ("javascript", "nodejs"):
            if project_type == "api":
                deps = ["express"]

        return ProjectSpec(
            name=name,
            description=description,
            language=language,
            framework=framework,
            project_type=project_type,
            features=features,
            dependencies=deps,
        )

    @staticmethod
    def generate(spec: ProjectSpec, output_dir: str | Path) -> Dict[str, str]:
        """Generate all project files into output_dir. Returns {path: content}."""
        project_dir = Path(output_dir)
        project_dir.mkdir(parents=True, exist_ok=True)

        all_files = {}

        # Language-specific files
        generator = LANGUAGE_GENERATORS.get(spec.language, _python_project)
        lang_files = generator(spec, project_dir)
        all_files.update(lang_files)

        # project.toon
        pkg = spec.name.replace("-", "_").replace(" ", "_").lower()
        modules = "\n        ".join(f'f[{pkg}/{f.split("/")[-1]}]' for f in lang_files if f.startswith(pkg + "/") or f.startswith("src/"))
        test_modules = "\n        ".join(f'f[{f}]' for f in lang_files if f.startswith("tests/"))
        deps = "\n        ".join(f'"{d}"' for d in spec.dependencies)
        features = "\n        ".join(f'- {f}' for f in spec.features)

        all_files["project.toon"] = TOON_TEMPLATE.format(
            name=spec.name,
            description=spec.description,
            project_type=spec.project_type,
            language=spec.language,
            framework_line=f'framework: {spec.framework}' if spec.framework else '',
            modules=modules or "# (to be generated)",
            test_modules=test_modules or "# (to be generated)",
            deps=deps or "# (none yet)",
            features=features or "# (from description)",
        )

        # ROADMAP.md
        phase1 = ""
        phase2 = ""
        if spec.project_type == "api":
            phase1 = "- [ ] API endpoint scaffolding\n    - [ ] Database models\n    - [ ] Request/response schemas"
            phase2 = "- [ ] CRUD operations\n    - [ ] Authentication\n    - [ ] Input validation\n    - [ ] API documentation (OpenAPI)"
        elif spec.project_type == "cli":
            phase1 = "- [ ] Argument parsing\n    - [ ] Core commands\n    - [ ] Config file support"
            phase2 = "- [ ] Output formatting\n    - [ ] Interactive mode\n    - [ ] Shell completion"
        elif spec.project_type == "data-pipeline":
            phase1 = "- [ ] Data source connectors\n    - [ ] Transform pipeline\n    - [ ] Output sinks"
            phase2 = "- [ ] Error recovery\n    - [ ] Parallel processing\n    - [ ] Monitoring & metrics"
        else:
            phase1 = "- [ ] Core module implementation\n    - [ ] Configuration system"
            phase2 = "- [ ] Extended features\n    - [ ] Integration tests"

        all_files["ROADMAP.md"] = ROADMAP_TEMPLATE.format(
            name=spec.name,
            description=spec.description,
            project_type=spec.project_type,
            phase1_tasks=phase1,
            phase2_tasks=phase2,
        )

        # README.md
        tree_lines = sorted(all_files.keys())
        tree = "\n    ".join(tree_lines)

        if spec.language == "python":
            install_cmd = f"pip install -e ."
            run_cmd = f"python -m {pkg}" if spec.project_type != "api" else f"python -m {pkg}.server"
            test_cmd = "pytest tests/ -v"
        else:
            install_cmd = "npm install"
            run_cmd = "npm start"
            test_cmd = "npm test"

        all_files["README.md"] = README_TEMPLATE.format(
            name=spec.name,
            description=spec.description,
            install_cmd=install_cmd,
            run_cmd=run_cmd,
            test_cmd=test_cmd,
            tree=tree,
        )

        # .gitignore
        if spec.language == "python":
            all_files[".gitignore"] = textwrap.dedent("""\
                __pycache__/
                *.pyc
                .venv/
                dist/
                *.egg-info/
                .pytest_cache/
                toonic_data/
                .env
            """)
        else:
            all_files[".gitignore"] = textwrap.dedent("""\
                node_modules/
                dist/
                .env
                toonic_data/
            """)

        # toonic-server.yaml for autopilot
        all_files["toonic-server.yaml"] = textwrap.dedent(f"""\
            # Toonic Autopilot Config for {spec.name}
            host: "0.0.0.0"
            port: 8900
            goal: "autonomously develop {spec.name}: {spec.description}"
            interval: 30.0
            max_context_tokens: 100000
            log_level: "INFO"

            models:
              text:
                provider: openrouter
                model: "google/gemini-2.5-flash-preview:thinking"
                max_tokens: 16384
                api_key_env: OPENROUTER_API_KEY
              code:
                provider: openrouter
                model: "google/gemini-2.5-flash-preview:thinking"
                max_tokens: 16384
                api_key_env: OPENROUTER_API_KEY
        """)

        # Write all files
        written = {}
        for rel_path, content in all_files.items():
            full_path = project_dir / rel_path
            full_path.parent.mkdir(parents=True, exist_ok=True)
            full_path.write_text(content, encoding="utf-8")
            written[rel_path] = content
            logger.info(f"  Created: {rel_path}")

        return written

    @classmethod
    def init(cls, description: str, name: str = "", language: str = "",
             output_dir: str = "") -> tuple[ProjectSpec, Dict[str, str]]:
        """One-call scaffold: detect spec + generate files."""
        spec = cls.detect_spec(description, name=name, language=language)
        if not output_dir:
            output_dir = f"./{spec.name}"
        files = cls.generate(spec, output_dir)
        return spec, files
