"""get_customer_licenses -- Retrieve license and package info from the LMA.

Runs exactly 2 SOQL queries (~10s total):
  1. Production licenses by Account ID
  2. Sandbox licenses by Company__c fuzzy match

Returns a compact summary to the AI (unique packages + org summary).
Writes full raw data to customers/<slug>/licenses.json.
Sandbox results are cross-referenced with production packages to
filter out false matches from the fuzzy Company__c query.
"""

import json
import logging
import re
from collections import defaultdict
from datetime import datetime, timezone
from pathlib import Path
from typing import Any

from ..utils import sf_cli, state

logger = logging.getLogger(__name__)

_COMPANY_SUFFIXES = re.compile(
    r"\b(Ltd|Limited|Inc|Corp|Corporation|Pty|PLC|GmbH|AG|SA|SAS|"
    r"Co|Company|Group|Holdings|International|Services|Solutions|"
    r"Technologies|Consulting)\b\.?",
    re.IGNORECASE,
)

TOOL_DEFINITION = {
    "name": "get_customer_licenses",
    "description": (
        "Retrieve all CloudSense licenses for a customer from the LMA. "
        "Queries production licenses by Account ID and sandbox licenses "
        "by Company name. Returns a compact package and org summary. "
        "Writes full data to customers/<slug>/licenses.json. "
        "Requires connect_lma and find_customer first."
    ),
    "inputSchema": {
        "type": "object",
        "properties": {
            "account_id": {
                "type": "string",
                "description": "Salesforce Account ID from find_customer.",
            },
            "account_name": {
                "type": "string",
                "description": "Customer account name from find_customer.",
            },
            "working_directory": {
                "type": "string",
                "description": "Workspace directory. The AI passes this automatically.",
            },
        },
        "required": ["account_id", "account_name"],
    },
}


LICENSE_FIELDS = (
    "Id, "
    "sfLma__Subscriber_Org_ID__c, "
    "sfLma__Account__c, "
    "Package_Name__c, "
    "Package_Version_Number__c, "
    "sfLma__License_Status__c, "
    "sfLma__Expiration_Date__c, "
    "Days_To_Expire__c, "
    "sfLma__Is_Sandbox__c, "
    "Company__c"
)


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


def _extract_search_keyword(account_name: str) -> str:
    """Strip corporate suffixes to get a short keyword for Company__c match."""
    cleaned = _COMPANY_SUFFIXES.sub("", account_name).strip()
    cleaned = re.sub(r"\s+", " ", cleaned).strip()
    words = cleaned.split()
    if len(words) <= 2:
        return cleaned
    return " ".join(words[:2])


def _query_prod_licenses(
    account_id: str, lma_alias: str,
) -> list[dict[str, Any]]:
    query = (
        f"SELECT {LICENSE_FIELDS} "
        f"FROM sfLma__License__c "
        f"WHERE sfLma__Account__c = '{account_id}' "
        f"AND sfLma__Is_Sandbox__c = false "
        f"ORDER BY Package_Name__c"
    )
    try:
        return sf_cli.lma_query(query, lma_alias)
    except sf_cli.SfCliError as e:
        logger.warning("Production license query failed: %s", e)
        return []


def _query_sandbox_licenses(
    keyword: str, lma_alias: str,
) -> list[dict[str, Any]]:
    safe_keyword = keyword.replace("'", "\\'")
    query = (
        f"SELECT {LICENSE_FIELDS} "
        f"FROM sfLma__License__c "
        f"WHERE Company__c LIKE '%{safe_keyword}%' "
        f"AND sfLma__Is_Sandbox__c = true "
        f"ORDER BY Package_Name__c"
    )
    try:
        return sf_cli.lma_query(query, lma_alias)
    except sf_cli.SfCliError as e:
        logger.warning("Sandbox license query failed: %s", e)
        return []


def _filter_sandbox_by_prod_packages(
    sandbox_licenses: list[dict],
    prod_packages: set[str],
) -> tuple[list[dict], list[dict]]:
    """Split sandbox licenses into matched (share packages with prod) and unmatched."""
    if not prod_packages:
        return sandbox_licenses, []

    sandbox_by_org: dict[str, list[dict]] = defaultdict(list)
    for lic in sandbox_licenses:
        org_id = lic.get("sfLma__Subscriber_Org_ID__c") or "unknown"
        sandbox_by_org[org_id].append(lic)

    matched: list[dict] = []
    unmatched: list[dict] = []
    for org_id, lics in sandbox_by_org.items():
        org_packages = {l.get("Package_Name__c") or "" for l in lics}
        if org_packages & prod_packages:
            matched.extend(lics)
        else:
            unmatched.extend(lics)

    return matched, unmatched


