import type { FieldDescription, FieldType, JsonValue } from "@arco/clarizen-lib";
import { getFieldTypeExample } from "../metadata/field-hints.js";

export class ValueParseError extends Error {
  constructor(message: string) {
    super(message);
    this.name = "ValueParseError";
  }
}

const BOOLEAN_TRUE = new Set(["true", "1", "sim", "s", "yes", "y"]);
const BOOLEAN_FALSE = new Set(["false", "0", "não", "nao", "n", "no"]);

function parseBoolean(raw: string): boolean {
  const normalized = raw.trim().toLowerCase();
  if (BOOLEAN_TRUE.has(normalized)) return true;
  if (BOOLEAN_FALSE.has(normalized)) return false;
  throw new ValueParseError(
    `Valor booleano inválido: "${raw}". Use: ${getFieldTypeExample("Boolean")}`,
  );
}

function parseNumber(raw: string, integer: boolean): number {
  const trimmed = raw.trim();
  const value = Number(trimmed);
  if (!Number.isFinite(value)) {
    throw new ValueParseError(`Número inválido: "${raw}"`);
  }
  if (integer && !Number.isInteger(value)) {
    throw new ValueParseError(`Esperado número inteiro: "${raw}"`);
  }
  return value;
}

function parseEntity(raw: string): JsonValue {
  const trimmed = raw.trim();
  if (trimmed.startsWith("{")) {
    try {
      const parsed = JSON.parse(trimmed) as Record<string, unknown>;
      if (typeof parsed.id === "string") {
        return { id: parsed.id };
      }
      throw new ValueParseError('JSON de Entity deve conter "id".');
    } catch (err) {
      if (err instanceof ValueParseError) throw err;
      throw new ValueParseError(`JSON inválido para Entity: "${raw}"`);
    }
  }
  if (trimmed.startsWith("/")) {
    return { id: trimmed };
  }
  throw new ValueParseError(
    `Referência de Entity inválida: "${raw}". Use /Tipo/id ou {"id":"/Tipo/id"}`,
  );
}

function parseJsonObject(raw: string, typeName: string): JsonValue {
  try {
    return JSON.parse(raw.trim()) as JsonValue;
  } catch {
    throw new ValueParseError(
      `JSON inválido para ${typeName}: "${raw}". Exemplo: ${getFieldTypeExample(typeName as FieldType)}`,
    );
  }
}

function parseByType(raw: string, type: FieldType | undefined): JsonValue {
  const trimmed = raw.trim();

  switch (type) {
    case "Boolean":
      return parseBoolean(trimmed);
    case "String":
      return trimmed;
    case "Integer":
    case "Long":
      return parseNumber(trimmed, true);
    case "Double":
      return parseNumber(trimmed, false);
    case "DateTime":
    case "Date":
      return trimmed;
    case "Entity":
      return parseEntity(trimmed);
    case "Duration":
    case "Money":
      return parseJsonObject(trimmed, type);
    default:
      return trimmed;
  }
}

export function parseFieldValue(
  raw: string,
  field: FieldDescription,
): JsonValue {
  const trimmed = raw.trim();

  if (trimmed === "" && field.nullable) {
    return null;
  }

  if (trimmed.toLowerCase() === "null" && field.nullable) {
    return null;
  }

  if (trimmed === "") {
    throw new ValueParseError("Valor não pode ser vazio.");
  }

  return parseByType(raw, field.type);
}

export function formatValueForDisplay(
  value: JsonValue,
  displayLabel?: string,
): string {
  if (displayLabel) return displayLabel;
  if (value === null) return "(vazio / null)";
  if (typeof value === "string") return `"${value}"`;
  if (typeof value === "object" && value !== null && "id" in value) {
    const id = (value as { id?: unknown }).id;
    if (typeof id === "string") return id;
  }
  return JSON.stringify(value);
}
