"""Durable quote workflow orchestration for Deal Desk quote execution."""

from __future__ import annotations

import re
from typing import TYPE_CHECKING, Any, Literal
from uuid import uuid4

from pydantic import BaseModel, ConfigDict, Field, computed_field

from g_gremlin.autopilot.contracts import PlanStep

from .context import QuotePdfStatus, QuoteWorkflowState
from .eventing import (
    resolve_crm_org_id,
    update_context_for_quote_workflow_transition,
    utc_now_iso,
)
from .quote_handlers import (
    handle_dealdesk_quote_create,
    handle_dealdesk_quote_line_items_upsert,
    handle_dealdesk_quote_plan,
    handle_dealdesk_quote_rollback,
)
from .store import DealDeskStore

if TYPE_CHECKING:
    from g_gremlin.autopilot.tools import AutopilotToolDefinition


QuoteWorkflowRecoveryAction = Literal["retry", "rollback"]
_NON_ALNUM_RE = re.compile(r"[^A-Za-z0-9._-]+")


class _DealDeskWorkflowModel(BaseModel):
    model_config = ConfigDict(extra="ignore")

    def to_dict(self) -> dict[str, Any]:
        return self.model_dump(exclude_none=False, by_alias=True)

    @classmethod
    def from_dict(cls, data: dict[str, Any]) -> "_DealDeskWorkflowModel":
        return cls.model_validate(data)


class QuoteWorkflowTransitionV1(_DealDeskWorkflowModel):
    version: int = 1
    observed_at: str
    from_state: QuoteWorkflowState | None = None
    to_state: QuoteWorkflowState
    reason: str | None = None
    workflow_error: str | None = None

    @computed_field(alias="$schema")
    @property
    def schema(self) -> str:
        return "QuoteWorkflowTransition/v1"


class QuoteWorkflowRunV1(_DealDeskWorkflowModel):
    version: int = 1
    run_id: str
    opportunity_id: str
    crm_org_id: str
    org_alias: str | None = None
    context_id: str | None = None
    workflow_kind: Literal["quote_create", "seller_quote_revision"] = "quote_create"
    entrypoint: str = "manual"
    state_dir: str | None = None
    config_path: str | None = None
    triggered_at: str
    last_transition_at: str
    completed_at: str | None = None
    current_state: QuoteWorkflowState
    recovery_action: QuoteWorkflowRecoveryAction = "retry"
    quote_request: dict[str, Any] = Field(default_factory=dict)
    approval_required: bool = False
    approved_by: str | None = None
    approval_note: str | None = None
    allow_review_required: bool = False
    allow_insufficient_evidence: bool = False
    current_quote_id: str | None = None
    pdf_status: QuotePdfStatus = "not_requested"
    workflow_error: str | None = None
    plan_result: dict[str, Any] = Field(default_factory=dict)
    create_result: dict[str, Any] = Field(default_factory=dict)
    line_items_result: dict[str, Any] = Field(default_factory=dict)
    rollback_result: dict[str, Any] = Field(default_factory=dict)
    intent: dict[str, Any] = Field(default_factory=dict)
    preview: dict[str, Any] = Field(default_factory=dict)
    detected_engine: str | None = None
    receipt_outcome: str | None = None
    receipt_dir: str | None = None
    pending_run_id: str | None = None
    pending_plan_path: str | None = None
    apply_result: dict[str, Any] = Field(default_factory=dict)
    latest_result: dict[str, Any] = Field(default_factory=dict)
    rollback_data: dict[str, Any] = Field(default_factory=dict)
    notes: list[str] = Field(default_factory=list)
    transitions: list[QuoteWorkflowTransitionV1] = Field(default_factory=list)
    resume_count: int = 0

    @computed_field(alias="$schema")
    @property
    def schema(self) -> str:
        return "QuoteWorkflowRun/v1"


