"""Generate tool manifest and agent skill files.

`mcpbundles init` writes:
  1. A point-in-time tool manifest (mcpbundles_tools.md/.json)
  2. A .skills/mcpbundles-cli/ skill directory following the SKILL.md
     open standard (agentskills.io) so AI agents automatically learn
     how to discover and call tools via the CLI.
"""

import json
import logging
from datetime import datetime, timezone
from pathlib import Path

logger = logging.getLogger(__name__)


def generate_markdown_manifest(tools: list, output: Path, connection_name: str | None = None) -> None:
    lines: list[str] = [
        "# MCPBundles Available Tools",
        "",
        f"*Generated at {datetime.now(timezone.utc).strftime('%Y-%m-%d %H:%M:%S UTC')}*",
        "",
        "> **Snapshot**: This file was generated by `mcpbundles init` and may become stale.",
        "> Run `mcpbundles init` again to refresh.",
        "",
    ]
    if connection_name:
        lines.append(f"Connection: `{connection_name}`")
        lines.append("")

    lines.append(f"## Tools ({len(tools)} total)")
    lines.append("")

    for tool in sorted(tools, key=lambda t: t.name):
        lines.append(f"### `{tool.name}`")
        lines.append("")
        if tool.description:
            lines.append(tool.description.strip())
            lines.append("")

        schema = tool.inputSchema or {}
        properties = schema.get("properties", {})
        required_fields = set(schema.get("required", []))

        if properties:
            lines.append("**Parameters:**")
            lines.append("")
            for prop_name, prop_schema in properties.items():
                is_req = prop_name in required_fields
                ptype = prop_schema.get("type", "any")
                desc = prop_schema.get("description", "")
                marker = "**required**" if is_req else "optional"
                enum_vals = prop_schema.get("enum")
                enum_str = f", one of: `{enum_vals}`" if enum_vals else ""
                lines.append(f"- `{prop_name}` ({ptype}, {marker}){enum_str}: {desc}")
            lines.append("")

    output.write_text("\n".join(lines))


def generate_json_manifest(tools: list, output: Path, connection_name: str | None = None) -> None:
    data = {
        "generated_at": datetime.now(timezone.utc).isoformat(),
        "snapshot": True,
        "connection": connection_name,
        "tool_count": len(tools),
        "tools": [
            {
                "name": t.name,
                "description": t.description,
                "inputSchema": t.inputSchema,
            }
            for t in sorted(tools, key=lambda t: t.name)
        ],
    }
    output.write_text(json.dumps(data, indent=2))


