"""get_portfolio_insights tool -- cross-customer license intelligence.

Queries ALL active production licenses from the LMA org in bulk,
computes deterministic scores for renewal urgency, seat expansion,
version currency, and customer size, then produces a priority-ranked
portfolio report with heatmap and file artifacts.

The scoring logic lives here (not in the AI) so results are
consistent, auditable, and repeatable across conversations.
"""

import json
import logging
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__)

TOOL_DEFINITION = {
    "name": "get_portfolio_insights",
    "description": (
        "Analyze the ENTIRE CloudSense customer portfolio for renewal, "
        "upsell, and upgrade opportunities. Queries all active production "
        "licenses from the LMA org, computes priority scores using defined "
        "rules (not AI-invented), and produces a ranked report with heatmap. "
        "Use this for any cross-customer analysis: 'which customers need "
        "renewal?', 'who has upsell opportunity?', 'show me the portfolio'. "
        "Requires connect_lma to have been called first."
    ),
    "inputSchema": {
        "type": "object",
        "properties": {
            "min_seats": {
                "type": "integer",
                "description": (
                    "Optional filter: only include customers with at least "
                    "this many total seats. Default: no filter."
                ),
            },
            "min_packages": {
                "type": "integer",
                "description": (
                    "Optional filter: only include customers with at least "
                    "this many packages. Default: no filter."
                ),
            },
            "expiry_horizon_days": {
                "type": "integer",
                "description": (
                    "Only include customers with licenses expiring within "
                    "this many days. Default: 365."
                ),
            },
        },
        "required": [],
    },
}


# ── Scoring rules (deterministic, not AI-decided) ──────────────

def _renewal_score(min_days: float | None) -> tuple[int, str]:
    """Score renewal urgency based on nearest license expiry."""
    if min_days is None:
        return 0, "OK"
    if min_days < 30:
        return 100, "CRITICAL"
    if min_days < 90:
        return 80, "URGENT"
    if min_days < 180:
        return 50, "WATCH"
    if min_days < 365:
        return 20, "PLAN"
    return 0, "OK"


def _seat_score(max_util_pct: float) -> tuple[int, str]:
    """Score seat expansion need based on highest utilization."""
    if max_util_pct >= 100:
        return 100, "MAXED"
    if max_util_pct >= 90:
        return 80, "HIGH"
    if max_util_pct >= 75:
        return 50, "MODERATE"
    return 0, "LOW"


def _version_score(packages_behind: int) -> tuple[int, str]:
    """Score version currency based on packages not on latest."""
    if packages_behind >= 3:
        return 100, "STALE"
    if packages_behind >= 1:
        return 60, "BEHIND"
    return 0, "CURRENT"


def _size_weight(total_seats: int, package_count: int) -> float:
    """Size multiplier for prioritization."""
    if total_seats > 500 or package_count > 10:
        return 1.5
    if total_seats >= 100 or package_count >= 5:
        return 1.0
    return 0.7


def _combined_priority(
    renewal: int, seats: int, version: int, size_w: float
) -> int:
    """Compute weighted priority score (0-100)."""
    raw = (renewal * 0.35) + (seats * 0.25) + (version * 0.20) + (20 * size_w)
    return min(100, int(round(raw)))


def _tier_label(priority: int) -> str:
    if priority >= 70:
        return "Tier 1 -- Immediate Action"
    if priority >= 40:
        return "Tier 2 -- Plan"
    return "Tier 3 -- Monitor"


# ── Helpers ─────────────────────────────────────────────────────

def _safe_float(val: Any) -> float | None:
    if val is None:
        return None
    try:
        return float(val)
    except (ValueError, TypeError):
        return None


def _safe_int(val: Any) -> int:
    try:
        return int(val)
    except (ValueError, TypeError):
        return 0


def _get_lma_alias() -> str:
    """Try to find LMA alias from any state file in cwd, else default."""
    cwd = Path.cwd()
    existing = state.load_state(cwd)
    if existing and existing.get("lma_org_alias"):
        return existing["lma_org_alias"]
    return "cs-lma"


def _bulk_query_licenses(lma_alias: str) -> list[dict[str, Any]]:
    """Query all active production licenses from LMA."""
    query = (
        "SELECT sfLma__Account__r.Name, sfLma__Account__r.Id, "
        "Package_Name__c, Package_Version_Number__c, "
        "sfLma__License_Status__c, sfLma__Expiration_Date__c, "
        "Days_To_Expire__c, sfLma__Seat_Count__c, "
        "sfLma__Used_Licenses__c, CSLabs1__Unused_Licenses__c, "
        "sfLma__Package_Version__r.sfLma__Package__c "
        "FROM sfLma__License__c "
        "WHERE sfLma__Is_Sandbox__c = false "
        "AND sfLma__License_Status__c = 'Active' "
        "AND Package_Version_Number__c != '' "
        "AND sfLma__Account__r.Name != null"
    )
    try:
        return sf_cli.lma_query(query, lma_alias)
    except sf_cli.SfCliError as e:
        logger.warning("Bulk license query failed: %s", e)
        return []