def handle_dealdesk_quote_workflow_start(
    step: PlanStep,
    tool_def: AutopilotToolDefinition | None,
) -> dict[str, object]:
    del tool_def
    args = dict(step.args or {})
    opportunity_id = _normalize_text(args.get("opportunity_id"))
    if not opportunity_id:
        raise RuntimeError("Missing opportunity_id for dealdesk quote workflow start.")

    org_alias = _normalize_text(args.get("org_alias")) or None
    crm_org_id = resolve_crm_org_id(org_alias=org_alias, crm_org_id=args.get("crm_org_id"))
    state_dir = _normalize_text(args.get("state_dir")) or None
    config_path = _normalize_text(args.get("config_path")) or None
    now = utc_now_iso()
    store = DealDeskStore(state_dir)
    requested_run_id = _normalize_text(args.get("run_id")) or _build_workflow_run_id(opportunity_id)
    if store.load_quote_workflow_run(run_id=requested_run_id, crm_org_id=crm_org_id, org_alias=org_alias) is not None:
        raise RuntimeError(f"Quote workflow run {requested_run_id} already exists. Use resume instead.")
    run = QuoteWorkflowRunV1(
        run_id=requested_run_id,
        opportunity_id=opportunity_id,
        crm_org_id=crm_org_id,
        org_alias=org_alias,
        context_id=_context_id_for(store, opportunity_id=opportunity_id, crm_org_id=crm_org_id, org_alias=org_alias),
        entrypoint=_normalize_text(args.get("entrypoint")) or "manual",
        state_dir=str(store.state_dir),
        config_path=config_path,
        triggered_at=now,
        last_transition_at=now,
        current_state="intake_received",
        quote_request=_json_object(args.get("quote_request")),
        approved_by=_normalize_text(args.get("approved_by") or args.get("actor")) or None,
        approval_note=_normalize_text(args.get("approval_note") or args.get("rationale")) or None,
        allow_review_required=bool(args.get("allow_review_required")),
        allow_insufficient_evidence=bool(args.get("allow_insufficient_evidence")),
        pdf_status="not_requested",
    )
    _record_transition(
        store,
        run,
        to_state="intake_received",
        observed_at=now,
        reason="workflow_started",
    )
    _advance_quote_workflow(store, run, recovery_action="retry")
    return _render_workflow_result(
        run,
        workflow_run_path=store.quote_workflow_run_path(
            run_id=run.run_id,
            crm_org_id=run.crm_org_id,
            org_alias=run.org_alias,
        ),
        before={
            "opportunity_id": opportunity_id,
            "quote_request": dict(run.quote_request),
            "approved_by": run.approved_by,
            "allow_review_required": run.allow_review_required,
            "allow_insufficient_evidence": run.allow_insufficient_evidence,
        },
    )


def handle_dealdesk_quote_workflow_resume(
    step: PlanStep,
    tool_def: AutopilotToolDefinition | None,
) -> dict[str, object]:
    del tool_def
    args = dict(step.args or {})
    run_id = _normalize_text(args.get("run_id"))
    if not run_id:
        raise RuntimeError("Missing run_id for dealdesk quote workflow resume.")

    store = DealDeskStore(_normalize_text(args.get("state_dir")) or None)
    run = store.load_quote_workflow_run(
        run_id=run_id,
        crm_org_id=_normalize_text(args.get("crm_org_id")) or None,
        org_alias=_normalize_text(args.get("org_alias")) or None,
    )
    if run is None:
        raise RuntimeError(f"Quote workflow run {run_id} could not be found.")

    approved_by = _normalize_text(args.get("approved_by") or args.get("actor")) or None
    approval_note = _normalize_text(args.get("approval_note") or args.get("rationale")) or None
    if approved_by:
        run.approved_by = approved_by
    if approval_note:
        run.approval_note = approval_note
    if "allow_review_required" in args:
        run.allow_review_required = bool(args.get("allow_review_required"))
    if "allow_insufficient_evidence" in args:
        run.allow_insufficient_evidence = bool(args.get("allow_insufficient_evidence"))
    recovery_action = _parse_recovery_action(args.get("recovery_action"))
    run.recovery_action = recovery_action
    run.resume_count += 1
    _save_run(store, run)
    _advance_quote_workflow(store, run, recovery_action=recovery_action)
    return _render_workflow_result(
        run,
        workflow_run_path=store.quote_workflow_run_path(
            run_id=run.run_id,
            crm_org_id=run.crm_org_id,
            org_alias=run.org_alias,
        ),
        before={"run_id": run_id, "recovery_action": recovery_action, "approved_by": approved_by},
    )


