"""Deal Desk tool handlers for Autopilot execution."""

from __future__ import annotations

from datetime import datetime, timedelta, timezone
from typing import Any

from g_gremlin import sfdc as sfdc_helpers
from g_gremlin.autopilot.ai_provider import AIProviderStore

from g_gremlin.autopilot.contracts import PlanStep
from g_gremlin.autopilot.tools import AutopilotToolDefinition

from .config import DealDeskConfig, load_dealdesk_config
from .eventing import (
    build_pattern_update_event,
    resolve_crm_org_id,
    save_pattern_update_events,
    update_context_for_approval_stamp,
    update_context_for_renewal_shell,
    utc_now_iso,
)
from .field_resolution import build_dealdesk_config_draft, build_quote_environment_profile
from .forensics import (
    build_deal_pattern_profile,
    fetch_historical_opportunities,
    suggest_opportunity_signal_fields,
)
from .readiness import evaluate_quote_readiness
from .store import DealDeskStore

_ACCOUNT_RELATIONSHIP_CANDIDATE_FIELDS: tuple[str, ...] = (
    "Name",
    "Legal_Name__c",
    "Legal_Entity__c",
    "Legal_Entity_Name__c",
    "Entity__c",
    "Billing_Entity__c",
    "Billing_Entity_Name__c",
    "Contracting_Entity__c",
    "Sold_To_Entity__c",
    "Seller_Entity__c",
)


def _as_list(value: Any) -> list[str]:
    if value is None:
        return []
    if isinstance(value, list):
        return [str(item) for item in value if item not in (None, "")]
    return [str(value)]


def _parse_date(value: Any) -> datetime | None:
    if not value:
        return None
    if isinstance(value, datetime):
        return value
    text = str(value)
    for fmt in ("%Y-%m-%d", "%Y-%m-%dT%H:%M:%S", "%Y-%m-%dT%H:%M:%S.%f"):
        try:
            dt = datetime.strptime(text, fmt)
            return dt.replace(tzinfo=timezone.utc)
        except Exception:
            continue
    try:
        return datetime.fromisoformat(text.replace("Z", "+00:00"))
    except Exception:
        return None


def _describe_fields(org_alias: str | None) -> dict[str, dict[str, Any]]:
    desc = sfdc_helpers.describe_sobject("Opportunity", org_alias=org_alias)
    fields = desc.get("result", {}).get("fields", []) if isinstance(desc, dict) else []
    mapping: dict[str, dict[str, Any]] = {}
    for field in fields or []:
        name = field.get("name")
        if name:
            mapping[str(name).lower()] = field
    return mapping


def _field_available(
    field_map: dict[str, dict[str, Any]],
    field_name: str | None,
    *,
    require_updateable: bool = False,
    require_createable: bool = False,
) -> bool:
    if not field_name:
        return False
    info = field_map.get(str(field_name).lower())
    if not info:
        return False
    if require_updateable and not info.get("updateable", True):
        return False
    if require_createable and not info.get("createable", True):
        return False
    if info.get("accessible") is False:
        return False
    return True


def _load_config_from_step(step: PlanStep) -> DealDeskConfig:
    args = step.args or {}
    config_path = args.get("config_path")
    if isinstance(config_path, dict):
        config_path = config_path.get("path")
    normalized_config_path = str(config_path or "").strip()
    if normalized_config_path:
        try:
            return load_dealdesk_config(normalized_config_path)
        except Exception:
            pass
    store = DealDeskStore(args.get("state_dir"))
    generated = store.load_generated_config(
        crm_org_id=args.get("crm_org_id"),
        org_alias=args.get("org_alias"),
    )
    if generated is not None:
        return generated
    return load_dealdesk_config()


def _load_optional_config_from_step(step: PlanStep) -> DealDeskConfig | None:
    args = step.args or {}
    config_path = args.get("config_path")
    if isinstance(config_path, dict):
        config_path = config_path.get("path")
    if config_path in (None, ""):
        return None
    return load_dealdesk_config(config_path)


def _escape_like(value: str) -> str:
    escaped = sfdc_helpers.escape_soql_value(value)
    return escaped.replace("%", "\\%").replace("_", "\\_")