_SKILL_MD = """\
---
name: mcpbundles-cli
description: >-
  Discover and execute third-party API tools via the mcpbundles CLI. Use when
  the user asks to "call an MCP tool", "use mcpbundles", "list available
  bundles", "run a bundle tool", "search for tools", execute any mcpbundles
  command, or interact with services connected through MCPBundles. Also use
  when you see mcpbundles installed or a named connection configured.
---

# MCPBundles CLI — Tool Discovery and Execution

The `mcpbundles` CLI connects you to third-party API tools. Tools are organized
into **bundles** (e.g. `posthog`, `stripe`, `hubspot-crm`). Each bundle groups
related tools for one service.

## Key Concept: Hub vs Bundle

There are two layers and you must understand both:

| Layer | What it contains | How to access |
|-------|-----------------|---------------|
| **Hub** | Platform tools: `search_tools`, `get_bundles`, `code_execution` | `mcpbundles tools` (no `--bundle` flag) |
| **Bundle** | Actual service tools for a specific provider | `mcpbundles tools --bundle <slug>` |

**`mcpbundles tools` alone only shows hub-level meta-tools.** The actual service
tools live on bundle endpoints. You must use `--bundle <slug>` to see them.

## Step 1: Discover Available Bundles

```bash
mcpbundles call get_bundles
```

Returns all enabled bundles with their slugs and tool counts. Filter:

```bash
mcpbundles call get_bundles -- search="analytics"
```

## Step 2: Discover Tools in a Bundle

```bash
mcpbundles tools --bundle <slug>
```

Filter within a bundle:

```bash
mcpbundles tools --bundle <slug> -f query
```

Get the full schema for a specific tool (you need the exact name from step 2):

```bash
mcpbundles tools <tool-name> --bundle <slug>
```

## Step 3: Call a Tool

Two paths — choose based on complexity.

### Path A: Direct Call (`call --bundle`)

Best for simple calls with straightforward arguments.

```bash
mcpbundles call <tool-name> --bundle <slug>
mcpbundles call <tool-name> --bundle <slug> -- key=value limit:=5
```

### Path B: Code Execution (`exec -f`)

Best for complex arguments, chaining multiple calls, or data processing.
**Write a Python file and pass it with `-f` to avoid shell quoting entirely.**

```bash
cat > /tmp/mcb_query.py << 'PYEOF'
import json

# Step 1: Discover tools in the bundle
tools = await list_tools("<slug>")
for t in tools[:5]:
    print(t["function_name"], "-", t["description"][:80])

# Step 2: Get schema for a tool you want to call
schema = await get_tool_schema("<function_name_from_step_1>")
print(json.dumps(schema, indent=2))
PYEOF

mcpbundles exec -f /tmp/mcb_query.py
```

Once you know the function name and schema, call it:

```bash
cat > /tmp/mcb_call.py << 'PYEOF'
import json

result = await <function_name>(
    bundle="<slug>",
    param1="value1",
    param2={"nested": "object"}
)
print(json.dumps(result, indent=2, default=str))
PYEOF

mcpbundles exec -f /tmp/mcb_call.py
```

The `exec -f` pattern is strongly preferred for any call with nested JSON,
multiple steps, or data manipulation. It eliminates all shell quoting issues.

For quick inline code:

```bash
mcpbundles exec "result = await health_check(); print(result)"
```

## Critical Rules

1. **Tool names have hash suffixes** — Every tool has a unique suffix like
   `-67e` or `-5b7`. Never guess. Always discover names first with
   `mcpbundles tools --bundle <slug>` or `await list_tools("<slug>")`.

2. **Bundle tools require `bundle=` in exec** — When calling tools inside
   `code_execution`, always pass `bundle="<slug>"`. Platform tools
   (`health_check`, `get_bundles`, `search_tools`) do NOT take `bundle=`.

3. **Use `exec -f` for complex args** — If the tool takes nested JSON objects,
   write a `.py` file and use `mcpbundles exec -f /tmp/script.py`. Never
   wrestle with shell quoting for nested JSON.

4. **Variables don't persist between exec calls** — Put all related operations
   in one Python file.

5. **Named connections** — If multiple connections exist, use `--as <name>` on
   every command. Run `mcpbundles connections` to see available names.

## Searching for Tools

When you don't know which bundle has the tool you need:

```bash
mcpbundles call search_tools -- query="send email"
```

Returns results with `function_name`, `bundle_slug`, and usage examples.

## Recommended Workflow

1. `mcpbundles call get_bundles` — find the right bundle slug
2. `mcpbundles tools --bundle <slug>` — find the right tool name
3. `mcpbundles tools <tool-name> --bundle <slug>` — get the schema
4. Call via `mcpbundles call ... --bundle <slug>` (simple) or write a
   Python file and use `mcpbundles exec -f /tmp/script.py` (complex)

## Reference

See [references/argument-formats.md](references/argument-formats.md) for the
full argument syntax (key=value, :=json, JSON string, file, stdin).

See [references/common-patterns.md](references/common-patterns.md) for
reusable workflow templates.
"""