def handle_dealdesk_quote_workflow_status(
    step: PlanStep,
    tool_def: AutopilotToolDefinition | None,
) -> dict[str, object]:
    del tool_def
    args = dict(step.args or {})
    run_id = _normalize_text(args.get("run_id"))
    opportunity_id = _normalize_text(args.get("opportunity_id"))
    if not run_id and not opportunity_id:
        raise RuntimeError("Provide run_id or opportunity_id for dealdesk quote workflow status.")

    store = DealDeskStore(_normalize_text(args.get("state_dir")) or None)
    workflow_kind = _normalize_text(args.get("workflow_kind")) or None
    org_alias = _normalize_text(args.get("org_alias")) or None
    crm_org_id = _normalize_text(args.get("crm_org_id")) or None

    run = _load_workflow_run_for_status(
        store,
        run_id=run_id or None,
        opportunity_id=opportunity_id or None,
        crm_org_id=crm_org_id,
        org_alias=org_alias,
        workflow_kind=workflow_kind,
    )
    if run is None:
        identifier = run_id or opportunity_id
        raise RuntimeError(f"Quote workflow status could not find a matching run for {identifier}.")

    context = store.load_context(
        opportunity_id=run.opportunity_id,
        crm_org_id=run.crm_org_id,
        org_alias=run.org_alias,
    )
    run_path = store.quote_workflow_run_path(
        run_id=run.run_id,
        crm_org_id=run.crm_org_id,
        org_alias=run.org_alias,
    )
    return {
        "targets": [run.current_quote_id] if run.current_quote_id else [run.opportunity_id],
        "before": {
            "run_id": run_id or "",
            "opportunity_id": opportunity_id or "",
            "workflow_kind": workflow_kind or "",
            "org_alias": org_alias or "",
            "crm_org_id": crm_org_id or "",
        },
        "after": {
            "status": run.current_state,
            "workflow_run_id": run.run_id,
            "workflow_kind": run.workflow_kind,
            "workflow_run_path": str(run_path),
            "opportunity_id": run.opportunity_id,
            "crm_org_id": run.crm_org_id,
            "org_alias": run.org_alias,
            "context_id": run.context_id,
            "current_quote_id": run.current_quote_id,
            "approval_required": run.approval_required,
            "approved_by": run.approved_by,
            "approval_note": run.approval_note,
            "receipt_outcome": run.receipt_outcome,
            "receipt_dir": run.receipt_dir,
            "pending_run_id": run.pending_run_id,
            "pending_plan_path": run.pending_plan_path,
            "workflow_error": run.workflow_error,
            "last_transition_at": run.last_transition_at,
            "completed_at": run.completed_at,
            "context": context.to_dict() if context is not None else None,
            "workflow_run": run.to_dict(),
        },
    }


