from __future__ import annotations

import argparse
from dataclasses import dataclass
from typing import Any, Callable, Dict, List, Optional


class SequenceCommandInputError(ValueError):
    """Raised when sequence command inputs are incomplete or malformed."""


@dataclass(frozen=True)
class SequenceRuntime:
    load_json_input: Callable[..., Any]
    split_cli_string_values: Callable[[Optional[List[str]]], List[str]]
    parse_json_string: Callable[[str, str], Any]
    parse_string_list_payload: Callable[..., List[str]]
    load_string_values_from_file: Callable[..., List[str]]
    dedupe_keep_order: Callable[[List[str]], List[str]]


def sequence_mutation_params(args: argparse.Namespace) -> Optional[Dict[str, Any]]:
    actor_user_id = str(getattr(args, "actor_user_id", "") or "").strip()
    if actor_user_id:
        return {"actorUserId": actor_user_id}
    return None


def collect_sequence_lead_ids(args: argparse.Namespace, *, runtime: SequenceRuntime) -> List[str]:
    lead_ids: List[str] = []
    lead_ids.extend(runtime.split_cli_string_values(getattr(args, "lead_id", None)))

    lead_ids_json = getattr(args, "lead_ids_json", None)
    if lead_ids_json:
        payload = runtime.parse_json_string(str(lead_ids_json), "lead-ids")
        lead_ids.extend(
            runtime.parse_string_list_payload(
                payload,
                context="--lead-ids-json",
                array_key="leadIds",
            )
        )

    lead_ids_file = getattr(args, "lead_ids_file", None)
    if lead_ids_file:
        lead_ids.extend(
            runtime.load_string_values_from_file(
                str(lead_ids_file),
                context="lead-ids",
                array_key="leadIds",
                csv_column=getattr(args, "lead_ids_column", None),
            )
        )

    return runtime.dedupe_keep_order([lead_id for lead_id in lead_ids if lead_id])


def build_sequence_enroll_payload(args: argparse.Namespace, *, runtime: SequenceRuntime) -> Dict[str, Any]:
    lead_ids = collect_sequence_lead_ids(args, runtime=runtime)
    if not lead_ids:
        raise SequenceCommandInputError(
            "provide --data-json/--data-file or at least one lead id via "
            "--lead-id/--lead-ids-json/--lead-ids-file"
        )

    payload: Dict[str, Any] = {"leadIds": lead_ids}
    if getattr(args, "start_date", None):
        payload["startDate"] = str(args.start_date)

    research_context = runtime.load_json_input(
        inline_json=getattr(args, "research_context_json", None),
        file_path=getattr(args, "research_context_file", None),
        context="research-context",
        default=None,
    )
    if research_context is not None:
        payload["researchContext"] = research_context

    variable_map = runtime.load_json_input(
        inline_json=getattr(args, "variable_map_json", None),
        file_path=getattr(args, "variable_map_file", None),
        context="variable-map",
        default=None,
    )
    if variable_map is not None:
        payload["variableMap"] = variable_map

    variable_overrides = runtime.load_json_input(
        inline_json=getattr(args, "variable_overrides_json", None),
        file_path=getattr(args, "variable_overrides_file", None),
        context="variable-overrides",
        default=None,
    )
    if variable_overrides is not None:
        payload["variableOverrides"] = variable_overrides

    assigned_to_user_id = str(getattr(args, "assigned_to_user_id", "") or "").strip()
    if assigned_to_user_id:
        payload["assignedToUserId"] = assigned_to_user_id
    if bool(getattr(args, "update_lead_owner", False)):
        payload["updateLeadOwner"] = True
    if bool(getattr(args, "review_only", False)):
        payload["reviewOnly"] = True

    return payload
