"""Command-line interface for Cadence.

Provides scaffolding and utility commands:
- cadence init: Initialize a new project
- cadence new cadence <name>: Generate a new cadence
- cadence new note <name>: Generate a new note
- cadence diagram <module>: Generate cadence diagrams
- cadence validate <module>: Validate cadence definitions

Usage:
    python -m cadence init
    python -m cadence new cadence checkout
    python -m cadence diagram myapp.cadences:checkout_cadence --format mermaid
"""

from __future__ import annotations

import argparse
import sys
from pathlib import Path
from textwrap import dedent


def init_command(args: argparse.Namespace) -> int:
    """Initialize a new Cadence project."""
    project_dir = Path(args.directory or ".")
    project_name = args.name or project_dir.name

    print(f"Initializing Cadence project: {project_name}")

    # Create directory structure
    dirs = [
        project_dir / "cadences",
        project_dir / "notes",
        project_dir / "tests",
    ]

    for d in dirs:
        d.mkdir(parents=True, exist_ok=True)
        init_file = d / "__init__.py"
        if not init_file.exists():
            init_file.write_text('"""Auto-generated by cadence init."""\n')

    # Create sample cadence
    cadences_init = project_dir / "cadences" / "__init__.py"
    auto_gen_marker = '"""Auto-generated by cadence init."""'
    if not cadences_init.exists() or cadences_init.read_text().strip() == auto_gen_marker:
        cadences_init.write_text(
            dedent(f'''
            """
            {project_name} Cadences

            Define your cadences here.
            """

            from cadence import Cadence, Score, note
            from dataclasses import dataclass
            from typing import Optional


            @dataclass
            class SampleScore(Score):
                """Sample score for demonstration."""
                input_value: str = ""
                result: Optional[str] = None


            @note
            async def process_input(score: SampleScore) -> None:
                """Process the input value."""
                score.result = f"Processed: {{score.input_value}}"


            # Define a sample cadence
            sample_cadence = (
                Cadence("sample", SampleScore())
                .then("process", process_input)
            )


            __all__ = ["sample_cadence", "SampleScore"]
        ''').strip()
            + "\n"
        )

    # Create sample test
    test_file = project_dir / "tests" / "test_cadences.py"
    if not test_file.exists():
        test_file.write_text(
            dedent(f'''
            """Tests for {project_name} cadences."""

            import pytest
            from cadences import sample_cadence, SampleScore


            class TestSampleCadence:
                """Test the sample cadence."""

                @pytest.mark.asyncio
                async def test_sample_cadence(self):
                    """Test basic cadence execution."""
                    score = SampleScore(input_value="hello")
                    score.__post_init__()

                    result = await sample_cadence.run()

                    assert result.result == "Processed: hello"
        ''').strip()
            + "\n"
        )

    # Create pyproject.toml if it doesn't exist
    pyproject = project_dir / "pyproject.toml"
    if not pyproject.exists():
        pyproject.write_text(
            dedent(f'''
            [project]
            name = "{project_name}"
            version = "0.1.0"
            dependencies = [
                "cadence-flow",
            ]

            [project.optional-dependencies]
            dev = [
                "pytest>=8.0",
                "pytest-asyncio>=0.23",
            ]

            [tool.pytest.ini_options]
            testpaths = ["tests"]
            asyncio_mode = "auto"
        ''').strip()
            + "\n"
        )

    print("  Created: cadences/")
    print("  Created: notes/")
    print("  Created: tests/")
    print("  Created: cadences/__init__.py (sample cadence)")
    print("  Created: tests/test_cadences.py")
    if not (project_dir / "pyproject.toml").exists():
        print("  Created: pyproject.toml")

    print("\nProject initialized! Next steps:")
    print(f"  1. cd {project_dir}")
    print("  2. pip install -e .[dev]")
    print("  3. pytest")
    print("  4. Edit cadences/__init__.py to add your cadences")

    return 0