def _advance_quote_workflow(
    store: DealDeskStore,
    run: QuoteWorkflowRunV1,
    *,
    recovery_action: QuoteWorkflowRecoveryAction,
) -> QuoteWorkflowRunV1:
    if run.current_state == "delivered":
        _save_run(store, run)
        return run

    if recovery_action == "rollback" and run.current_quote_id:
        return _rollback_to_draft_ready(store, run)

    plan_after = _after_payload(run.plan_result)
    if (
        not run.plan_result
        or run.current_state in {"intake_received", "readiness_check"}
        or (not bool(plan_after.get("execution_eligible")) and not run.current_quote_id)
    ):
        _record_transition(
            store,
            run,
            to_state="readiness_check",
            observed_at=utc_now_iso(),
            reason="planning_started",
        )
        try:
            plan_result = handle_dealdesk_quote_plan(_plan_step(run), None)
        except Exception as exc:
            _record_transition(
                store,
                run,
                to_state="failed",
                observed_at=utc_now_iso(),
                reason="planning_exception",
                workflow_error=f"planning raised {type(exc).__name__}: {exc}",
            )
            return run
        run.plan_result = plan_result
        run.latest_result = plan_result
        plan_after = _after_payload(plan_result)
        run.context_id = _normalize_text(plan_after.get("context_id")) or run.context_id
        run.approval_required = bool(_json_object(plan_after.get("quote_plan")).get("approval_required"))
        _save_run(store, run)
        if not bool(plan_after.get("execution_eligible")):
            _record_transition(
                store,
                run,
                to_state="failed",
                observed_at=utc_now_iso(),
                reason="planning_blocked",
                workflow_error=_failure_summary(plan_result, phase="planning"),
            )
            return run
        _record_transition(
            store,
            run,
            to_state="draft_ready",
            observed_at=utc_now_iso(),
            reason="draft_ready",
        )

    if run.approval_required and not run.approved_by:
        if run.current_state != "approval_pending":
            _record_transition(
                store,
                run,
                to_state="approval_pending",
                observed_at=utc_now_iso(),
                reason="awaiting_approval",
            )
        return run

    if run.current_state != "executing":
        _record_transition(
            store,
            run,
            to_state="executing",
            observed_at=utc_now_iso(),
            reason="execution_started",
        )

    if not run.current_quote_id:
        try:
            create_result = handle_dealdesk_quote_create(_create_step(run), None)
        except Exception as exc:
            _record_transition(
                store,
                run,
                to_state="failed",
                observed_at=utc_now_iso(),
                reason="quote_create_exception",
                workflow_error=f"quote_create raised {type(exc).__name__}: {exc}",
            )
            return run
        run.create_result = create_result
        run.latest_result = create_result
        create_after = _after_payload(create_result)
        if _normalize_text(create_after.get("status")) != "created":
            _record_transition(
                store,
                run,
                to_state="failed",
                observed_at=utc_now_iso(),
                reason="quote_create_blocked",
                workflow_error=_failure_summary(create_result, phase="quote_create"),
            )
            return run
        run.current_quote_id = _normalize_text(create_after.get("quote_id")) or None
        run.rollback_data = _merge_rollback_data(run.rollback_data, _json_object(create_after.get("rollback_data")))
        _save_run(store, run)

    try:
        line_item_result = handle_dealdesk_quote_line_items_upsert(_line_item_step(run), None)
    except Exception as exc:
        _record_transition(
            store,
            run,
            to_state="failed",
            observed_at=utc_now_iso(),
            reason="line_items_exception",
            workflow_error=f"quote_line_items raised {type(exc).__name__}: {exc}",
        )
        return run
    run.line_items_result = line_item_result
    run.latest_result = line_item_result
    line_item_after = _after_payload(line_item_result)
    run.rollback_data = _merge_rollback_data(run.rollback_data, _json_object(line_item_after.get("rollback_data")))
    _save_run(store, run)
    if _normalize_text(line_item_after.get("status")) == "upserted":
        _record_transition(
            store,
            run,
            to_state="delivered",
            observed_at=utc_now_iso(),
            reason="quote_delivered",
        )
        return run
    _record_transition(
        store,
        run,
        to_state="failed",
        observed_at=utc_now_iso(),
        reason="line_items_incomplete",
        workflow_error=_failure_summary(line_item_result, phase="quote_line_items"),
    )
    return run


