///|
/// Typed command descriptors used by service and CLI adapters. Keeping command
/// validation in the library gives HTTP, queue, and command-line front ends a
/// single contract for safe execution and structured reporting.
pub(all) enum WorkflowCommandKind {
  WorkflowScan
  WorkflowRedact
  WorkflowReview
  WorkflowExport
  WorkflowValidate
  WorkflowBenchmark
} derive(Debug, Eq)

///|
pub(all) struct WorkflowCommand {
  command_id : String
  kind : WorkflowCommandKind
  document_id : String
  payload_checksum : String
  requested_by : String
  purpose : String
  dry_run : Bool
  arguments : Map[String, String]
} derive(Debug)

///|
pub(all) struct WorkflowCommandResult {
  command_id : String
  accepted : Bool
  status : String
  message : String
  output_checksum : String
  metrics : Map[String, Int]
} derive(Debug)

///|
pub fn workflow_command_kind_name(kind : WorkflowCommandKind) -> String {
  match kind {
    WorkflowScan => "scan"
    WorkflowRedact => "redact"
    WorkflowReview => "review"
    WorkflowExport => "export"
    WorkflowValidate => "validate"
    WorkflowBenchmark => "benchmark"
  }
}

///|
pub fn workflow_command(
  command_id : String,
  kind : WorkflowCommandKind,
  document_id : String,
  payload_checksum : String,
  requested_by : String,
  purpose : String,
) -> WorkflowCommand {
  {
    command_id,
    kind,
    document_id,
    payload_checksum,
    requested_by,
    purpose,
    dry_run: false,
    arguments: Map([]),
  }
}

///|
pub fn WorkflowCommand::dry_run(self : WorkflowCommand) -> WorkflowCommand {
  { ..self, dry_run: true }
}

///|
pub fn WorkflowCommand::with_argument(
  command : WorkflowCommand,
  key : String,
  value : String,
) -> WorkflowCommand {
  command.arguments[key] = value
  command
}

///|
pub fn WorkflowCommand::argument(
  self : WorkflowCommand,
  key : String,
) -> String {
  self.arguments.get_or_default(key, "")
}

///|
pub fn WorkflowCommand::is_valid(self : WorkflowCommand) -> Bool {
  self.command_id.length() > 0 &&
  self.document_id.length() > 0 &&
  self.payload_checksum.length() > 0 &&
  self.requested_by.length() > 0 &&
  self.purpose.length() > 0
}

///|
pub fn WorkflowCommand::checksum(self : WorkflowCommand) -> String {
  stable_hash(
    self.command_id +
    ":" +
    workflow_command_kind_name(self.kind) +
    ":" +
    self.document_id +
    ":" +
    self.payload_checksum,
  )
}

///|
pub fn workflow_command_result(
  command : WorkflowCommand,
  accepted : Bool,
  status : String,
  message : String,
  output_checksum : String,
) -> WorkflowCommandResult {
  {
    command_id: command.command_id,
    accepted,
    status,
    message,
    output_checksum,
    metrics: Map([]),
  }
}

///|
pub fn WorkflowCommandResult::with_metric(
  result : WorkflowCommandResult,
  key : String,
  value : Int,
) -> WorkflowCommandResult {
  result.metrics[key] = if value < 0 { 0 } else { value }
  result
}

///|
pub fn WorkflowCommandResult::is_success(self : WorkflowCommandResult) -> Bool {
  self.accepted && self.status == "succeeded"
}

///|
pub fn WorkflowCommandResult::to_json(self : WorkflowCommandResult) -> String {
  "{" +
  "\"command_id\":\"" +
  json_escape(self.command_id) +
  "\"," +
  "\"accepted\":" +
  self.accepted.to_string() +
  "," +
  "\"status\":\"" +
  json_escape(self.status) +
  "\"," +
  "\"message\":\"" +
  json_escape(self.message) +
  "\"," +
  "\"output_checksum\":\"" +
  json_escape(self.output_checksum) +
  "\"}"
}

///|
pub fn validate_workflow_command(
  command : WorkflowCommand,
) -> WorkflowCommandResult {
  if !command.is_valid() {
    workflow_command_result(
      command, false, "rejected", "required command fields are missing", "",
    )
  } else if command.kind == WorkflowExport && command.purpose == "" {
    workflow_command_result(
      command, false, "rejected", "export purpose is required", "",
    )
  } else {
    workflow_command_result(
      command,
      true,
      "accepted",
      "command passed validation",
      command.checksum(),
    )
  }
}

///|
pub fn complete_workflow_command(
  command : WorkflowCommand,
  output_checksum : String,
  metrics : Map[String, Int],
) -> WorkflowCommandResult {
  let result = workflow_command_result(
    command, true, "succeeded", "command completed", output_checksum,
  )
  for key, value in metrics {
    result.metrics[key] = value
  }
  result
}

///|
pub fn fail_workflow_command(
  command : WorkflowCommand,
  message : String,
) -> WorkflowCommandResult {
  workflow_command_result(command, false, "failed", message, "")
}

///|
pub fn workflow_command_batch(
  commands : Array[WorkflowCommand],
) -> Map[String, Int] {
  let counts : Map[String, Int] = Map([])
  for command in commands {
    let key = workflow_command_kind_name(command.kind)
    counts[key] = counts.get_or_default(key, 0) + 1
  }
  counts
}

///|
pub fn workflow_commands_are_unique(commands : Array[WorkflowCommand]) -> Bool {
  let seen : Map[String, Bool] = Map([])
  let mut unique = true
  for command in commands {
    if seen.contains(command.command_id) {
      unique = false
    }
    seen[command.command_id] = true
  }
  unique
}

///|
pub fn workflow_command_requires_payload(kind : WorkflowCommandKind) -> Bool {
  kind == WorkflowScan || kind == WorkflowRedact || kind == WorkflowValidate
}

///|
pub fn workflow_command_is_mutating(kind : WorkflowCommandKind) -> Bool {
  kind == WorkflowRedact || kind == WorkflowReview || kind == WorkflowExport
}

///|
pub fn workflow_command_safe_for_dry_run(command : WorkflowCommand) -> Bool {
  command.dry_run || !workflow_command_is_mutating(command.kind)
}

///|
pub fn workflow_command_summary(command : WorkflowCommand) -> String {
  [
    "command_id=" + command.command_id,
    "kind=" + workflow_command_kind_name(command.kind),
    "document_id=" + command.document_id,
    "requested_by=" + command.requested_by,
    "purpose=" + command.purpose,
    "dry_run=" + command.dry_run.to_string(),
    "valid=" + command.is_valid().to_string(),
    "checksum=" + command.checksum(),
  ].join("\n")
}

///|
pub fn workflow_command_kind_counts(
  commands : Array[WorkflowCommand],
) -> Map[String, Int] {
  let counts : Map[String, Int] = Map([])
  for command in commands {
    let name = workflow_command_kind_name(command.kind)
    counts[name] = counts.get_or_default(name, 0) + 1
  }
  counts
}

///|
pub fn workflow_result_checksum(result : WorkflowCommandResult) -> String {
  stable_hash(
    result.command_id + ":" + result.status + ":" + result.output_checksum,
  )
}

///|
pub fn workflow_command_id_set(
  commands : Array[WorkflowCommand],
) -> Array[String] {
  commands.map(fn(command) { command.command_id })
}