def new_cadence_command(args: argparse.Namespace) -> int:
    """Generate a new cadence file."""
    cadence_name = args.name
    output_dir = Path(args.output or "cadences")
    output_dir.mkdir(parents=True, exist_ok=True)

    # Convert to valid Python identifier
    module_name = cadence_name.lower().replace("-", "_").replace(" ", "_")
    normalized = cadence_name.replace("-", " ").replace("_", " ")
    class_name = "".join(word.title() for word in normalized.split())

    output_file = output_dir / f"{module_name}.py"

    if output_file.exists() and not args.force:
        print(f"Error: {output_file} already exists. Use --force to overwrite.")
        return 1

    content = (
        dedent(f'''
        """
        {class_name} Cadence

        TODO: Add description of what this cadence does.
        """

        from cadence import Cadence, Score, note
        from dataclasses import dataclass
        from typing import Optional, List


        @dataclass
        class {class_name}Score(Score):
            """Score for {cadence_name} cadence."""
            # TODO: Add your score fields
            id: str = ""
            result: Optional[str] = None
            errors: Optional[List[str]] = None


        # --- Notes ---

        @note
        async def validate(score: {class_name}Score) -> None:
            """Validate the input."""
            if not score.id:
                if score.errors is None:
                    score.errors = []
                score.errors.append("id is required")


        @note
        async def process(score: {class_name}Score) -> None:
            """Process the request."""
            # TODO: Implement processing logic
            score.result = f"Processed {{score.id}}"


        @note
        async def finalize(score: {class_name}Score) -> None:
            """Finalize the operation."""
            # TODO: Cleanup, notifications, etc.
            pass


        # --- Cadence Definition ---

        {module_name}_cadence = (
            Cadence("{module_name}", {class_name}Score())
            .then("validate", validate)
            .then("process", process)
            .then("finalize", finalize)
        )


        __all__ = ["{module_name}_cadence", "{class_name}Score"]
    ''').strip()
        + "\n"
    )

    output_file.write_text(content)
    print(f"Created: {output_file}")
    print(f"\nScore class: {class_name}Score")
    print(f"Cadence variable: {module_name}_cadence")

    return 0


def new_note_command(args: argparse.Namespace) -> int:
    """Generate a new note file."""
    note_name = args.name
    output_dir = Path(args.output or "notes")
    output_dir.mkdir(parents=True, exist_ok=True)

    module_name = note_name.lower().replace("-", "_").replace(" ", "_")
    func_name = module_name

    output_file = output_dir / f"{module_name}.py"

    if output_file.exists() and not args.force:
        print(f"Error: {output_file} already exists. Use --force to overwrite.")
        return 1

    # Check for resilience options
    decorators = ["@note"]
    if args.retry:
        decorators.insert(0, f"@retry(max_attempts={args.retry})")
    if args.timeout:
        decorators.insert(0, f"@timeout(seconds={args.timeout})")
    if args.fallback:
        decorators.insert(0, f"@fallback(default={args.fallback})")

    imports = ["from cadence import note"]
    if args.retry:
        imports.append("from cadence import retry")
    if args.timeout:
        imports.append("from cadence import timeout")
    if args.fallback:
        imports.append("from cadence import fallback")

    decorator_str = "\n".join(decorators)
    import_str = "\n".join(imports)

    content = (
        dedent(f'''
        """
        {note_name} Note

        TODO: Add description of what this note does.
        """

        {import_str}
        from typing import Any


        {decorator_str}
        async def {func_name}(score: Any) -> None:
            """
            {note_name}

            Args:
                score: The cadence score

            TODO: Implement this note
            """
            pass


        __all__ = ["{func_name}"]
    ''').strip()
        + "\n"
    )

    output_file.write_text(content)
    print(f"Created: {output_file}")
    print(f"Function: {func_name}")

    return 0


def diagram_command(args: argparse.Namespace) -> int:
    """Generate a diagram from a cadence."""
    from cadence.diagram import print_cadence, save_diagram, to_dot, to_mermaid

    # Parse module:attribute
    if ":" in args.cadence:
        module_path, attr_name = args.cadence.rsplit(":", 1)
    else:
        print(
            "Error: Cadence must be specified as module:attribute "
            "(e.g., myapp.cadences:checkout_cadence)"
        )
        return 1

    # Import the cadence
    try:
        import importlib

        module = importlib.import_module(module_path)
        cadence = getattr(module, attr_name)
    except ImportError as e:
        print(f"Error importing module: {e}")
        return 1
    except AttributeError:
        print(f"Error: {attr_name} not found in {module_path}")
        return 1

    # Generate output
    if args.output:
        save_diagram(cadence, args.output, format=args.format)
        print(f"Diagram saved to: {args.output}")
    elif args.format == "text":
        print_cadence(cadence)
    elif args.format == "mermaid":
        print(to_mermaid(cadence))
    else:
        print(to_dot(cadence))

    return 0