def _rollback_to_draft_ready(store: DealDeskStore, run: QuoteWorkflowRunV1) -> QuoteWorkflowRunV1:
    _record_transition(
        store,
        run,
        to_state="executing",
        observed_at=utc_now_iso(),
        reason="rollback_started",
    )
    try:
        rollback_result = handle_dealdesk_quote_rollback(_rollback_step(run), None)
    except Exception as exc:
        _record_transition(
            store,
            run,
            to_state="failed",
            observed_at=utc_now_iso(),
            reason="rollback_exception",
            workflow_error=f"quote_rollback raised {type(exc).__name__}: {exc}",
        )
        return run
    run.rollback_result = rollback_result
    run.latest_result = rollback_result
    rollback_after = _after_payload(rollback_result)
    run.rollback_data = _json_object(rollback_after.get("remaining_rollback_data"))
    _save_run(store, run)
    if _normalize_text(rollback_after.get("status")) == "rolled_back":
        _record_transition(
            store,
            run,
            to_state="draft_ready",
            observed_at=utc_now_iso(),
            reason="rollback_succeeded",
            clear_current_quote_id=True,
        )
        return run
    _record_transition(
        store,
        run,
        to_state="failed",
        observed_at=utc_now_iso(),
        reason="rollback_incomplete",
        workflow_error=_failure_summary(rollback_result, phase="quote_rollback"),
    )
    return run


def _record_transition(
    store: DealDeskStore,
    run: QuoteWorkflowRunV1,
    *,
    to_state: QuoteWorkflowState,
    observed_at: str,
    reason: str | None = None,
    workflow_error: str | None = None,
    clear_current_quote_id: bool = False,
    engine: str | None = None,
    quote_status: str | None = None,
) -> None:
    from_state = run.current_state if run.transitions else None
    run.transitions.append(
        QuoteWorkflowTransitionV1(
            observed_at=observed_at,
            from_state=from_state,
            to_state=to_state,
            reason=reason,
            workflow_error=_normalize_text(workflow_error) or None,
        )
    )
    run.current_state = to_state
    run.last_transition_at = observed_at
    run.workflow_error = _normalize_text(workflow_error) or None
    if clear_current_quote_id:
        run.current_quote_id = None
    if to_state == "delivered":
        run.completed_at = observed_at
    else:
        run.completed_at = None
    _save_run(
        store,
        run,
        clear_current_quote_id=clear_current_quote_id,
        engine=engine,
        quote_status=quote_status,
    )


def _save_run(
    store: DealDeskStore,
    run: QuoteWorkflowRunV1,
    *,
    clear_current_quote_id: bool = False,
    engine: str | None = None,
    quote_status: str | None = None,
) -> None:
    store.save_quote_workflow_run(run)
    update_context_for_quote_workflow_transition(
        store=store,
        crm_org_id=run.crm_org_id,
        org_alias=run.org_alias,
        opportunity_id=run.opportunity_id,
        run_id=run.run_id,
        state=run.current_state,
        observed_at=run.last_transition_at,
        workflow_error=run.workflow_error,
        current_quote_id=run.current_quote_id,
        clear_current_quote_id=clear_current_quote_id,
        engine=engine or run.detected_engine,
        quote_status=quote_status,
        pdf_status=run.pdf_status,
    )


def _plan_step(run: QuoteWorkflowRunV1) -> PlanStep:
    return PlanStep(
        step_id=f"{run.run_id}:plan",
        tool="dealdesk.quote.plan",
        args={
            "opportunity_id": run.opportunity_id,
            "quote_request": dict(run.quote_request),
            "config_path": run.config_path or "",
            "state_dir": run.state_dir or "",
            "org_alias": run.org_alias,
            "crm_org_id": run.crm_org_id,
            "allow_insufficient_evidence": run.allow_insufficient_evidence,
            "run_id": run.run_id,
        },
        risk_level=0,
    )


