/**
 * 중앙 포맷팅 함수.
 * 모든 컴포넌트는 날짜/숫자/통화를 표시할 때 이 함수들만 호출한다.
 *
 * 사용법:
 *   import { formatDate, formatNumber, formatCurrency } from "@/lib/formatting";
 *   formatDate("2025-01-01")           // "2025-01-01"
 *   formatDate("2025-01-01T14:30:00Z", "datetime")  // "2025-01-01 14:30:00"
 *   formatNumber(1234567)              // "1,234,567"
 *   formatCurrency(50000)              // "₩50,000"
 */

export { getFormatRules, setFormatRules, DEFAULT_FORMAT_RULES } from "./rules";
export type { FormatRules, DateFormatRules, NumberFormatRules, CurrencyFormatRules } from "./rules";

import { getFormatRules } from "./rules";

// --- 날짜 포맷 ---

type DateFormatType = "display" | "datetime" | "input" | "time";

/**
 * 날짜 값을 지정된 형식으로 포맷한다.
 * @param value - ISO 문자열, Date, 타임스탬프
 * @param type - "display" | "datetime" | "input" | "time"
 * @returns 포맷된 문자열 (파싱 실패 시 원본 반환)
 */
export function formatDate(value: unknown, type: DateFormatType = "display"): string {
  if (value == null || value === "") return "";

  const rules = getFormatRules();
  const format = rules.date[type];

  try {
    const date = value instanceof Date ? value : new Date(String(value));
    if (isNaN(date.getTime())) return String(value);

    return applyDateFormat(date, format);
  } catch {
    return String(value);
  }
}

/**
 * YYYY-MM-DD HH:mm:ss 패턴을 Date 객체에 적용
 */
function applyDateFormat(date: Date, pattern: string): string {
  const y = date.getFullYear();
  const M = date.getMonth() + 1;
  const d = date.getDate();
  const H = date.getHours();
  const m = date.getMinutes();
  const s = date.getSeconds();

  return pattern
    .replace("YYYY", String(y))
    .replace("MM", String(M).padStart(2, "0"))
    .replace("DD", String(d).padStart(2, "0"))
    .replace("HH", String(H).padStart(2, "0"))
    .replace("mm", String(m).padStart(2, "0"))
    .replace("ss", String(s).padStart(2, "0"));
}

// --- 숫자 포맷 ---

/** 최대 허용 소수점 자릿수 */
const MAX_DECIMAL_PLACES = 5;

/**
 * 실제 값에서 소수점 자릿수를 감지한다 (최대 MAX_DECIMAL_PLACES).
 */
function detectDecimals(num: number): number {
  if (Number.isInteger(num)) return 0;
  const parts = String(num).split(".");
  if (parts.length < 2) return 0;
  return Math.min(parts[1].length, MAX_DECIMAL_PLACES);
}

/**
 * 숫자를 로케일 기반으로 포맷한다 (천단위 구분자 + 소수점 자동감지).
 * @param value - 숫자 또는 숫자 문자열
 * @param decimals - 소수점 자릿수 (미지정 시 실제 값에서 자동감지, 최대 5자리)
 * @returns 포맷된 문자열
 */
export function formatNumber(value: unknown, decimals?: number): string {
  if (value == null || value === "") return "";

  const rules = getFormatRules();
  const num = typeof value === "number" ? value : parseFloat(String(value));
  if (isNaN(num)) return String(value);

  const dec = decimals ?? detectDecimals(num);

  return new Intl.NumberFormat(rules.number.locale, {
    minimumFractionDigits: dec,
    maximumFractionDigits: dec,
  }).format(num);
}

// --- 통화 포맷 ---

/**
 * 금액을 통화 형식으로 포맷한다.
 * @param value - 숫자 또는 숫자 문자열
 * @param currencyCode - 통화 코드 (미지정 시 기본값 사용)
 * @returns 포맷된 문자열 (예: "₩50,000")
 */
export function formatCurrency(value: unknown, currencyCode?: string): string {
  if (value == null || value === "") return "";

  const rules = getFormatRules();
  const num = typeof value === "number" ? value : parseFloat(String(value));
  if (isNaN(num)) return String(value);

  const code = currencyCode ?? rules.currency.code;

  return new Intl.NumberFormat(rules.currency.locale, {
    style: "currency",
    currency: code,
    maximumFractionDigits: code === "KRW" ? 0 : 2,
  }).format(num);
}

// --- 범용 포맷 ---

/**
 * 데이터 타입에 따라 자동으로 적절한 포맷을 적용한다.
 * @param value - 포맷할 값
 * @param dataType - "date" | "datetime" | "number" | "currency" | "text"
 */
export function formatValue(value: unknown, dataType: string): string {
  switch (dataType) {
    case "date":
      return formatDate(value, "display");
    case "datetime":
      return formatDate(value, "datetime");
    case "time":
      return formatDate(value, "time");
    case "number":
    case "integer":
    case "float":
    case "decimal":
      return formatNumber(value);
    case "currency":
    case "money":
      return formatCurrency(value);
    default:
      return value == null ? "" : String(value);
  }
}