def _snapshot_fields(
    *,
    sobject: str,
    record_id: str,
    fields: list[str],
    org_alias: str | None,
) -> dict[str, Any] | None:
    if not fields:
        return None
    escaped_id = sfdc_helpers.escape_soql_value(record_id)
    select_fields = ", ".join(["Id"] + fields)
    query = f"SELECT {select_fields} FROM {sobject} WHERE Id = '{escaped_id}'"
    rows = sfdc_helpers.run_soql(query, org_alias=org_alias)
    return rows[0] if rows else None


def _fetch_opportunity_snapshot(
    *,
    opportunity_id: str,
    environment_profile,
    org_alias: str | None,
) -> dict[str, Any]:
    fields = {"Id", "Name", "StageName", "CloseDate", "Amount", "AccountId", "Type"}
    try:
        describe_fields = _describe_fields(org_alias)
    except Exception:
        describe_fields = {}
    fields.update(
        suggest_opportunity_signal_fields(
            field.get("name") for field in describe_fields.values() if field.get("name")
        )
    )
    try:
        account_desc = sfdc_helpers.describe_sobject("Account", org_alias=org_alias)
        account_fields = {
            str(field.get("name") or "").strip()
            for field in (account_desc.get("result", {}).get("fields", []) or [])
            if isinstance(field, dict) and field.get("name")
        }
        for field_name in _ACCOUNT_RELATIONSHIP_CANDIDATE_FIELDS:
            if field_name in account_fields:
                fields.add(f"Account.{field_name}")
    except Exception:
        pass
    for mapping in environment_profile.field_mappings:
        if mapping.object_name.lower() == "opportunity":
            fields.add(mapping.field_name)
    escaped_id = sfdc_helpers.escape_soql_value(opportunity_id)
    query = f"SELECT {', '.join(sorted(fields))} FROM Opportunity WHERE Id = '{escaped_id}' LIMIT 1"
    rows = sfdc_helpers.run_soql(query, org_alias=org_alias)
    return rows[0] if rows else {}


def _pattern_profile_stale(pattern_profile, update_events: list[Any]) -> bool:
    baseline = _parse_date(
        getattr(pattern_profile, "updated_at", None) or getattr(pattern_profile, "generated_at", None)
    )
    if baseline is None:
        return bool(update_events)
    for event in update_events:
        event_ts = _parse_date(getattr(event, "observed_at", None))
        if event_ts and event_ts > baseline:
            return True
    return False