def _create_step(run: QuoteWorkflowRunV1) -> PlanStep:
    return PlanStep(
        step_id=f"{run.run_id}:create",
        tool="dealdesk.quote.create",
        args={
            "opportunity_id": run.opportunity_id,
            "config_path": run.config_path or "",
            "state_dir": run.state_dir or "",
            "org_alias": run.org_alias,
            "crm_org_id": run.crm_org_id,
            "approved_by": run.approved_by,
            "approval_note": run.approval_note,
            "allow_review_required": run.allow_review_required,
            "allow_insufficient_evidence": run.allow_insufficient_evidence,
            "run_id": run.run_id,
        },
        risk_level=1,
    )


def _line_item_step(run: QuoteWorkflowRunV1) -> PlanStep:
    return PlanStep(
        step_id=f"{run.run_id}:line-items",
        tool="dealdesk.quote.line_items.upsert",
        args={
            "quote_id": run.current_quote_id,
            "opportunity_id": run.opportunity_id,
            "config_path": run.config_path or "",
            "state_dir": run.state_dir or "",
            "org_alias": run.org_alias,
            "crm_org_id": run.crm_org_id,
            "allow_insufficient_evidence": run.allow_insufficient_evidence,
            "run_id": run.run_id,
        },
        risk_level=1,
    )


def _rollback_step(run: QuoteWorkflowRunV1) -> PlanStep:
    return PlanStep(
        step_id=f"{run.run_id}:rollback",
        tool="dealdesk.quote.rollback",
        args={
            "quote_id": run.current_quote_id,
            "opportunity_id": run.opportunity_id,
            "config_path": run.config_path or "",
            "state_dir": run.state_dir or "",
            "org_alias": run.org_alias,
            "crm_org_id": run.crm_org_id,
            "rollback_data": dict(run.rollback_data),
            "run_id": run.run_id,
        },
        risk_level=2,
    )


def _render_workflow_result(
    run: QuoteWorkflowRunV1,
    *,
    workflow_run_path: Any,
    before: dict[str, Any],
) -> dict[str, object]:
    return {
        "targets": [run.current_quote_id] if run.current_quote_id else [run.opportunity_id],
        "before": before,
        "after": {
            "status": run.current_state,
            "workflow_run_id": run.run_id,
            "workflow_run_path": str(workflow_run_path),
            "workflow_error": run.workflow_error,
            "current_quote_id": run.current_quote_id,
            "approval_required": run.approval_required,
            "approved_by": run.approved_by,
            "last_transition_at": run.last_transition_at,
            "completed_at": run.completed_at,
            "pdf_status": run.pdf_status,
            "workflow_run": run.to_dict(),
        },
    }


def _load_workflow_run_for_status(
    store: DealDeskStore,
    *,
    run_id: str | None,
    opportunity_id: str | None,
    crm_org_id: str | None,
    org_alias: str | None,
    workflow_kind: str | None,
) -> QuoteWorkflowRunV1 | None:
    if run_id:
        run = store.load_quote_workflow_run(
            run_id=run_id,
            crm_org_id=crm_org_id,
            org_alias=org_alias,
        )
        if run is None:
            return None
        if workflow_kind and run.workflow_kind != workflow_kind:
            return None
        return run

    runs = store.list_quote_workflow_runs(
        opportunity_id=opportunity_id,
        crm_org_id=crm_org_id,
        org_alias=org_alias,
    )
    if workflow_kind:
        runs = [item for item in runs if item.workflow_kind == workflow_kind]
    if not runs:
        return None
    runs.sort(
        key=lambda item: (
            _normalize_text(item.last_transition_at or item.triggered_at),
            _normalize_text(item.triggered_at),
            _normalize_text(item.run_id),
        ),
        reverse=True,
    )
    return runs[0]


