"""list_metadata -- Discover metadata components in a customer org.

Returns member names and count for a single metadata type.
AI calls this in parallel for multiple types to plan retrieval chunking.
"""

import json
import logging
import re
from pathlib import Path
from typing import Any

from ..utils import sf_cli, state

logger = logging.getLogger(__name__)

TOOL_DEFINITION = {
    "name": "list_metadata",
    "description": (
        "Discover metadata components of a given type in a connected customer "
        "sandbox. Returns member names and count. AI calls this in parallel "
        "for all relevant types (ApexClass, ApexTrigger, Flow, CustomObject, "
        "etc.) to plan retrieval chunking. Requires connect_customer_org first."
    ),
    "inputSchema": {
        "type": "object",
        "properties": {
            "account_name": {
                "type": "string",
                "description": "Customer account name (e.g. 'NBN Co Ltd').",
            },
            "metadata_type": {
                "type": "string",
                "description": (
                    "Single metadata type to discover. Examples: ApexClass, "
                    "ApexTrigger, Flow, CustomObject, ApexPage, "
                    "AuraDefinitionBundle, LightningComponentBundle, "
                    "CustomField."
                ),
            },
            "working_directory": {
                "type": "string",
                "description": "Workspace directory. The AI passes this automatically.",
            },
        },
        "required": ["account_name", "metadata_type"],
    },
}

PREVIEW_LIMIT = 50


def _make_slug(name: str) -> str:
    slug = name.lower().strip()
    slug = re.sub(r"[^a-z0-9]+", "-", slug)
    return slug.strip("-") or "unknown"


def _load_org_connection(working_dir: Path, slug: str) -> dict[str, Any] | None:
    """Load org-connection.json for a customer."""
    conn_path = working_dir / "customers" / slug / "org-connection.json"
    if not conn_path.exists():
        return None
    try:
        return json.loads(conn_path.read_text())
    except (json.JSONDecodeError, OSError):
        return None


async def run(arguments: dict[str, Any]) -> str:
    """Execute the list_metadata tool."""
    state.set_working_dir(arguments.get("working_directory"))
    working_dir = state.get_working_dir()

    account_name = arguments.get("account_name", "").strip()
    metadata_type = arguments.get("metadata_type", "").strip()

    if not account_name:
        return "STATUS: MISSING_CUSTOMER\n\naccount_name is required."
    if not metadata_type:
        return "STATUS: MISSING_TYPE\n\nmetadata_type is required."

    slug = _make_slug(account_name)
    conn = _load_org_connection(working_dir, slug)

    if not conn:
        return (
            "STATUS: NOT_CONNECTED\n\n"
            f"No org-connection.json found for `{account_name}`.\n"
            "Call `connect_customer_org` first to authorize a sandbox."
        )

    org_alias = conn.get("org_alias", "")
    if not org_alias:
        return (
            "STATUS: NOT_CONNECTED\n\n"
            "org-connection.json exists but has no org_alias.\n"
            "Call `connect_customer_org` again."
        )

    try:
        data = sf_cli.run_sf(
            ["org", "list", "metadata",
             "--metadata-type", metadata_type,
             "--target-org", org_alias],
            timeout=60,
        )
    except sf_cli.SfCliError as e:
        return (
            f"STATUS: QUERY_FAILED\n\n"
            f"Failed to list {metadata_type}: {e}\n\n"
            "This may indicate the org session has expired. "
            "Run `connect_customer_org` again to re-authorize."
        )

    result = data.get("result", [])
    if not isinstance(result, list):
        result = []

    members = []
    for item in result:
        full_name = item.get("fullName")
        if full_name:
            members.append(full_name)

    members.sort()
    count = len(members)

    discovery_dir = working_dir / "customers" / slug / "metadata" / "discovery"
    discovery_dir.mkdir(parents=True, exist_ok=True)
    discovery_file = discovery_dir / f"{metadata_type}.json"
    discovery_file.write_text(json.dumps({
        "metadata_type": metadata_type,
        "count": count,
        "members": members,
    }, indent=2))

    if count == 0:
        return (
            f"STATUS: NO_MEMBERS\n\n"
            f"**{metadata_type}**: 0 components found in the org.\n"
            f"Saved: `customers/{slug}/metadata/discovery/{metadata_type}.json`"
        )

    preview = members[:PREVIEW_LIMIT]
    preview_text = "\n".join(f"  - {m}" for m in preview)
    truncated = ""
    if count > PREVIEW_LIMIT:
        truncated = f"\n  ... and {count - PREVIEW_LIMIT} more (see full list in discovery file)"

    chunking_hint = ""
    if count <= 3000:
        chunking_hint = (
            "**Retrieval hint:** Count is under 3,000 -- can retrieve with "
            "wildcard in a single `retrieve_metadata` call."
        )
    elif count <= 10000:
        chunk_size = 2000
        num_chunks = (count + chunk_size - 1) // chunk_size
        chunking_hint = (
            f"**Retrieval hint:** {count:,} members -- chunk into "
            f"~{num_chunks} batches of ~{chunk_size:,} for `retrieve_metadata`. "
            f"Can call batches in parallel."
        )
    else:
        chunk_size = 2000
        num_chunks = (count + chunk_size - 1) // chunk_size
        chunking_hint = (
            f"**Retrieval hint:** {count:,} members (LARGE) -- chunk into "
            f"~{num_chunks} batches of ~{chunk_size:,} for `retrieve_metadata`. "
            f"Call batches in parallel. Stay well under 10K per call."
        )

    return (
        f"STATUS: READY\n\n"
        f"**{metadata_type}**: {count:,} components found\n\n"
        f"Preview (first {min(count, PREVIEW_LIMIT)}):\n"
        f"{preview_text}{truncated}\n\n"
        f"{chunking_hint}\n\n"
        f"Full list: `customers/{slug}/metadata/discovery/{metadata_type}.json`"
    )