def _query_latest_versions(
    package_ids: set[str], lma_alias: str
) -> dict[str, str]:
    """Get latest version number for each package ID."""
    latest: dict[str, str] = {}
    for pkg_id in package_ids:
        query = (
            f"SELECT sfLma__Version_Number__c "
            f"FROM sfLma__Package_Version__c "
            f"WHERE sfLma__Package__c = '{pkg_id}' "
            f"ORDER BY sfLma__Release_Date__c DESC "
            f"LIMIT 1"
        )
        try:
            records = sf_cli.lma_query(query, lma_alias)
            if records:
                ver = records[0].get("sfLma__Version_Number__c", "")
                if ver:
                    latest[pkg_id] = ver
        except sf_cli.SfCliError:
            pass
    return latest


# ── Main ────────────────────────────────────────────────────────

async def run(arguments: dict[str, Any]) -> str:
    """Execute the get_portfolio_insights tool."""
    min_seats = arguments.get("min_seats", 0)
    min_packages = arguments.get("min_packages", 0)
    expiry_horizon = arguments.get("expiry_horizon_days", 365)

    lma_alias = _get_lma_alias()

    report: list[str] = []
    report.append("# Portfolio Insights\n")
    report.append(f"- LMA org: {lma_alias}")
    report.append(f"- Generated: {datetime.now(timezone.utc).strftime('%Y-%m-%d %H:%M UTC')}")
    if min_seats:
        report.append(f"- Filter: min {min_seats} seats")
    if min_packages:
        report.append(f"- Filter: min {min_packages} packages")
    report.append("")

    # ── 1. Bulk query ──────────────────────────────────────────
    all_licenses = _bulk_query_licenses(lma_alias)
    if not all_licenses:
        report.append("No active production licenses found in LMA.")
        report.append("\n## STATUS: NO DATA\n")
        return "\n".join(report)

    report.append(f"- Total active production licenses: {len(all_licenses)}")

    # ── 2. Group by customer ───────────────────────────────────
    customers: dict[str, list[dict]] = defaultdict(list)
    for lic in all_licenses:
        acct_ref = lic.get("sfLma__Account__r")
        if isinstance(acct_ref, dict):
            acct_name = acct_ref.get("Name", "Unknown")
        else:
            continue
        customers[acct_name].append(lic)

    report.append(f"- Unique customers: {len(customers)}")

    # ── 3. Collect unique package IDs for version lookup ───────
    package_ids: set[str] = set()
    for licenses in customers.values():
        for lic in licenses:
            pkg_ref = lic.get("sfLma__Package_Version__r")
            if isinstance(pkg_ref, dict):
                pid = pkg_ref.get("sfLma__Package__c", "")
                if pid:
                    package_ids.add(pid)

    report.append(f"- Unique packages: {len(package_ids)}")
    report.append(f"- Querying latest version for each package...")

    latest_versions = _query_latest_versions(package_ids, lma_alias)
    report.append(f"- Latest versions resolved: {len(latest_versions)}\n")

    # ── 4. Score each customer ─────────────────────────────────
    scored: list[dict[str, Any]] = []

    for acct_name, licenses in customers.items():
        acct_ref = licenses[0].get("sfLma__Account__r", {})
        acct_id = acct_ref.get("Id", "") if isinstance(acct_ref, dict) else ""

        package_count = len(set(
            l.get("Package_Name__c", "") for l in licenses
        ))
        total_seats = sum(_safe_int(l.get("sfLma__Seat_Count__c")) for l in licenses)

        if total_seats < min_seats or package_count < min_packages:
            continue

        # Renewal
        days_values = [_safe_float(l.get("Days_To_Expire__c")) for l in licenses]
        days_values = [d for d in days_values if d is not None]
        min_days = min(days_values) if days_values else None

        if min_days is not None and min_days > expiry_horizon:
            pass  # still include, but renewal score will be low

        renewal_sc, renewal_label = _renewal_score(min_days)

        # Seats
        max_util = 0.0
        high_util_packages = 0
        for lic in licenses:
            seat_count = _safe_float(lic.get("sfLma__Seat_Count__c"))
            used = _safe_float(lic.get("sfLma__Used_Licenses__c"))
            if seat_count and seat_count > 0 and used is not None:
                util = (used / seat_count) * 100
                max_util = max(max_util, util)
                if util >= 90:
                    high_util_packages += 1

        seat_sc, seat_label = _seat_score(max_util)

        # Version
        packages_behind = 0
        for lic in licenses:
            installed = lic.get("Package_Version_Number__c", "")
            pkg_ref = lic.get("sfLma__Package_Version__r")
            if isinstance(pkg_ref, dict):
                pid = pkg_ref.get("sfLma__Package__c", "")
                latest = latest_versions.get(pid, "")
                if latest and installed and latest != installed:
                    packages_behind += 1

        version_sc, version_label = _version_score(packages_behind)

        # Size & priority
        size_w = _size_weight(total_seats, package_count)
        priority = _combined_priority(renewal_sc, seat_sc, version_sc, size_w)
        tier = _tier_label(priority)

        scored.append({
            "customer": acct_name,
            "account_id": acct_id,
            "package_count": package_count,
            "total_seats": total_seats,
            "min_days_to_expiry": round(min_days, 1) if min_days is not None else None,
            "renewal_score": renewal_sc,
            "renewal_label": renewal_label,
            "max_utilization_pct": round(max_util, 1),
            "high_util_packages": high_util_packages,
            "seat_score": seat_sc,
            "seat_label": seat_label,
            "packages_behind": packages_behind,
            "version_score": version_sc,
            "version_label": version_label,
            "size_weight": size_w,
            "priority": priority,
            "tier": tier,
        })

    scored.sort(key=lambda c: c["priority"], reverse=True)

    # ── 5. Executive summary ───────────────────────────────────
    tier1 = [c for c in scored if c["priority"] >= 70]
    tier2 = [c for c in scored if 40 <= c["priority"] < 70]
    tier3 = [c for c in scored if c["priority"] < 40]

    critical_renewals = [c for c in scored if c["renewal_label"] in ("CRITICAL", "URGENT")]
    maxed_seats = [c for c in scored if c["seat_label"] in ("MAXED", "HIGH")]
    stale_versions = [c for c in scored if c["version_label"] == "STALE"]

    report.append("## Executive Summary\n")
    report.append(f"- **{len(scored)}** customers analyzed")
    report.append(f"- **{len(tier1)}** Tier 1 (Immediate Action)")
    report.append(f"- **{len(tier2)}** Tier 2 (Plan)")
    report.append(f"- **{len(tier3)}** Tier 3 (Monitor)")
    report.append(f"- **{len(critical_renewals)}** with critical/urgent renewals")
    report.append(f"- **{len(maxed_seats)}** with high seat utilization (90%+)")
    report.append(f"- **{len(stale_versions)}** with 3+ packages behind latest version")

    # ── 6. Portfolio heatmap ───────────────────────────────────
    report.append("\n## Portfolio Heatmap\n")
    report.append("| Customer | Pkgs | Seats | Renewal | Seats | Version | Priority |")
    report.append("|----------|------|-------|---------|-------|---------|----------|")

    for c in scored[:50]:
        days_str = f"{int(c['min_days_to_expiry'])}d" if c["min_days_to_expiry"] is not None else "—"
        report.append(
            f"| {c['customer']} | {c['package_count']} | {c['total_seats']} "
            f"| {c['renewal_label']} ({days_str}) "
            f"| {c['seat_label']} ({c['max_utilization_pct']:.0f}%) "
            f"| {c['version_label']} ({c['packages_behind']}) "
            f"| **{c['priority']}** |"
        )

    if len(scored) > 50:
        report.append(f"\n_...and {len(scored) - 50} more customers in the JSON artifact._")

    # ── 7. Tier 1 detail ───────────────────────────────────────
    if tier1:
        report.append("\n## Tier 1 -- Immediate Action\n")
        for c in tier1:
            days_str = f"{int(c['min_days_to_expiry'])} days" if c["min_days_to_expiry"] is not None else "N/A"
            report.append(f"### {c['customer']} (Priority: {c['priority']})\n")
            report.append(f"- Packages: {c['package_count']}, Seats: {c['total_seats']}")
            report.append(f"- Renewal: {c['renewal_label']} ({days_str})")
            report.append(
                f"- Seat utilization: {c['seat_label']} "
                f"(peak {c['max_utilization_pct']:.0f}%, "
                f"{c['high_util_packages']} package(s) at 90%+)"
            )
            report.append(f"- Version: {c['version_label']} ({c['packages_behind']} behind)")
            report.append("")

    # ── 8. Top opportunities ───────────────────────────────────
    report.append("\n## Top 10 Renewal Opportunities\n")
    renewals_sorted = sorted(
        [c for c in scored if c["min_days_to_expiry"] is not None],
        key=lambda c: c["min_days_to_expiry"],
    )[:10]
    if renewals_sorted:
        report.append("| Customer | Expires In | Packages | Seats |")
        report.append("|----------|-----------|----------|-------|")
        for c in renewals_sorted:
            report.append(
                f"| {c['customer']} | {int(c['min_days_to_expiry'])} days "
                f"| {c['package_count']} | {c['total_seats']} |"
            )

    report.append("\n## Top 10 Seat Expansion Opportunities\n")
    seats_sorted = sorted(scored, key=lambda c: c["max_utilization_pct"], reverse=True)[:10]
    if seats_sorted:
        report.append("| Customer | Peak Utilization | Pkgs at 90%+ | Total Seats |")
        report.append("|----------|-----------------|-------------|-------------|")
        for c in seats_sorted:
            report.append(
                f"| {c['customer']} | {c['max_utilization_pct']:.0f}% "
                f"| {c['high_util_packages']} | {c['total_seats']} |"
            )

    report.append("\n## Top 10 Version Upgrade Opportunities\n")
    version_sorted = sorted(scored, key=lambda c: c["packages_behind"], reverse=True)[:10]
    if version_sorted:
        report.append("| Customer | Packages Behind | Total Packages | Seats |")
        report.append("|----------|----------------|---------------|-------|")
        for c in version_sorted:
            report.append(
                f"| {c['customer']} | {c['packages_behind']} "
                f"| {c['package_count']} | {c['total_seats']} |"
            )

    # ── 9. Combined opportunities ──────────────────────────────
    combined = [
        c for c in scored
        if c["renewal_label"] in ("CRITICAL", "URGENT", "WATCH")
        and c["seat_label"] in ("MAXED", "HIGH")
    ]
    if combined:
        report.append("\n## Combined Opportunities (Renewal + Seat Expansion)\n")
        report.append("| Customer | Renewal | Seat Util | Priority |")
        report.append("|----------|---------|-----------|----------|")
        for c in sorted(combined, key=lambda c: c["priority"], reverse=True):
            days_str = f"{int(c['min_days_to_expiry'])}d" if c["min_days_to_expiry"] is not None else "—"
            report.append(
                f"| {c['customer']} | {c['renewal_label']} ({days_str}) "
                f"| {c['seat_label']} ({c['max_utilization_pct']:.0f}%) "
                f"| **{c['priority']}** |"
            )

    # ── 10. Write artifacts ────────────────────────────────────
    cwd = Path.cwd()
    artifact_path = cwd / "portfolio-insights.json"
    report_path = cwd / "portfolio-insights.md"

    artifact = {
        "generated_at": datetime.now(timezone.utc).isoformat(),
        "lma_org": lma_alias,
        "total_licenses_queried": len(all_licenses),
        "customers_analyzed": len(scored),
        "filters": {
            "min_seats": min_seats,
            "min_packages": min_packages,
            "expiry_horizon_days": expiry_horizon,
        },
        "summary": {
            "tier1_count": len(tier1),
            "tier2_count": len(tier2),
            "tier3_count": len(tier3),
            "critical_renewals": len(critical_renewals),
            "high_utilization": len(maxed_seats),
            "stale_versions": len(stale_versions),
        },
        "scoring_rules": {
            "renewal_weight": 0.35,
            "seats_weight": 0.25,
            "version_weight": 0.20,
            "size_weight": 0.20,
        },
        "customers": scored,
    }

    try:
        artifact_path.write_text(json.dumps(artifact, indent=2, default=str))
        report_text = "\n".join(report)
        report_path.write_text(report_text)
        report.append("\n## Artifacts\n")
        report.append(f"- JSON: {artifact_path}")
        report.append(f"- Report: {report_path}")
    except OSError as e:
        logger.warning("Could not write artifacts: %s", e)
        report.append(f"\n_Could not write files: {e}_")

    # ── 11. Scoring methodology ────────────────────────────────
    report.append("\n---\n")
    report.append("## Scoring Methodology\n")
    report.append(
        "All scores are computed by the tool using fixed rules "
        "(not AI-generated). Priority = "
        "(Renewal x 0.35) + (Seats x 0.25) + (Version x 0.20) + (Size x 0.20)\n"
    )
    report.append("- **Renewal:** CRITICAL (<30d)=100, URGENT (<90d)=80, WATCH (<180d)=50, PLAN (<365d)=20")
    report.append("- **Seats:** MAXED (100%)=100, HIGH (90%+)=80, MODERATE (75%+)=50")
    report.append("- **Version:** STALE (3+ behind)=100, BEHIND (1-2)=60, CURRENT=0")
    report.append("- **Size:** Large (>500 seats or >10 pkgs) x1.5, Medium x1.0, Small x0.7")

    report.append("\n## STATUS: READY\n")

    return "\n".join(report)