def _failure_summary(result: dict[str, Any], *, phase: str) -> str:
    after = _after_payload(result)
    blocking_reasons = after.get("blocking_reasons")
    if isinstance(blocking_reasons, list):
        messages = [_normalize_text(item) for item in blocking_reasons]
        normalized = [item for item in messages if item]
        if normalized:
            return "; ".join(normalized)
    status = _normalize_text(after.get("status"))
    if status:
        return f"{phase} returned status {status}."
    return f"{phase} failed without a structured status."


def _after_payload(result: dict[str, Any]) -> dict[str, Any]:
    payload = result.get("after")
    return payload if isinstance(payload, dict) else {}


def _json_object(value: Any) -> dict[str, Any]:
    return dict(value) if isinstance(value, dict) else {}


def _merge_rollback_data(existing: dict[str, Any], new: dict[str, Any]) -> dict[str, Any]:
    if not existing:
        return dict(new or {})
    if not new:
        return dict(existing)
    merged = dict(existing)
    existing_records = _json_object(existing.get("records_by_type"))
    new_records = _json_object(new.get("records_by_type"))
    all_record_types = sorted(set(existing_records) | set(new_records))
    merged_records: dict[str, list[str]] = {}
    for record_type in all_record_types:
        values: list[str] = []
        for item in list(existing_records.get(record_type) or []) + list(new_records.get(record_type) or []):
            normalized = _normalize_text(item)
            if normalized and normalized not in values:
                values.append(normalized)
        merged_records[record_type] = values
    merged["records_by_type"] = merged_records
    ordered_record_ids: list[str] = []
    for record_type in ("QuoteLineItem", "Quote"):
        for record_id in merged_records.get(record_type, []):
            if record_id not in ordered_record_ids:
                ordered_record_ids.append(record_id)
    merged["record_ids"] = ordered_record_ids
    delete_order = [str(item) for item in list(new.get("delete_order") or existing.get("delete_order") or [])]
    merged["delete_order"] = [item for item in delete_order if item in {"QuoteLineItem", "Quote"}] or ["Quote"]
    merged["automatic_delete_eligible"] = bool(new.get("automatic_delete_eligible", existing.get("automatic_delete_eligible")))
    if _normalize_text(new.get("rollback_eligible_until")):
        merged["rollback_eligible_until"] = new.get("rollback_eligible_until")
    if _normalize_text(new.get("instructions")):
        merged["instructions"] = new.get("instructions")
    return merged


def _context_id_for(
    store: DealDeskStore,
    *,
    opportunity_id: str,
    crm_org_id: str,
    org_alias: str | None,
) -> str | None:
    context = store.load_context(
        opportunity_id=opportunity_id,
        crm_org_id=crm_org_id,
        org_alias=org_alias,
    )
    return context.context_id if context is not None else None


def _build_workflow_run_id(opportunity_id: str) -> str:
    safe_opportunity_id = _NON_ALNUM_RE.sub("-", opportunity_id).strip("._-") or "opportunity"
    return f"quote_workflow_{safe_opportunity_id}_{uuid4().hex[:10]}"


def build_quote_workflow_run_id(
    *,
    opportunity_id: str,
    prefix: str = "quote_workflow",
) -> str:
    safe_opportunity_id = _NON_ALNUM_RE.sub("-", opportunity_id).strip("._-") or "opportunity"
    safe_prefix = _NON_ALNUM_RE.sub("-", prefix).strip("._-") or "quote_workflow"
    return f"{safe_prefix}_{safe_opportunity_id}_{uuid4().hex[:10]}"


def persist_quote_workflow_run(
    store: DealDeskStore,
    run: QuoteWorkflowRunV1,
    *,
    clear_current_quote_id: bool = False,
    engine: str | None = None,
    quote_status: str | None = None,
) -> None:
    _save_run(
        store,
        run,
        clear_current_quote_id=clear_current_quote_id,
        engine=engine,
        quote_status=quote_status,
    )