def handle_dealdesk_renewal_create_shell(
    step: PlanStep,
    tool_def: AutopilotToolDefinition | None,
) -> dict[str, Any]:
    args = step.args or {}
    record_ids = _as_list(args.get("record_ids") or args.get("record_id") or args.get("source_opp_id"))
    if not record_ids:
        raise RuntimeError("Missing source opportunity id for renewal shell creation.")
    source_opp_id = record_ids[0]
    org_alias = args.get("org_alias")
    run_id = args.get("run_id") or args.get("action_details", {}).get("run_id")
    preview_only = bool(args.get("preview_only"))
    store = DealDeskStore(args.get("state_dir"))
    crm_org_id = resolve_crm_org_id(org_alias=org_alias, crm_org_id=args.get("crm_org_id"))

    config = _load_config_from_step(step)
    if not config.renewal.enabled:
        return {
            "targets": [],
            "before": {"source_opp_id": source_opp_id},
            "after": {"status": "disabled"},
        }

    field_map = _describe_fields(org_alias)

    provenance_field = config.renewal.provenance_field
    provenance_available = _field_available(field_map, provenance_field, require_createable=True)

    marker = None
    if config.renewal.provenance_description_marker and run_id:
        marker = f"Created by g-gremlin run {run_id}"

    # Check for existing renewal via provenance field or description marker
    existing_id = None
    if provenance_available and provenance_field:
        escaped = sfdc_helpers.escape_soql_value(source_opp_id)
        query = (
            f"SELECT Id, CreatedDate FROM Opportunity WHERE {provenance_field} = '{escaped}'"
            " ORDER BY CreatedDate DESC LIMIT 1"
        )
        rows = sfdc_helpers.run_soql(query, org_alias=org_alias)
        if rows:
            existing_id = rows[0].get("Id")
    elif marker:
        escaped_marker = _escape_like(marker)
        query = (
            "SELECT Id, CreatedDate FROM Opportunity "
            f"WHERE Description LIKE '%{escaped_marker}%' "
            "ORDER BY CreatedDate DESC LIMIT 1"
        )
        rows = sfdc_helpers.run_soql(query, org_alias=org_alias)
        if rows:
            existing_id = rows[0].get("Id")

    if existing_id:
        observed_at = utc_now_iso()
        if not preview_only:
            event = build_pattern_update_event(
                source_type="renewal_shell",
                crm_org_id=crm_org_id,
                opportunity_id=source_opp_id,
                source_ref=f"dealdesk.renewal.create_shell:{source_opp_id}:{existing_id}",
                observed_at=observed_at,
                source_receipt_id=str(run_id or "").strip() or None,
                raw_record={"Id": source_opp_id, "ExistingRenewalId": existing_id},
                extracted_motion_id="renewal",
                confidence=0.98,
                outcome="exists",
                custom={"existing_renewal_opportunity_id": existing_id},
                evidence_ids=[source_opp_id, str(existing_id)],
            )
            save_pattern_update_events(
                store=store,
                events=[event],
                org_alias=org_alias,
                config=config,
                refresh_profile=True,
            )
            update_context_for_renewal_shell(
                store=store,
                crm_org_id=crm_org_id,
                org_alias=org_alias,
                opportunity_id=source_opp_id,
                renewal_opportunity_id=str(existing_id),
                observed_at=observed_at,
                receipt_id=str(run_id or "").strip() or None,
            )
        return {
            "targets": [existing_id],
            "before": {"source_opp_id": source_opp_id},
            "after": {"status": "exists", "existing_id": existing_id, "preview_only": preview_only},
        }

    fields_to_fetch = {"Id", "Name", "CloseDate", "Description", "AccountId"}
    fields_to_fetch.update(config.renewal.copy_fields or [])
    term_field = config.amount_rules.term_months_field
    if term_field:
        fields_to_fetch.add(term_field)
    escaped_id = sfdc_helpers.escape_soql_value(source_opp_id)
    query = f"SELECT {', '.join(sorted(fields_to_fetch))} FROM Opportunity WHERE Id = '{escaped_id}'"
    rows = sfdc_helpers.run_soql(query, org_alias=org_alias)
    if not rows:
        raise RuntimeError(f"Opportunity not found: {source_opp_id}")
    source = rows[0]

    close_date = _parse_date(source.get("CloseDate"))
    if close_date is None:
        close_date = datetime.now(timezone.utc)
    renewal_close = close_date + timedelta(days=config.renewal.date_offset_days)
    renewal_year = renewal_close.year

    name_template = config.renewal.name_template or "{original_name} - Renewal {year}"
    renewal_name = name_template.format(
        original_name=source.get("Name") or "Opportunity",
        year=renewal_year,
    )

    values: dict[str, Any] = {
        "Name": renewal_name,
        "StageName": config.renewal.stage_default,
        "CloseDate": renewal_close.date().isoformat(),
    }

    for field in config.renewal.copy_fields or []:
        if not _field_available(field_map, field, require_createable=True):
            continue
        if field in source:
            values[field] = source.get(field)

    if term_field and _field_available(field_map, term_field, require_createable=True):
        values[term_field] = source.get(term_field) or config.renewal.term_default_months

    if provenance_available and provenance_field:
        values[provenance_field] = source_opp_id

    if marker and _field_available(field_map, "Description", require_createable=True):
        base_desc = source.get("Description")
        if base_desc:
            values["Description"] = f"{base_desc}\n\n{marker}"
        else:
            values["Description"] = marker

    if preview_only:
        return {
            "targets": [],
            "before": {"source_opp_id": source_opp_id},
            "after": {
                "status": "preview",
                "preview_only": True,
                "provenance_marker": marker,
                "values": values,
            },
        }

    result = sfdc_helpers.create_record("Opportunity", values, org_alias=org_alias)
    created_id = None
    if isinstance(result, dict):
        created_id = (
            (result.get("result") or {}).get("id")
            or result.get("id")
            or result.get("recordId")
        )

    observed_at = utc_now_iso()
    if created_id:
        event = build_pattern_update_event(
            source_type="renewal_shell",
            crm_org_id=crm_org_id,
            opportunity_id=source_opp_id,
            source_ref=f"dealdesk.renewal.create_shell:{source_opp_id}:{created_id}",
            observed_at=observed_at,
            source_receipt_id=str(run_id or "").strip() or None,
            raw_record={**source, **values},
            extracted_motion_id="renewal",
            confidence=0.98,
            outcome="created",
            custom={"created_renewal_opportunity_id": created_id},
            evidence_ids=[source_opp_id, created_id],
        )
        save_pattern_update_events(
            store=store,
            events=[event],
            org_alias=org_alias,
            config=config,
            refresh_profile=True,
        )
        update_context_for_renewal_shell(
            store=store,
            crm_org_id=crm_org_id,
            org_alias=org_alias,
            opportunity_id=source_opp_id,
            renewal_opportunity_id=created_id,
            observed_at=observed_at,
            receipt_id=str(run_id or "").strip() or None,
            account_id=str(source.get("AccountId") or "").strip() or None,
        )

    return {
        "targets": [created_id] if created_id else [],
        "before": {"source_opp_id": source_opp_id},
        "after": {
            "status": "created" if created_id else "unknown",
            "created_id": created_id,
            "provenance_marker": marker,
            "values": values,
            "preview_only": False,
        },
    }


