import type { Clarizen, JsonValue } from "@arco/clarizen-lib";
import { isCancel, select, spinner, text } from "@clack/prompts";
import { formatFieldHint } from "../metadata/field-hints.js";
import {
  fetchPickListOptions,
  formatPickListOptionLabel,
  getPickListEntityType,
  type FieldWithRefs,
} from "../metadata/picklist.js";
import { pickFromFiltered } from "../metadata/picker.js";
import { parseFieldValue, ValueParseError } from "./value.js";

export interface FieldValueSelection {
  value: JsonValue;
  displayLabel?: string;
}

async function pickPickListOption(
  field: FieldWithRefs,
  options: Array<{ value: string; label: string }>,
): Promise<string | symbol> {
  const fieldLabel = field.label ?? field.name ?? "valor";
  return pickFromFiltered(
    `Escolha o valor para ${fieldLabel}`,
    options,
    "Como deseja buscar na lista?",
  );
}

async function promptPickListSelection(
  clarizen: Clarizen,
  field: FieldWithRefs,
  pickListTypeName: string,
): Promise<FieldValueSelection | symbol> {
  const loadSpinner = spinner();
  loadSpinner.start(`Carregando opções de ${pickListTypeName}...`);

  const pickListOptions = await fetchPickListOptions(
    clarizen,
    pickListTypeName,
    (count) => {
      loadSpinner.message(
        `Carregando opções de ${pickListTypeName}... (${count})`,
      );
    },
  );

  loadSpinner.stop(`${pickListOptions.length} opção(ões) carregada(s).`);

  if (pickListOptions.length === 0) {
    console.log(
      `\nNenhuma opção encontrada para ${pickListTypeName}. Informe o ID manualmente.\n`,
    );
    return promptManualEntityValue(field);
  }

  const selectOptions = pickListOptions.map((o) => ({
    value: o.id,
    label: formatPickListOptionLabel(o),
  }));

  const selectedId = await pickPickListOption(field, selectOptions);
  if (isCancel(selectedId)) return selectedId;

  const match = pickListOptions.find((o) => o.id === selectedId);
  return {
    value: { id: selectedId },
    displayLabel: match
      ? `${match.label} (${match.id})`
      : String(selectedId),
  };
}

async function promptManualEntityValue(
  field: FieldWithRefs,
): Promise<FieldValueSelection | symbol> {
  while (true) {
    const raw = await text({
      message: "Informe o ID da entidade (ex: /Tipo/guid)",
      validate: (value) => (!value?.trim() ? "Valor obrigatório." : undefined),
    });
    if (isCancel(raw)) return raw;

    try {
      const value = parseFieldValue(raw, field);
      return { value, displayLabel: formatEntityDisplayLabel(value) };
    } catch (err) {
      if (err instanceof ValueParseError) {
        console.error(`\n  Erro: ${err.message}\n`);
        continue;
      }
      throw err;
    }
  }
}

function formatEntityDisplayLabel(value: JsonValue): string | undefined {
  if (value && typeof value === "object" && "id" in value) {
    const id = (value as { id?: unknown }).id;
    if (typeof id === "string") return id;
  }
  return undefined;
}

export async function promptPickListValue(
  clarizen: Clarizen,
  field: FieldWithRefs,
): Promise<FieldValueSelection | symbol> {
  console.log("\n" + formatFieldHint(field) + "\n");

  if (field.nullable) {
    const action = await select({
      message: "O que deseja fazer com este campo?",
      options: [
        { value: "set", label: "Definir um valor" },
        { value: "clear", label: "Limpar campo (null)" },
      ],
    });
    if (isCancel(action)) return action;
    if (action === "clear") {
      return { value: null, displayLabel: "(vazio / null)" };
    }
  }

  const pickListTypeName = getPickListEntityType(field);
  if (!pickListTypeName) {
    console.log(
      "\nPicklist sem tipo de referência na metadata. Informe o ID manualmente.\n",
    );
    return promptManualEntityValue(field);
  }

  return promptPickListSelection(clarizen, field, pickListTypeName);
}
