"""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 MCP tools, use mcpbundles, list bundles, search for
  tools, or interact with services connected through MCPBundles.
---

# MCPBundles CLI

Tools are organized into **bundles** (e.g. `stripe`, `hubspot-crm`).
`mcpbundles tools` shows hub meta-tools only. Use `--bundle <slug>` for service tools.

## Workflow

```bash
mcpbundles call get_bundles                          # 1. find bundle slug
mcpbundles call search_tools -- query="send email"   #    or search across all
mcpbundles tools --bundle <slug>                     # 2. find tool name (has hash suffix like -5b7)
mcpbundles tools <tool-name> --bundle <slug>         # 3. get schema
mcpbundles call <tool-name> --bundle <slug> -- k=v   # 4. call it
```

## Arguments

```bash
mcpbundles call <tool> --bundle <slug> -- key=value limit:=5 tags:='["a","b"]'
```

For complex/nested JSON, use `exec -f` to avoid shell quoting:

```bash
mcpbundles exec -f /tmp/script.py    # Python with: result = await tool_name(bundle="slug", ...)
mcpbundles exec "r = await health_check(); print(r)"  # inline
```

## Rules

- **Tool names have hash suffixes** — always discover first, never guess
- **`bundle=` required in exec** — `await tool(bundle="slug", ...)` for bundle tools; platform tools (`health_check`, `get_bundles`) omit it
- **Variables don't persist** between `exec` calls — put everything in one script
- **Multiple connections** — use `--as <name>` on every command

## REST API Providers

Register any REST API with `upsert_provider` using `base_url` instead of `server_url`.
Creates provider + bundle + credential with one `api_request` tool. Auth is auto-injected.

```bash
mcpbundles call upsert_provider -- base_url="https://api.example.com/v1" api_key="sk-xxx" name="Example"
mcpbundles call upsert_provider -- base_url="..." api_key="..." auth_type="header" auth_header_name="X-API-Key"
mcpbundles call upsert_provider -- base_url="..." auth_type="none"
```

`auth_type`: bearer (default), header, query_param, basic, none.

The tool name is `{provider_slug}-api-request` (shown in `upsert_provider` response).

```bash
mcpbundles call <provider-slug>-api-request --bundle <bundle> -- method="GET" path="/users"
mcpbundles call <provider-slug>-api-request --bundle <bundle> -- method="POST" path="/data" body:='{"key":"val"}'
```

## Reference

See [references/argument-formats.md](references/argument-formats.md) and
[references/common-patterns.md](references/common-patterns.md).
"""

_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