_ARGUMENT_FORMATS_MD = """\
# MCPBundles CLI Argument Formats

The `call` command supports several argument formats.

## Key=Value (Auto-Coerced)

Strings by default. Booleans and numbers are auto-coerced when the tool schema
declares them.

```bash
mcpbundles call search_tools -- query="CRM contacts" limit:=5
```

## Typed JSON with `:=`

Force a JSON type with `:=`:

```bash
mcpbundles call <tool-name> --bundle <slug> -- count:=42 active:=true tags:='["a","b"]'
```

## Full JSON String

Pass a single JSON object:

```bash
mcpbundles call <tool-name> --bundle <slug> -- '{"key": "value", "count": 42}'
```

## From File (`-f`)

```bash
mcpbundles call <tool-name> -f payload.json
```

## From Stdin

```bash
echo '{"key": "value"}' | mcpbundles call <tool-name> --stdin
```

## Separator

Always use `--` before arguments when using `--bundle` to prevent flags from
being misinterpreted:

```bash
mcpbundles call <tool-name> --bundle <slug> -- key=value another:=true
```
"""

_COMMON_PATTERNS_MD = """\
# MCPBundles Common Workflow Templates

Reusable patterns. Replace `<slug>` with the bundle slug and `<function_name>`
with the actual tool function name discovered via `list_tools`.

## Discover Bundles and Tool Counts

```bash
cat > /tmp/mcb_discover.py << 'PYEOF'
bundles = await get_bundles()
for b in bundles.get("bundles", []):
    print(f"{b['slug']:30s} {b.get('tool_count', '?')} tools")
PYEOF

mcpbundles exec -f /tmp/mcb_discover.py
```

## List Tools in a Bundle

```bash
cat > /tmp/mcb_list.py << 'PYEOF'
tools = await list_tools("<slug>")
for t in tools:
    print(f"{t['function_name']:50s} {t['description'][:60]}")
PYEOF

mcpbundles exec -f /tmp/mcb_list.py
```

## Get a Tool's Schema Before Calling

```bash
cat > /tmp/mcb_schema.py << 'PYEOF'
import json
schema = await get_tool_schema("<function_name>")
print(json.dumps(schema, indent=2))
PYEOF

mcpbundles exec -f /tmp/mcb_schema.py
```

## Call a Tool With Complex Arguments

```bash
cat > /tmp/mcb_call.py << 'PYEOF'
import json

result = await <function_name>(
    bundle="<slug>",
    param1="value",
    param2={"nested": "object", "with": ["complex", "structure"]}
)
print(json.dumps(result, indent=2, default=str))
PYEOF

mcpbundles exec -f /tmp/mcb_call.py
```

## Chain Multiple Tool Calls

```bash
cat > /tmp/mcb_chain.py << 'PYEOF'
import json

# Step 1: Get data from one tool
result1 = await <function_name_1>(bundle="<slug>", param="value")
items = result1.get("data", [])
print(f"Step 1: got {len(items)} items")

# Step 2: Use results in another call
for item in items[:3]:
    result2 = await <function_name_2>(
        bundle="<slug>",
        id=item["id"]
    )
    print(f"  {item['id']}: {result2.get('status')}")
PYEOF

mcpbundles exec -f /tmp/mcb_chain.py
```

## Simple Direct Calls (No File Needed)

```bash
mcpbundles call health_check
mcpbundles call get_bundles
mcpbundles call search_tools -- query="email"
mcpbundles call <tool-name> --bundle <slug>
mcpbundles call <tool-name> --bundle <slug> -- limit:=5
```
"""


def generate_skill_files(skill_dir: Path | None = None) -> Path:
    """Generate .skills/mcpbundles-cli/ with SKILL.md and references.

    Returns the path to the generated SKILL.md.
    """
    if skill_dir is None:
        skill_dir = Path(".skills") / "mcpbundles-cli"

    refs_dir = skill_dir / "references"
    refs_dir.mkdir(parents=True, exist_ok=True)

    skill_path = skill_dir / "SKILL.md"
    skill_path.write_text(_SKILL_MD)

    (refs_dir / "argument-formats.md").write_text(_ARGUMENT_FORMATS_MD)
    (refs_dir / "common-patterns.md").write_text(_COMMON_PATTERNS_MD)

    return skill_path
