"""CLI commands for Deal Desk automation under Autopilot."""

from __future__ import annotations

import json
import os
import sys
from pathlib import Path

import click

from g_gremlin.dealdesk.config import load_dealdesk_config, DealDeskConfigError
from g_gremlin.dealdesk.planner import build_dealdesk_plan
from g_gremlin.scout.engine import ScoutEngine
from g_gremlin.scout.loader import load_scan_config

from .approvals import ApprovalTokenStore, default_approval_ttl_seconds
from .contracts import PlanV1, PlanStep
from .executor import PlanExecutor
from .policy import apply_policy_overlays, compute_policy_hash, load_policy
from .receipts import ReceiptWriter
from .slack_blocks import build_dealdesk_digest_blocks
from .slack_notify import create_client as slack_create_client
from .slack_notify import resolve_channel
from .state import resolve_state_dir
from .tools import get_default_registry


EXIT_SUCCESS = 0
EXIT_USAGE = 2
EXIT_ISSUES = 1


def _get_user_credentials() -> set[str]:
    try:
        from g_gremlin.credentials.provider import CredentialProvider

        provider = CredentialProvider()
        return set(provider.list_configured_integrations())
    except Exception:
        return set()


@click.group("dealdesk")
def dealdesk_group() -> None:
    """Deal desk automation (close verification, renewals, stamps)."""


@dealdesk_group.group("check")
def dealdesk_check_group() -> None:
    """Manage Deal Desk scout checks."""


@dealdesk_check_group.command("list")
def dealdesk_check_list() -> None:
    """List Deal Desk scout checks."""
    engine = ScoutEngine(user_credentials=_get_user_credentials())
    scouts = [s for s in engine.list_scouts() if s.name.startswith("dealdesk")]
    if not scouts:
        click.echo("No Deal Desk scouts found.")
        return
    for scout in scouts:
        click.echo(f"{scout.display_name} ({scout.name})")


@dealdesk_group.command("run")
@click.option("--since-days", default=7, type=int, show_default=True)
@click.option("--org-alias", default=None, help="Salesforce org alias.")
@click.option(
    "--config",
    "config_path",
    default=None,
    type=click.Path(exists=True, dir_okay=False, path_type=Path),
    help="Deal Desk config path (default: config/dealdesk/dealdesk.yaml).",
)
@click.option(
    "--policy",
    "policy_path",
    default=Path("autopilot_policy.yaml"),
    type=click.Path(exists=True, dir_okay=False, path_type=Path),
    help="Autopilot policy YAML path (default: ./autopilot_policy.yaml).",
)
@click.option(
    "--output",
    "output_path",
    default=None,
    type=click.Path(dir_okay=False, path_type=Path),
    help="Write plan JSON to this path.",
)
@click.option("--slack", "send_slack", is_flag=True, help="Post Deal Desk digest to Slack.")
@click.option(
    "--artifact-base-url",
    "artifact_base_url",
    default=None,
    help="Base URL for artifact links.",
)
def dealdesk_run(
    since_days: int,
    org_alias: str | None,
    config_path: Path | None,
    policy_path: Path,
    output_path: Path | None,
    send_slack: bool,
    artifact_base_url: str | None,
) -> None:
    """Run Deal Desk close verification and generate a plan."""
    try:
        config = load_dealdesk_config(config_path or None)
    except DealDeskConfigError as exc:
        click.echo(f"Error loading Deal Desk config: {exc}", err=True)
        sys.exit(EXIT_USAGE)

    try:
        policy = load_policy(policy_path)
    except Exception as exc:
        click.echo(f"Error loading policy: {exc}", err=True)
        sys.exit(EXIT_USAGE)

    state_dir = resolve_state_dir(policy)
    scan_cfg = load_scan_config()
    crm = (scan_cfg.crm or "salesforce").lower()

    engine = ScoutEngine(user_credentials=_get_user_credentials())
    params = {
        "since_days": since_days,
        "config_path": str(config_path) if config_path else "",
    }
    scan_result = engine.run_scan(
        ["dealdesk_close_verification"],
        top_n=scan_cfg.top_n,
        per_scout_params={"dealdesk_close_verification": params},
        org_alias=org_alias or policy.org_alias or scan_cfg.org_alias,
        crm=crm,
        hubspot_profile=scan_cfg.hubspot_profile,
        dynamics_profile=scan_cfg.dynamics_profile,
    )

    run_dir = state_dir / "receipts" / scan_result.run_id
    run_dir.mkdir(parents=True, exist_ok=True)
    scan_path = run_dir / "scan.json"
    scan_path.write_text(
        json.dumps(scan_result.to_dict(include_fields=True, include_reason=True), indent=2),
        encoding="utf-8",
    )

    registry = get_default_registry()
    plan = build_dealdesk_plan(
        scan_result,
        registry,
        config,
        state_dir=state_dir,
        artifact_base_url=artifact_base_url or os.getenv("G_GREMLIN_ARTIFACT_BASE_URL"),
        config_path=config_path,
    )

    plan.policy_hash = compute_policy_hash(apply_policy_overlays(policy, state_dir))
    plan_path = run_dir / "plan.json"
    plan_path.write_text(json.dumps(plan.to_dict(), indent=2), encoding="utf-8")

    if output_path:
        output_path.parent.mkdir(parents=True, exist_ok=True)
        output_path.write_text(json.dumps(plan.to_dict(), indent=2), encoding="utf-8")

    approval_token = None
    if plan.steps:
        store = ApprovalTokenStore(state_dir / "db" / "approval_tokens.sqlite")
        approval_token = store.create_token(
            plan_hash=plan.plan_hash or "",
            policy_hash=plan.policy_hash or "",
            run_id=plan.run_id,
            ttl_seconds=default_approval_ttl_seconds(),
        ).token
        pending_dir = state_dir / "pending" / plan.run_id
        pending_dir.mkdir(parents=True, exist_ok=True)
        (pending_dir / "plan.json").write_text(json.dumps(plan.to_dict(), indent=2), encoding="utf-8")

    if send_slack:
        bot_token = os.getenv("G_GREMLIN_SLACK_BOT_TOKEN")
        if not bot_token:
            click.echo("Slack bot token missing (G_GREMLIN_SLACK_BOT_TOKEN).", err=True)
        else:
            client = slack_create_client(bot_token)
            channel = resolve_channel(policy.defaults)
            if not channel:
                click.echo("Slack channel not configured.", err=True)
            else:
                index_path = (
                    state_dir / "artifacts" / scan_result.run_id / "close_packets" / "close_packets_index.json"
                )
                blocks = build_dealdesk_digest_blocks(
                    scan_result,
                    plan=plan,
                    approve_token=approval_token,
                    index_path=index_path,
                )
                text = f"Deal Desk digest for run {scan_result.run_id}"
                client.chat_postMessage(channel=channel, text=text, **blocks)

    click.echo(json.dumps(plan.to_dict(), indent=2))