def _build_org_summary(
    prod_licenses: list[dict], sandbox_licenses: list[dict],
) -> dict[str, Any]:
    """Group licenses by subscriber org ID."""
    prod_orgs: dict[str, list[dict]] = defaultdict(list)
    sandbox_orgs: dict[str, list[dict]] = defaultdict(list)

    for lic in prod_licenses:
        org_id = lic.get("sfLma__Subscriber_Org_ID__c") or "unknown"
        prod_orgs[org_id].append(lic)

    for lic in sandbox_licenses:
        org_id = lic.get("sfLma__Subscriber_Org_ID__c") or "unknown"
        sandbox_orgs[org_id].append(lic)

    best_sandbox = None
    best_count = 0
    for org_id, lics in sandbox_orgs.items():
        if len(lics) > best_count:
            best_count = len(lics)
            best_sandbox = org_id

    return {
        "production_orgs": {
            oid: {
                "package_count": len(lics),
                "packages": sorted(set(
                    l.get("Package_Name__c") or "?" for l in lics
                )),
            }
            for oid, lics in prod_orgs.items()
        },
        "sandbox_orgs": {
            oid: {
                "package_count": len(lics),
                "company": (lics[0].get("Company__c") or "?") if lics else "?",
                "packages": sorted(set(
                    l.get("Package_Name__c") or "?" for l in lics
                )),
            }
            for oid, lics in sandbox_orgs.items()
        },
        "production_org_count": len(prod_orgs),
        "sandbox_org_count": len(sandbox_orgs),
        "recommended_sandbox": best_sandbox,
        "recommended_sandbox_packages": best_count,
    }


def _summarize_packages(licenses: list[dict]) -> list[dict[str, Any]]:
    """Deduplicate licenses into a compact unique-packages list."""
    by_package: dict[str, dict[str, Any]] = {}
    for lic in licenses:
        pkg = lic.get("Package_Name__c") or "?"
        ver = lic.get("Package_Version_Number__c") or "?"
        status = lic.get("sfLma__License_Status__c") or "?"
        days = lic.get("Days_To_Expire__c")

        if pkg not in by_package:
            by_package[pkg] = {
                "package": pkg,
                "versions": set(),
                "statuses": set(),
                "min_expiry_days": None,
                "license_count": 0,
            }
        entry = by_package[pkg]
        entry["versions"].add(ver)
        entry["statuses"].add(status)
        entry["license_count"] += 1
        if days is not None:
            try:
                d = float(days)
                if entry["min_expiry_days"] is None or d < entry["min_expiry_days"]:
                    entry["min_expiry_days"] = d
            except (ValueError, TypeError):
                pass

    result = []
    for entry in by_package.values():
        result.append({
            "package": entry["package"],
            "versions": sorted(entry["versions"]),
            "status": ", ".join(sorted(entry["statuses"])),
            "min_expiry_days": entry["min_expiry_days"],
            "license_count": entry["license_count"],
        })
    return result


def _lean_prod_record(lic: dict) -> dict:
    """Keep only useful fields from a production license record."""
    return {
        "id": lic.get("Id"),
        "org_id": lic.get("sfLma__Subscriber_Org_ID__c"),
        "package": lic.get("Package_Name__c"),
        "version": lic.get("Package_Version_Number__c"),
        "status": lic.get("sfLma__License_Status__c"),
        "expiry": lic.get("sfLma__Expiration_Date__c"),
        "days_to_expire": lic.get("Days_To_Expire__c"),
    }


def _group_sandbox_by_org(licenses: list[dict]) -> dict[str, Any]:
    """Group sandbox licenses by subscriber org ID with lean fields."""
    orgs: dict[str, dict[str, Any]] = {}
    for lic in licenses:
        org_id = lic.get("sfLma__Subscriber_Org_ID__c") or "unknown"
        if org_id not in orgs:
            orgs[org_id] = {
                "company": lic.get("Company__c") or "",
                "packages": [],
            }
        orgs[org_id]["packages"].append({
            "id": lic.get("Id"),
            "package": lic.get("Package_Name__c"),
            "version": lic.get("Package_Version_Number__c"),
            "status": lic.get("sfLma__License_Status__c"),
            "expiry": lic.get("sfLma__Expiration_Date__c"),
            "days_to_expire": lic.get("Days_To_Expire__c"),
        })
    return orgs