def handle_dealdesk_approval_stamp(
    step: PlanStep,
    tool_def: AutopilotToolDefinition | None,
) -> dict[str, Any]:
    args = step.args or {}
    record_ids = _as_list(args.get("record_ids") or args.get("record_id"))
    if not record_ids:
        raise RuntimeError("Missing record_id for dealdesk approval stamp.")
    record_id = record_ids[0]
    org_alias = args.get("org_alias")
    run_id = args.get("run_id") or args.get("action_details", {}).get("run_id")
    store = DealDeskStore(args.get("state_dir"))
    crm_org_id = resolve_crm_org_id(org_alias=org_alias, crm_org_id=args.get("crm_org_id"))

    fields = args.get("fields") or args.get("stamp_fields") or {}
    if not isinstance(fields, dict):
        fields = {}

    if "DealDesk_Verified__c" not in fields:
        fields["DealDesk_Verified__c"] = True
    if "DealDesk_Verified_At__c" not in fields:
        fields["DealDesk_Verified_At__c"] = datetime.now(timezone.utc).isoformat()

    verified_by = args.get("verified_by") or args.get("actor")
    if verified_by and "DealDesk_Verified_By__c" not in fields:
        fields["DealDesk_Verified_By__c"] = verified_by

    before = _snapshot_fields(
        sobject="Opportunity",
        record_id=record_id,
        fields=list(fields.keys()),
        org_alias=org_alias,
    )
    result = sfdc_helpers.update_record("Opportunity", record_id, fields, org_alias=org_alias)
    observed_at = str(fields.get("DealDesk_Verified_At__c") or utc_now_iso())
    approver = args.get("verified_by") or args.get("actor") or fields.get("DealDesk_Verified_By__c")
    event = build_pattern_update_event(
        source_type="approval_stamp",
        crm_org_id=crm_org_id,
        opportunity_id=record_id,
        source_ref=f"dealdesk.approval.stamp:{record_id}",
        observed_at=observed_at,
        source_receipt_id=str(run_id or "").strip() or None,
        raw_record=before or {},
        confidence=0.99,
        approver_roles=[args.get("actor_role") or args.get("approval_role")],
        outcome="approved",
        custom={"verified_by": approver},
        evidence_ids=[record_id],
    )
    save_pattern_update_events(
        store=store,
        events=[event],
        org_alias=org_alias,
        refresh_profile=True,
    )
    update_context_for_approval_stamp(
        store=store,
        crm_org_id=crm_org_id,
        org_alias=org_alias,
        opportunity_id=record_id,
        observed_at=observed_at,
        actor=str(approver or "").strip() or None,
        actor_role=str(args.get("actor_role") or args.get("approval_role") or "").strip() or None,
        channel=str(args.get("channel") or "").strip() or None,
        rationale=str(args.get("rationale") or "").strip() or None,
        receipt_id=str(run_id or "").strip() or None,
        account_id=str((before or {}).get("AccountId") or "").strip() or None,
    )

    return {
        "targets": [record_id],
        "before": before or {"record_id": record_id},
        "after": {
            "updated": fields,
            "result": result,
            "rollback_data": {
                "records": [before] if isinstance(before, dict) else [],
                "sobject": "Opportunity",
            },
        },
    }


