"""find_customer tool -- search for a customer in the LMA org.

Uses SOSL (fuzzy/phonetic) as the primary search, falling back to
SOQL LIKE if SOSL returns no results or fails. Stores the selected
account in state for use by get_customer_licenses.
"""

import logging
from pathlib import Path
from typing import Any

from ..utils import sf_cli, state

logger = logging.getLogger(__name__)

TOOL_DEFINITION = {
    "name": "find_customer",
    "description": (
        "Search for a customer by name in the CloudSense LMA org. "
        "Uses SOSL fuzzy search (handles misspellings and partial matches) "
        "with SOQL LIKE as fallback. If exactly one match is found, returns "
        "it directly. If multiple matches are found, returns the list so the "
        "AI can ask the user to confirm. Requires connect_lma to have been "
        "called first."
    ),
    "inputSchema": {
        "type": "object",
        "properties": {
            "customer_name": {
                "type": "string",
                "description": (
                    "Customer name to search for. Can be partial or fuzzy -- "
                    "the tool handles misspellings. "
                    "Examples: 'nbnco', 'NBN Co', 'starhub', 'maxis'"
                ),
            },
            "working_directory": {
                "type": "string",
                "description": (
                    "Absolute path to the working directory where state is stored. "
                    "If provided, reads LMA org alias from state and stores the "
                    "selected customer account. If not provided, uses default "
                    "LMA alias 'cs-lma'."
                ),
            },
        },
        "required": ["customer_name"],
    },
}


def _get_lma_alias(working_dir: Path | None) -> str:
    """Read the LMA org alias from state, or return default."""
    if working_dir:
        existing = state.load_state(working_dir)
        if existing and existing.get("lma_org_alias"):
            return existing["lma_org_alias"]
    return "cs-lma"


def _sosl_search(name: str, lma_alias: str) -> list[dict[str, Any]]:
    """Run SOSL fuzzy search against Account."""
    try:
        results = sf_cli.sosl_search(
            search_term=name,
            returning="Account(Id, Name)",
            target_org=lma_alias,
        )
        return results
    except sf_cli.SfCliError as e:
        logger.warning("SOSL search failed: %s", e)
        return []


def _soql_fallback(name: str, lma_alias: str) -> list[dict[str, Any]]:
    """Run SOQL LIKE fallback search against Account."""
    safe_name = name.replace("'", "\\'")
    query = (
        f"SELECT Id, Name FROM Account "
        f"WHERE Name LIKE '%{safe_name}%' "
        f"ORDER BY Name LIMIT 20"
    )
    return sf_cli.lma_query(query, lma_alias)


async def run(arguments: dict[str, Any]) -> str:
    """Execute the find_customer tool."""
    customer_name = arguments["customer_name"]
    working_dir_str = arguments.get("working_directory")
    working_dir = Path(working_dir_str) if working_dir_str else None

    lma_alias = _get_lma_alias(working_dir)

    report: list[str] = []
    report.append("# Find Customer in LMA\n")
    report.append(f"- Search term: **{customer_name}**")
    report.append(f"- LMA org: {lma_alias}\n")

    # ── 1. Try SOSL first ──────────────────────────────────────
    report.append("## Search Results\n")

    results = _sosl_search(customer_name, lma_alias)
    search_method = "SOSL"

    if not results:
        logger.info("SOSL returned 0 results, falling back to SOQL LIKE")
        results = _soql_fallback(customer_name, lma_alias)
        search_method = "SOQL LIKE (fallback)"

    report.append(f"- Search method: {search_method}")
    report.append(f"- Matches found: {len(results)}\n")

    # ── 2. Handle results ──────────────────────────────────────
    if not results:
        report.append(
            "No matching accounts found. Suggestions:\n"
            "- Try a shorter or different search term\n"
            "- Check the spelling of the customer name\n"
            "- Verify the LMA org is correct"
        )
        report.append("\n## STATUS: NO MATCHES\n")
        return "\n".join(report)

    if len(results) == 1:
        account = results[0]
        acct_id = account.get("Id", "")
        acct_name = account.get("Name", "")

        report.append(f"**Exact match found:**\n")
        report.append(f"- Account Name: {acct_name}")
        report.append(f"- Account ID: {acct_id}")

        if working_dir:
            existing = state.load_state(working_dir)
            if existing:
                state.update_state(
                    working_dir,
                    lma_customer_account_id=acct_id,
                    lma_customer_account_name=acct_name,
                )
                report.append(f"\n- Customer saved to state file")

        report.append("\n---\n")
        report.append("## STATUS: READY\n")
        report.append(
            f"Customer identified: **{acct_name}** ({acct_id}).\n\n"
            f"Next step: call **get_customer_licenses** with "
            f"account_id='{acct_id}' and account_name='{acct_name}' "
            f"to retrieve their installed packages and license details."
        )
        return "\n".join(report)

    # Multiple matches
    report.append("**Multiple matches found** -- please confirm:\n")
    report.append("| # | Account Name | Account ID |")
    report.append("|---|-------------|------------|")
    for i, account in enumerate(results, 1):
        acct_id = account.get("Id", "")
        acct_name = account.get("Name", "")
        report.append(f"| {i} | {acct_name} | {acct_id} |")

    report.append(
        "\nAsk the user which account is correct, then call "
        "find_customer again with the exact name, or proceed "
        "directly to get_customer_licenses with the confirmed "
        "account_id and account_name."
    )
    report.append("\n## STATUS: NEEDS CONFIRMATION\n")
    report.append(
        "Multiple accounts matched. The AI should ask the user "
        "to confirm which account is the correct one."
    )

    return "\n".join(report)
