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

Runs up to 4 SOQL queries (~15s total):
  1. Production licenses by Account ID
  2. Production licenses by Company__c (catches licenses without Account link)
  3. Sandbox licenses by Company__c fuzzy match
  4. Latest released version for each installed package

Results from queries 1 and 2 are merged (deduplicated by Id).
Sandbox results are cross-referenced with production packages to
filter out false matches from the fuzzy Company__c query.

Returns a compact summary to the AI (unique packages, version currency,
seat utilization, org summary). Writes full raw data to
customers/<slug>/licenses.json.
"""

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__Licensed_Seats__c, "
    "sfLma__Used_Licenses__c, "
    "sfLma__Expiration_Date__c, "
    "Days_To_Expire__c, "
    "sfLma__Is_Sandbox__c, "
    "sfLma__Package_Version__r.sfLma__Package__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 re.sub(r'[^\w]+$', '', cleaned)
    return re.sub(r'[^\w]+$', '', " ".join(words[:2]))


def _is_genuine_company(company: str | None, keyword: str) -> bool:
    """Return True if *keyword* appears at a logical boundary in *company*.

    SOQL ``LIKE '%keyword%'`` casts a wide net.  Trialforce / training
    orgs often embed the customer name inside an auto-generated
    identifier (e.g. ``StudentR36Sep25Starhub1``).  A word-boundary
    check keeps real company names (``StarHub Ltd``, ``PT StarHub``)
    while rejecting those generated identifiers.

    Some LMA Company__c values concatenate the name without spaces
    (e.g. ``NBNCo`` instead of ``NBN Co``).  A camelCase-aware
    fallback normalises the string and retries, but only accepts
    matches where the keyword is the *first* word — trialforce
    identifiers embed the name mid-string, so the ``^`` anchor
    rejects them.
    """
    if not company:
        return False
    if re.search(r'\b' + re.escape(keyword) + r'\b', company, re.IGNORECASE):
        return True
    # CamelCase-aware fallback: insert spaces at case transitions,
    # then accept only if keyword is the first word (^ anchor).
    spaced = re.sub(r'(?<=[a-z\d])(?=[A-Z])', ' ', company)
    spaced = re.sub(r'(?<=[A-Z])(?=[A-Z][a-z])', ' ', spaced)
    if spaced != company:
        return bool(re.search(r'^' + re.escape(keyword) + r'\b', spaced, re.IGNORECASE))
    return False


def _parse_seats(licensed: Any, used: Any) -> dict[str, Any]:
    """Parse seat utilization into a compact structure."""
    if licensed is None and used is None:
        return {"type": "unknown"}
    if isinstance(licensed, str) and licensed.lower() == "site license":
        return {"type": "site"}
    try:
        total = int(licensed) if licensed is not None else None
        active = int(used) if used is not None else 0
        if total and total > 0:
            pct = round((active / total) * 100)
            return {"type": "numeric", "licensed": total, "used": active, "pct": pct}
        return {"type": "numeric", "licensed": total, "used": active, "pct": 0}
    except (ValueError, TypeError):
        return {"type": "other", "raw_licensed": str(licensed)}


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_companyc_prod_licenses(
    keyword: str, lma_alias: str,
) -> list[dict[str, Any]]:
    """Production licenses linked via Company__c only (no Account).

    Mirrors the sandbox Company__c search pattern but for production
    licenses whose sfLma__Account__c is null.
    """
    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 = false "
        f"AND sfLma__Account__c = null "
        f"ORDER BY Package_Name__c"
    )
    try:
        return sf_cli.lma_query(query, lma_alias)
    except sf_cli.SfCliError as e:
        logger.warning("Company__c 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],
    latest_versions: dict[str, dict[str, Any]] | None = None,
) -> 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")

        pkg_version_rel = lic.get("sfLma__Package_Version__r") or {}
        pkg_id = pkg_version_rel.get("sfLma__Package__c") if isinstance(pkg_version_rel, dict) else None

        if pkg not in by_package:
            by_package[pkg] = {
                "package": pkg,
                "versions": set(),
                "statuses": set(),
                "min_expiry_days": None,
                "license_count": 0,
                "seats": [],
                "package_id": None,
            }
        entry = by_package[pkg]
        entry["versions"].add(ver)
        entry["statuses"].add(status)
        entry["license_count"] += 1
        if pkg_id and not entry["package_id"]:
            entry["package_id"] = pkg_id

        entry["seats"].append(_parse_seats(
            lic.get("sfLma__Licensed_Seats__c"),
            lic.get("sfLma__Used_Licenses__c"),
        ))

        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

    lv = latest_versions or {}
    result = []
    for entry in by_package.values():
        seat_summary = _aggregate_seats(entry["seats"])
        pkg_id = entry["package_id"]
        latest_info = lv.get(pkg_id, {}) if pkg_id else {}
        latest_ver = latest_info.get("version")
        installed_versions = sorted(entry["versions"])
        on_latest = None
        if latest_ver and installed_versions:
            on_latest = latest_ver in installed_versions
        result.append({
            "package": entry["package"],
            "versions": installed_versions,
            "status": ", ".join(sorted(entry["statuses"])),
            "min_expiry_days": entry["min_expiry_days"],
            "license_count": entry["license_count"],
            "licensed_seats": seat_summary.get("licensed"),
            "used_seats": seat_summary.get("used"),
            "utilization_pct": seat_summary.get("pct"),
            "seat_type": seat_summary["type"],
            "latest_version": latest_ver,
            "on_latest": on_latest,
        })
    return result


def _aggregate_seats(seat_entries: list[dict[str, Any]]) -> dict[str, Any]:
    """Aggregate seat data across multiple licenses for the same package."""
    numeric = [s for s in seat_entries if s["type"] == "numeric"]
    if numeric:
        total_licensed = sum(s.get("licensed") or 0 for s in numeric)
        total_used = sum(s.get("used") or 0 for s in numeric)
        pct = round((total_used / total_licensed) * 100) if total_licensed > 0 else 0
        return {"type": "numeric", "licensed": total_licensed, "used": total_used, "pct": pct}
    site = [s for s in seat_entries if s["type"] == "site"]
    if site:
        return {"type": "site"}
    return {"type": "unknown"}


def _extract_release(version_str: str | None) -> int | None:
    """Extract the CloudSense release number from a version string.

    R33+ use major version as release number (e.g. 38.0.4 -> R38).
    Pre-R33 packages (major < 33, e.g. 1.484.6) return None (LEGACY).
    """
    if not version_str:
        return None
    try:
        major = int(version_str.split(".")[0])
        return major if major >= 33 else None
    except (ValueError, IndexError):
        return None


def _query_latest_versions(
    package_ids: set[str], lma_alias: str,
) -> dict[str, dict[str, Any]]:
    """Query the latest released version for each package ID.

    Returns {package_id: {"version": "38.0.4", "release": 38}}.
    """
    if not package_ids:
        return {}

    id_list = ", ".join(f"'{pid}'" for pid in sorted(package_ids))
    query = (
        f"SELECT sfLma__Package__c, sfLma__Version_Number__c, sfLma__Release_Date__c "
        f"FROM sfLma__Package_Version__c "
        f"WHERE sfLma__Package__c IN ({id_list}) "
        f"ORDER BY sfLma__Release_Date__c DESC"
    )
    try:
        records = sf_cli.lma_query(query, lma_alias)
    except sf_cli.SfCliError as e:
        logger.warning("Latest versions query failed: %s", e)
        return {}

    latest: dict[str, dict[str, Any]] = {}
    for rec in records:
        pid = rec.get("sfLma__Package__c")
        if pid and pid not in latest:
            ver = rec.get("sfLma__Version_Number__c") or "?"
            latest[pid] = {
                "version": ver,
                "release": _extract_release(ver),
            }
    return latest


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"),
        "licensed_seats": lic.get("sfLma__Licensed_Seats__c"),
        "used_licenses": lic.get("sfLma__Used_Licenses__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"),
            "licensed_seats": lic.get("sfLma__Licensed_Seats__c"),
            "used_licenses": lic.get("sfLma__Used_Licenses__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)

    keyword = _extract_search_keyword(account_name)

    prod_licenses = _query_prod_licenses(account_id, lma_alias)
    companyc_prod = [
        lic for lic in _query_companyc_prod_licenses(keyword, lma_alias)
        if _is_genuine_company(lic.get("Company__c"), keyword)
    ]

    seen_ids = {lic.get("Id") for lic in prod_licenses}
    companyc_count = 0
    for lic in companyc_prod:
        if lic.get("Id") not in seen_ids:
            prod_licenses.append(lic)
            seen_ids.add(lic.get("Id"))
            companyc_count += 1

    raw_sandbox = [
        lic for lic in _query_sandbox_licenses(keyword, lma_alias)
        if _is_genuine_company(lic.get("Company__c"), keyword)
    ]

    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)

    # Collect package IDs from production licenses for latest-version lookup
    all_package_ids: set[str] = set()
    for lic in prod_licenses:
        pv_rel = lic.get("sfLma__Package_Version__r") or {}
        pid = pv_rel.get("sfLma__Package__c") if isinstance(pv_rel, dict) else None
        if pid:
            all_package_ids.add(pid)

    latest_versions = _query_latest_versions(all_package_ids, lma_alias)

    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, latest_versions)
    lines.append(f"### Production: {len(prod_licenses)} licenses, {len(prod_summary)} unique packages\n")
    if prod_summary:
        lines.append("| Package | Version(s) | Latest | Current? | Status | Licensed | Used | Util% | Expiry (days) |")
        lines.append("|---------|-----------|--------|----------|--------|----------|------|-------|---------------|")
        for p in prod_summary:
            ver = ", ".join(p["versions"])
            latest_ver = p.get("latest_version") or "?"
            on_latest = p.get("on_latest")
            if on_latest is True:
                current_str = "Yes"
            elif on_latest is False:
                current_str = "**No**"
            else:
                current_str = "?"
            expiry = p["min_expiry_days"]
            expiry_str = str(int(expiry)) if expiry is not None else "—"
            seat_type = p.get("seat_type", "unknown")
            if seat_type == "numeric":
                licensed_str = str(p["licensed_seats"]) if p["licensed_seats"] is not None else "—"
                used_str = str(p["used_seats"]) if p["used_seats"] is not None else "—"
                util_str = f"{p['utilization_pct']}%" if p["utilization_pct"] is not None else "—"
            elif seat_type == "site":
                licensed_str = "Site"
                used_str = "—"
                util_str = "—"
            else:
                licensed_str = "—"
                used_str = "—"
                util_str = "—"
            lines.append(f"| {p['package']} | {ver} | {latest_ver} | {current_str} | {p['status']} | {licensed_str} | {used_str} | {util_str} | {expiry_str} |")
    else:
        lines.append("No production licenses found.")

    if companyc_count:
        lines.append(
            f"\n*Note: {companyc_count} production license(s) found via "
            f"Company__c match (no Account link). These orgs were "
            f"provisioned outside the standard Account workflow.*"
        )

    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"
        "- **Renewal risk**: packages with expiry under 90 days (CRITICAL) "
        "or 90-180 days (URGENT)\n"
        "- **Version currency**: packages NOT on latest version are upgrade "
        "candidates. Flag how many versions behind.\n"
        "- **Seat utilization**: >85% = upsell signal, <20% = over-provisioned "
        "/ adoption risk\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)
