"""Project root detection and initialization."""

from __future__ import annotations

import subprocess
from pathlib import Path

AQM_DIR = ".aqm"
AGENTS_YAML = "agents.yaml"

# YAML spec reference for AI generation — loaded from docs/spec.md at runtime
SPEC_PATH = Path(__file__).resolve().parent.parent.parent / "docs" / "spec.md"

DEFAULT_AGENTS_YAML = """\
apiVersion: aqm/v0.1

agents:
  - id: planner
    name: Planning Agent
    runtime: api
    system_prompt: |
      You are a versatile planner.
      Analyze the user's requirements and create a detailed execution plan.

      Requirements: {{ input }}
    handoffs:
      - to: executor
        task: execute
        condition: always
        payload: "{{ output }}"

  - id: executor
    name: Execution Agent
    runtime: claude_code
    system_prompt: |
      Execute the task based on the plan.

      Plan: {{ input }}
    claude_code_flags:
      - "--allowedTools"
      - "Edit,Write,Bash,Read"
"""


def find_project_root(start: Path | None = None) -> Path | None:
    """Traverse parent directories to find the .aqm/ directory."""
    current = (start or Path.cwd()).resolve()
    for parent in [current, *current.parents]:
        if (parent / AQM_DIR).is_dir():
            return parent
    return None


def init_project(path: Path | None = None, yaml_content: str | None = None) -> Path:
    """Create the .aqm/ directory and agents.yaml.

    Args:
        path: Target directory. Defaults to cwd.
        yaml_content: Custom YAML content. Uses DEFAULT_AGENTS_YAML if None.
    """
    root = (path or Path.cwd()).resolve()
    aq_dir = root / AQM_DIR

    aq_dir.mkdir(exist_ok=True)
    (aq_dir / "tasks").mkdir(exist_ok=True)

    agents_yaml = aq_dir / AGENTS_YAML
    if not agents_yaml.exists():
        agents_yaml.write_text(yaml_content or DEFAULT_AGENTS_YAML, encoding="utf-8")

    return root


def _load_spec() -> str:
    """Load the YAML spec document for AI-assisted generation."""
    if SPEC_PATH.exists():
        return SPEC_PATH.read_text(encoding="utf-8")
    # Fallback: minimal spec summary
    return (
        "agents.yaml format: apiVersion: aqm/v0.1, agents list with id, name, "
        "runtime (api|claude_code), system_prompt, handoffs (to, task, condition, payload), "
        "gate (type: llm|human, prompt), mcp servers, params section."
    )


def analyze_project(project_dir: Path) -> str:
    """Analyze the existing project to extract tech stack and structure.

    Uses Claude CLI to scan the project directory and produce a summary
    of languages, frameworks, tools, and directory structure.

    Args:
        project_dir: Root directory of the project to analyze.

    Returns:
        Project analysis summary string.
    """
    analysis_prompt = (
        "Analyze this project directory and produce a concise summary. Include:\n"
        "- Primary language(s) and frameworks\n"
        "- Build tools and package managers (package.json, pyproject.toml, etc.)\n"
        "- Project structure (key directories and their purpose)\n"
        "- Testing frameworks in use\n"
        "- CI/CD configuration if present\n"
        "- Any notable tools or services (databases, APIs, etc.)\n\n"
        "Be concise — bullet points only, no prose."
    )

    result = subprocess.run(
        ["claude", "-p", analysis_prompt, "--print"],
        capture_output=True,
        text=True,
        timeout=90,
        cwd=str(project_dir),
    )

    if result.returncode != 0 or not result.stdout.strip():
        return ""

    return result.stdout.strip()


def generate_agents_yaml(
    description: str,
    project_dir: Path | None = None,
) -> str:
    """Use Claude CLI to generate agents.yaml from a description and project context.

    When project_dir is provided, the project is analyzed first and the
    analysis is included in the generation prompt so the resulting pipeline
    is tailored to the actual tech stack.

    Args:
        description: User's description of the desired pipeline/automation.
        project_dir: Optional project directory to analyze for context.

    Returns:
        Generated YAML content string.

    Raises:
        RuntimeError: If generation fails.
    """
    spec = _load_spec()

    # Build project context section
    project_context = ""
    if project_dir:
        analysis = analyze_project(project_dir)
        if analysis:
            project_context = (
                f"\n## Existing Project Analysis\n"
                f"The pipeline will be used in a project with the following characteristics:\n"
                f"{analysis}\n\n"
                f"Tailor the pipeline to this project's tech stack. For example:\n"
                f"- Use appropriate MCP servers for the detected tools\n"
                f"- Reference the project's test framework in QA agents\n"
                f"- Match the project's language/framework in system prompts\n"
            )

    prompt = f"""You are a YAML generator. You output ONLY valid YAML. No prose, no explanations, no markdown.

TASK: Generate an aqm agents.yaml pipeline configuration.

SPEC:
{spec}
{project_context}
USER REQUEST: {description}

RULES:
1. First line of output MUST be: apiVersion: aqm/v0.1
2. Output raw YAML only — no ```yaml fences, no comments explaining what you did, no introductory text
3. Every agent needs: id, name, runtime (api or claude_code), system_prompt
4. Use handoff conditions: always, on_approve, on_reject, auto
5. Use runtime: api for planning/reviewing, claude_code for code execution
6. Add gates (type: llm or human) where quality checks make sense
7. Add MCP servers where agents need external tools
8. Use params for configurable values

IMPORTANT: Your entire response must be parseable as YAML. Do not write anything before or after the YAML."""

    result = subprocess.run(
        ["claude", "-p", prompt, "--print", "--output-format", "text"],
        capture_output=True,
        text=True,
        timeout=120,
    )

    if result.returncode != 0:
        raise RuntimeError(f"Claude CLI failed: {result.stderr.strip()}")

    generated = result.stdout.strip()
    if not generated:
        raise RuntimeError("Claude returned empty output")

    # Strip markdown fences if present
    generated = _strip_markdown_fences(generated)

    # Strip any leading non-YAML prose before apiVersion
    generated = _strip_leading_prose(generated)

    return generated + "\n"


def _strip_markdown_fences(text: str) -> str:
    """Remove markdown code fences from generated output."""
    lines = text.split("\n")

    # Remove opening fence (```yaml, ```yml, ```)
    if lines and lines[0].strip().startswith("```"):
        lines = lines[1:]

    # Remove closing fence
    if lines and lines[-1].strip() == "```":
        lines = lines[:-1]

    return "\n".join(lines)


def _strip_leading_prose(text: str) -> str:
    """Strip any non-YAML text before the actual YAML content.

    Looks for 'apiVersion:' line and discards everything before it.
    """
    lines = text.split("\n")

    for i, line in enumerate(lines):
        stripped = line.strip()
        # Found the start of actual YAML
        if stripped.startswith("apiVersion:"):
            return "\n".join(lines[i:])
        # Also accept lines starting with valid top-level keys
        if stripped.startswith(("agents:", "params:", "imports:")):
            return "\n".join(lines[i:])

    # No recognizable YAML start found — return as-is
    return text


def get_agents_yaml_path(root: Path) -> Path:
    return root / AQM_DIR / AGENTS_YAML


def get_tasks_dir(root: Path) -> Path:
    return root / AQM_DIR / "tasks"


def get_db_path(root: Path) -> Path:
    return root / AQM_DIR / "queue.db"