def handle_dealdesk_quote_readiness(
    step: PlanStep,
    tool_def: AutopilotToolDefinition | None,
) -> dict[str, Any]:
    del tool_def
    args = step.args or {}
    opportunity_id = str(
        args.get("opportunity_id") or args.get("record_id") or args.get("opportunityId") or ""
    ).strip()
    if not opportunity_id:
        raise RuntimeError("Missing opportunity_id for dealdesk quote readiness.")

    org_alias = str(args.get("org_alias") or "").strip() or None
    force_refresh = bool(args.get("force_refresh"))
    store = DealDeskStore(args.get("state_dir"))
    ai_config = AIProviderStore(store.state_dir).load_config()
    crm_org_id = resolve_crm_org_id(org_alias=org_alias, crm_org_id=args.get("crm_org_id"))
    generated_at = datetime.now(timezone.utc).isoformat()

    environment_profile = None if force_refresh else store.load_quote_environment_profile(
        crm_org_id=crm_org_id,
        org_alias=org_alias,
    )
    environment_profile_path = store.quote_environment_profile_path(crm_org_id=crm_org_id, org_alias=org_alias)
    config = _load_optional_config_from_step(step)
    config_draft_path = store.generated_config_path(crm_org_id=crm_org_id, org_alias=org_alias)
    if environment_profile is None:
        environment_profile = build_quote_environment_profile(
            crm_org_id=crm_org_id,
            generated_at=generated_at,
            org_alias=org_alias,
            ai_config=ai_config,
        )
        environment_profile_path = store.save_quote_environment_profile(environment_profile)
        generated_config = build_dealdesk_config_draft(
            environment_profile,
            base_config=config,
        )
        config_draft_path = store.save_generated_config(
            generated_config,
            crm_org_id=crm_org_id,
            org_alias=org_alias,
        )

    if config is None:
        config = store.load_generated_config(crm_org_id=crm_org_id, org_alias=org_alias)
    if config is None:
        config = build_dealdesk_config_draft(environment_profile)
        config_draft_path = store.save_generated_config(
            config,
            crm_org_id=crm_org_id,
            org_alias=org_alias,
        )

    update_events = store.list_pattern_update_events(crm_org_id=crm_org_id, org_alias=org_alias)
    historical_rows: list[dict[str, Any]] = []
    pattern_profile = None if force_refresh else store.load_deal_pattern_profile(
        crm_org_id=crm_org_id,
        org_alias=org_alias,
    )
    pattern_profile_path = store.deal_pattern_profile_path(crm_org_id=crm_org_id, org_alias=org_alias)
    if pattern_profile is None or _pattern_profile_stale(pattern_profile, update_events):
        historical_rows = fetch_historical_opportunities(org_alias=org_alias)
        pattern_profile = build_deal_pattern_profile(
            crm_org_id=crm_org_id,
            generated_at=generated_at,
            records=historical_rows,
            org_alias=org_alias,
            config=config,
            update_events=update_events,
        )
        pattern_profile_path = store.save_deal_pattern_profile(pattern_profile)
    if not historical_rows:
        historical_rows = fetch_historical_opportunities(org_alias=org_alias)

    opportunity_snapshot = _fetch_opportunity_snapshot(
        opportunity_id=opportunity_id,
        environment_profile=environment_profile,
        org_alias=org_alias,
    )
    existing_context = store.load_context(
        opportunity_id=opportunity_id,
        crm_org_id=crm_org_id,
        org_alias=org_alias,
    )
    readiness, context = evaluate_quote_readiness(
        opportunity_id=opportunity_id,
        crm_org_id=crm_org_id,
        generated_at=generated_at,
        environment_profile=environment_profile,
        pattern_profile=pattern_profile,
        quote_request=args.get("quote_request") if isinstance(args.get("quote_request"), dict) else {},
        opportunity_snapshot=opportunity_snapshot,
        existing_context=existing_context,
        historical_records=historical_rows,
        update_events=update_events,
    )
    context_path = store.save_context(context)
    return {
        "targets": [opportunity_id],
        "before": {
            "opportunity_id": opportunity_id,
            "context_exists": existing_context is not None,
        },
        "after": {
            "status": readiness.readiness_mode,
            "detected_engine": environment_profile.detected_engine,
            "engine_confidence": environment_profile.engine_confidence,
            "readiness": readiness.to_dict(),
            "context_id": context.context_id,
            "context_path": str(context_path),
            "environment_profile_id": environment_profile.profile_id,
            "environment_profile_path": str(environment_profile_path),
            "pattern_profile_id": pattern_profile.profile_id,
            "pattern_profile_path": str(pattern_profile_path),
            "config_draft_path": str(config_draft_path),
            "source_record_count": pattern_profile.source_record_count,
        },
    }


__all__ = [
    "handle_dealdesk_renewal_create_shell",
    "handle_dealdesk_approval_stamp",
    "handle_dealdesk_quote_readiness",
]