def _write_licenses_json(
    working_dir: Path,
    slug: str,
    account_id: str,
    account_name: str,
    prod_licenses: list[dict],
    sandbox_licenses: list[dict],
    excluded_sandbox: list[dict],
) -> Path:
    customer_dir = working_dir / "customers" / slug
    customer_dir.mkdir(parents=True, exist_ok=True)
    out_path = customer_dir / "licenses.json"
    out_path.write_text(json.dumps({
        "account_id": account_id,
        "account_name": account_name,
        "generated_at": datetime.now(timezone.utc).isoformat(),
        "production_licenses": [_lean_prod_record(l) for l in prod_licenses],
        "sandbox_orgs": _group_sandbox_by_org(sandbox_licenses),
        "excluded_sandbox_orgs": _group_sandbox_by_org(excluded_sandbox),
    }, indent=2, default=str))
    return out_path


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

    account_id = arguments.get("account_id", "")
    account_name = arguments.get("account_name", "")

    if not account_id or not account_name:
        return (
            "STATUS: MISSING_CUSTOMER\n\n"
            "Both account_id and account_name are required. "
            "Call find_customer first, then pass the confirmed "
            "account_id and account_name to this tool."
        )

    current_state = state.load()
    lma_alias = current_state.get("lma_org_alias") or "cs-lma"
    slug = _make_slug(account_name)

    prod_licenses = _query_prod_licenses(account_id, lma_alias)
    keyword = _extract_search_keyword(account_name)
    raw_sandbox = _query_sandbox_licenses(keyword, lma_alias)

    prod_packages = {
        l.get("Package_Name__c") or "" for l in prod_licenses
    } - {""}
    sandbox_licenses, excluded_sandbox = _filter_sandbox_by_prod_packages(
        raw_sandbox, prod_packages,
    )

    if not prod_licenses and not sandbox_licenses:
        return (
            f"STATUS: NO_LICENSES\n\n"
            f"No licenses found for **{account_name}** ({account_id}).\n\n"
            f"Tell the user no CloudSense licenses were found for this "
            f"customer. They may want to search for a different customer "
            f"or verify the account is correct."
        )

    org_summary = _build_org_summary(prod_licenses, sandbox_licenses)

    artifact_path = _write_licenses_json(
        state.get_working_dir(), slug,
        account_id, account_name,
        prod_licenses, sandbox_licenses, excluded_sandbox,
    )

    # -- Build compact summary for AI --
    lines = [
        "STATUS: READY\n",
        f"## Licenses for {account_name}\n",
    ]

    prod_summary = _summarize_packages(prod_licenses)
    lines.append(f"### Production: {len(prod_licenses)} licenses, {len(prod_summary)} unique packages\n")
    if prod_summary:
        lines.append("| Package | Version(s) | Status | Nearest Expiry (days) |")
        lines.append("|---------|-----------|--------|----------------------|")
        for p in prod_summary:
            ver = ", ".join(p["versions"])
            expiry = p["min_expiry_days"]
            expiry_str = str(int(expiry)) if expiry is not None else "—"
            lines.append(f"| {p['package']} | {ver} | {p['status']} | {expiry_str} |")
    else:
        lines.append("No production licenses found.")

    sandbox_summary = _summarize_packages(sandbox_licenses)
    lines.append(f"\n### Sandbox: {len(sandbox_licenses)} licenses, {len(sandbox_summary)} unique packages\n")
    if sandbox_summary:
        lines.append("| Package | Version(s) | Status |")
        lines.append("|---------|-----------|--------|")
        for p in sandbox_summary:
            ver = ", ".join(p["versions"])
            lines.append(f"| {p['package']} | {ver} | {p['status']} |")
    else:
        lines.append("No sandbox licenses found.")

    if excluded_sandbox:
        excluded_companies = sorted(set(
            l.get("Company__c") or "?" for l in excluded_sandbox
        ))
        lines.append(
            f"\n*Note: {len(excluded_sandbox)} sandbox licenses from unrelated "
            f"companies were excluded (matched keyword '{keyword}' but no "
            f"packages in common with production): {', '.join(excluded_companies)}*"
        )

    lines.append(f"\n### Org Summary\n")
    lines.append(f"- Production orgs: **{org_summary['production_org_count']}**")
    for oid, info in org_summary["production_orgs"].items():
        lines.append(f"  - `{oid}`: {', '.join(info['packages'])}")
    lines.append(f"- Sandbox orgs: **{org_summary['sandbox_org_count']}**")
    for oid, info in org_summary["sandbox_orgs"].items():
        lines.append(f"  - `{oid}` ({info['company']}): {', '.join(info['packages'])}")

    if org_summary["recommended_sandbox"]:
        lines.append(
            f"\n**Recommended sandbox:** `{org_summary['recommended_sandbox']}` "
            f"({org_summary['recommended_sandbox_packages']} packages)"
        )

    lines.append(f"\n### Artifacts\n")
    lines.append(f"- Full data: `{artifact_path}`")

    lines.append(
        "\n\nPresent the license summary to the user. Highlight:\n"
        "- Any packages with expiry under 90 days\n"
        "- Version differences between production and sandbox\n"
        "- The recommended sandbox for metadata retrieval\n"
        "- Ask what they'd like to do next"
    )

    return "\n".join(lines)