def record_quote_workflow_transition(
    store: DealDeskStore,
    run: QuoteWorkflowRunV1,
    *,
    to_state: QuoteWorkflowState,
    observed_at: str,
    reason: str | None = None,
    workflow_error: str | None = None,
    clear_current_quote_id: bool = False,
    engine: str | None = None,
    quote_status: str | None = None,
) -> None:
    _record_transition(
        store,
        run,
        to_state=to_state,
        observed_at=observed_at,
        reason=reason,
        workflow_error=workflow_error,
        clear_current_quote_id=clear_current_quote_id,
        engine=engine,
        quote_status=quote_status,
    )


def sync_seller_quote_revision_workflow_outcome(
    *,
    state_dir: str,
    run_id: str,
    receipt_outcome: str,
    receipt_dir: str | None = None,
    actor: str | None = None,
    note: str | None = None,
    workflow_error: str | None = None,
) -> QuoteWorkflowRunV1 | None:
    store = DealDeskStore(state_dir)
    run = store.load_quote_workflow_run(run_id=run_id)
    if run is None or run.workflow_kind != "seller_quote_revision":
        run = next(
            (
                candidate
                for candidate in store.list_quote_workflow_runs()
                if candidate.workflow_kind == "seller_quote_revision" and candidate.pending_run_id == run_id
            ),
            None,
        )
    if run is None or run.workflow_kind != "seller_quote_revision":
        return None

    normalized_outcome = _normalize_text(receipt_outcome)
    run.receipt_outcome = normalized_outcome or None
    run.receipt_dir = _normalize_text(receipt_dir) or None
    run.pending_run_id = None
    run.pending_plan_path = None
    run.approval_required = False
    if actor:
        run.approved_by = actor
    if note:
        run.approval_note = note
    run.latest_result = {
        "receipt_outcome": normalized_outcome,
        "receipt_dir": run.receipt_dir,
        "actor": run.approved_by,
        "note": run.approval_note,
    }

    preview = _json_object(run.preview)
    selected_quote = _json_object(preview.get("selected_quote"))
    if _normalize_text(selected_quote.get("quote_id")):
        run.current_quote_id = _normalize_text(selected_quote.get("quote_id"))
    run.detected_engine = _normalize_text(run.detected_engine or preview.get("detected_engine")) or None
    quote_status = _normalize_text(selected_quote.get("status")) or None
    observed_at = utc_now_iso()

    if normalized_outcome == "success":
        record_quote_workflow_transition(
            store,
            run,
            to_state="delivered",
            observed_at=observed_at,
            reason="seller_quote_revision_approved",
            engine=run.detected_engine,
            quote_status=quote_status,
        )
        return run

    failure_message = workflow_error or note or f"seller quote revision finished with outcome {normalized_outcome or 'unknown'}."
    record_quote_workflow_transition(
        store,
        run,
        to_state="failed",
        observed_at=observed_at,
        reason="seller_quote_revision_not_delivered",
        workflow_error=failure_message,
        engine=run.detected_engine,
        quote_status=quote_status,
    )
    return run


def _normalize_text(value: Any) -> str:
    return str(value or "").strip()


def _parse_recovery_action(value: Any) -> QuoteWorkflowRecoveryAction:
    normalized = _normalize_text(value).lower()
    if normalized == "rollback":
        return "rollback"
    return "retry"


__all__ = [
    "build_quote_workflow_run_id",
    "handle_dealdesk_quote_workflow_status",
    "persist_quote_workflow_run",
    "QuoteWorkflowRecoveryAction",
    "QuoteWorkflowRunV1",
    "QuoteWorkflowTransitionV1",
    "record_quote_workflow_transition",
    "handle_dealdesk_quote_workflow_resume",
    "handle_dealdesk_quote_workflow_start",
    "sync_seller_quote_revision_workflow_outcome",
]