@dealdesk_group.command("apply")
@click.argument(
    "plan_path",
    type=click.Path(exists=True, dir_okay=False, path_type=Path),
)
@click.option(
    "--policy",
    "policy_path",
    default=Path("autopilot_policy.yaml"),
    type=click.Path(exists=True, dir_okay=False, path_type=Path),
    help="Policy YAML path (default: ./autopilot_policy.yaml).",
)
@click.option("--approve-token", default=None, help="Approval token.")
@click.option("--dry-run", is_flag=True, help="Dry run execution.")
def dealdesk_apply(
    plan_path: Path,
    policy_path: Path,
    approve_token: str | None,
    dry_run: bool,
) -> None:
    """Apply a Deal Desk plan and write a receipt."""
    try:
        plan_payload = json.loads(plan_path.read_text(encoding="utf-8"))
        plan = PlanV1.from_dict(plan_payload)
    except Exception as exc:
        click.echo(f"Error loading plan: {exc}", err=True)
        sys.exit(EXIT_USAGE)

    try:
        policy = load_policy(policy_path)
    except Exception as exc:
        click.echo(f"Error loading policy: {exc}", err=True)
        sys.exit(EXIT_USAGE)

    state_dir = resolve_state_dir(policy)
    policy = apply_policy_overlays(policy, state_dir)
    executor = PlanExecutor(policy=policy, registry=get_default_registry(), state_dir=state_dir)
    receipt = executor.execute(plan, approve_token=approve_token, dry_run=dry_run)
    writer = ReceiptWriter(state_dir)
    receipt_dir = writer.write(receipt, plan)
    click.echo(json.dumps({"status": receipt.outcome, "receipt_dir": str(receipt_dir)}, indent=2))


@dealdesk_group.group("renewal")
def dealdesk_renewal_group() -> None:
    """Renewal helper commands."""


@dealdesk_renewal_group.command("create")
@click.option("--opp-id", required=True, help="Source Opportunity ID.")
@click.option("--org-alias", default=None, help="Salesforce org alias.")
@click.option(
    "--config",
    "config_path",
    default=None,
    type=click.Path(exists=True, dir_okay=False, path_type=Path),
    help="Deal Desk config path (default: config/dealdesk/dealdesk.yaml).",
)
def dealdesk_renewal_create(opp_id: str, org_alias: str | None, config_path: Path | None) -> None:
    """Create a renewal shell for a single opportunity."""
    from g_gremlin.dealdesk.handlers import handle_dealdesk_renewal_create_shell

    registry = get_default_registry()
    tool_def = registry.get_tool("dealdesk.renewal.create_shell")
    step = PlanStep(
        step_id="manual-renewal",
        tool="dealdesk.renewal.create_shell",
        args={
            "record_ids": [opp_id],
            "org_alias": org_alias,
            "config_path": str(config_path) if config_path else "",
        },
        risk_level=tool_def.risk_level if tool_def else 2,
    )
    result = handle_dealdesk_renewal_create_shell(step, tool_def)
    click.echo(json.dumps(result, indent=2))


@dealdesk_group.group("config")
def dealdesk_config_group() -> None:
    """Deal Desk config helpers."""


@dealdesk_config_group.command("init")
@click.option(
    "--output",
    "output_path",
    default=Path("dealdesk.yaml"),
    type=click.Path(dir_okay=False, path_type=Path),
    help="Output path (default: ./dealdesk.yaml).",
)
def dealdesk_config_init(output_path: Path) -> None:
    """Write the default Deal Desk config to disk."""
    from g_gremlin.config import get_config_dir
    default_path = get_config_dir() / "dealdesk" / "dealdesk.yaml"
    if not default_path.exists():
        click.echo("Default Deal Desk config not found.", err=True)
        sys.exit(EXIT_ISSUES)
    output_path.parent.mkdir(parents=True, exist_ok=True)
    output_path.write_text(default_path.read_text(encoding="utf-8"), encoding="utf-8")
    click.echo(f"Wrote default config to {output_path}")


__all__ = ["dealdesk_group"]