def validate_command(args: argparse.Namespace) -> int:
    """Validate cadence definitions."""
    # Parse module:attribute or just module
    if ":" in args.module:
        module_path, attr_name = args.module.rsplit(":", 1)
        attrs_to_check = [attr_name]
    else:
        module_path = args.module
        attrs_to_check = None

    try:
        import importlib

        module = importlib.import_module(module_path)
    except ImportError as e:
        print(f"Error importing module: {e}")
        return 1

    from cadence.cadence import Cadence

    # Find all cadences in module
    if attrs_to_check is None:
        attrs_to_check = [
            name for name in dir(module) if isinstance(getattr(module, name), Cadence)
        ]

    if not attrs_to_check:
        print(f"No cadences found in {module_path}")
        return 1

    errors = []
    warnings = []

    for attr_name in attrs_to_check:
        cadence = getattr(module, attr_name, None)
        if not isinstance(cadence, Cadence):
            continue

        print(f"Validating: {attr_name} ({cadence._name})")

        # Check for empty cadences
        if not cadence._measures:
            errors.append(f"  {attr_name}: Cadence has no measures")
            continue

        # Check measure names
        measure_names = set()
        for measure in cadence._measures:
            if measure.name in measure_names:
                warnings.append(f"  {attr_name}: Duplicate measure name '{measure.name}'")
            measure_names.add(measure.name)

        # Check for potential issues
        from cadence.nodes.parallel import ParallelMeasure

        for measure in cadence._measures:
            if isinstance(measure, ParallelMeasure) and len(measure._tasks) < 2:
                warnings.append(
                    f"  {attr_name}: Parallel measure '{measure.name}' has only "
                    f"{len(measure._tasks)} task(s)"
                )

        print(f"  ✓ {len(cadence._measures)} measures")

    if errors:
        print("\nErrors:")
        for err in errors:
            print(f"  ✗ {err}")
        return 1

    if warnings:
        print("\nWarnings:")
        for w in warnings:
            print(f"  ⚠ {w}")

    print("\nValidation complete!")
    return 0


def main(argv: list[str] | None = None) -> int:
    """Main entry point for the CLI."""
    parser = argparse.ArgumentParser(
        prog="cadence",
        description="Cadence CLI - scaffolding and utilities",
    )
    parser.add_argument(
        "--version",
        action="version",
        version="%(prog)s 0.3.0",
    )

    subparsers = parser.add_subparsers(dest="command", help="Available commands")

    # init command
    init_parser = subparsers.add_parser("init", help="Initialize a new project")
    init_parser.add_argument(
        "directory",
        nargs="?",
        help="Project directory (default: current)",
    )
    init_parser.add_argument(
        "--name",
        help="Project name (default: directory name)",
    )

    # new command
    new_parser = subparsers.add_parser("new", help="Generate new components")
    new_subparsers = new_parser.add_subparsers(dest="component", help="Component type")

    # new cadence
    new_cadence_parser = new_subparsers.add_parser("cadence", help="Generate a new cadence")
    new_cadence_parser.add_argument("name", help="Cadence name")
    new_cadence_parser.add_argument("-o", "--output", help="Output directory (default: cadences)")
    new_cadence_parser.add_argument("-f", "--force", action="store_true", help="Overwrite existing")

    # new note
    new_note_parser = new_subparsers.add_parser("note", help="Generate a new note")
    new_note_parser.add_argument("name", help="Note name")
    new_note_parser.add_argument("-o", "--output", help="Output directory (default: notes)")
    new_note_parser.add_argument("-f", "--force", action="store_true", help="Overwrite existing")
    new_note_parser.add_argument("--retry", type=int, help="Add @retry with max_attempts")
    new_note_parser.add_argument("--timeout", type=float, help="Add @timeout with seconds")
    new_note_parser.add_argument("--fallback", help="Add @fallback with default value")

    # diagram command
    diagram_parser = subparsers.add_parser("diagram", help="Generate cadence diagrams")
    diagram_parser.add_argument("cadence", help="Cadence as module:attribute")
    diagram_parser.add_argument(
        "-f",
        "--format",
        choices=["mermaid", "dot", "text"],
        default="mermaid",
        help="Output format",
    )
    diagram_parser.add_argument("-o", "--output", help="Output file")

    # validate command
    validate_parser = subparsers.add_parser("validate", help="Validate cadence definitions")
    validate_parser.add_argument("module", help="Module to validate (module or module:cadence)")

    args = parser.parse_args(argv)

    if args.command == "init":
        return init_command(args)
    elif args.command == "new":
        if args.component == "cadence":
            return new_cadence_command(args)
        elif args.component == "note":
            return new_note_command(args)
        else:
            new_parser.print_help()
            return 1
    elif args.command == "diagram":
        return diagram_command(args)
    elif args.command == "validate":
        return validate_command(args)
    else:
        parser.print_help()
        return 0


if __name__ == "__main__":
    sys.exit(main())
