'use server';

import { revalidatePath } from 'next/cache';
import { verifySession } from '@/lib/auth/dal';
import {
  createCodeDetail,
  createCodeGroup,
  deleteCodeDetail,
  deleteCodeGroup,
  updateCodeDetail,
  updateCodeGroup,
} from '@/lib/data/repositories/common-code-repository';
import {
  validateCommonCodeDetail,
  validateCommonCodeGroup,
  type CommonCodeFormState,
} from '@/lib/domain/common-code-form';
import { COMMON_CODES_PATH } from '@/lib/domain/common-code-query';

/**
 * 코드관리 등록/수정/삭제 Server Action(기획 SYS_COD_001).
 *
 * **모든 Action이 `verifySession()`으로 시작한다** — Server Action은 UI를 거치지 않고 직접
 * POST될 수 있어 이 확인이 유일한 최종 방어선이다.
 *
 * 검증은 화면이 아니라 여기서 확정한다(`lib/domain/common-code-form.ts`의 규칙을 호출) —
 * 화면의 required 속성과 읽기 전용 표시는 편의일 뿐 신뢰 경계가 아니다.
 *
 * **수정 대상(코드ID)은 읽기 전용 입력이 아니라 hidden 필드에서 읽는다** — 읽기 전용 칸은
 * 위조될 수 있고, 위조되더라도 대상이 바뀌면 안 되기 때문이다.
 */

const INVALID_REQUEST_MESSAGE = '요청이 올바르지 않습니다.';
const SAVE_FAILED_MESSAGE = '저장하지 못했습니다. 잠시 후 다시 시도해 주세요.';
const DELETE_FAILED_MESSAGE = '삭제하지 못했습니다. 잠시 후 다시 시도해 주세요.';

function readString(formData: FormData, key: string): string {
  const value = formData.get(key);
  return typeof value === 'string' ? value : '';
}

/**
 * 폼 숫자 입력을 파싱한다. `Number('')`이 조용히 `0`이 되는 함정을 피하려고 빈 문자열은
 * 명시적으로 `NaN`으로 둔다 — 그래야 검증이 "값이 비었음"을 잡아낸다.
 */
function parseFormNumber(formData: FormData, key: string): number {
  const trimmed = readString(formData, key).trim();
  return trimmed === '' ? NaN : Number(trimmed);
}

/* ── 공통코드 ─────────────────────────────────────────────────────────────── */

/** 기획 [공통코드 등록] 팝업. */
export async function createCodeGroupAction(
  _prevState: CommonCodeFormState,
  formData: FormData
): Promise<CommonCodeFormState> {
  await verifySession();

  const validation = validateCommonCodeGroup({
    comCd: readString(formData, 'comCd'),
    name: readString(formData, 'name'),
    description: readString(formData, 'description'),
  });
  if (!validation.ok) {
    return { status: 'error', errors: validation.errors };
  }

  try {
    await createCodeGroup(validation.values);
  } catch {
    return { status: 'error', message: SAVE_FAILED_MESSAGE };
  }

  revalidatePath(COMMON_CODES_PATH);
  return { status: 'success' };
}

/** 기획 [공통코드 수정] 팝업 — 코드ID는 대상을 가리키는 값이라 바뀌지 않는다. */
export async function updateCodeGroupAction(
  _prevState: CommonCodeFormState,
  formData: FormData
): Promise<CommonCodeFormState> {
  await verifySession();

  const targetComCd = readString(formData, 'targetComCd').trim();
  if (!targetComCd) {
    return { status: 'error', message: INVALID_REQUEST_MESSAGE };
  }

  const validation = validateCommonCodeGroup({
    comCd: targetComCd,
    name: readString(formData, 'name'),
    description: readString(formData, 'description'),
  });
  if (!validation.ok) {
    return { status: 'error', errors: validation.errors };
  }

  try {
    await updateCodeGroup(targetComCd, validation.values);
  } catch {
    return { status: 'error', message: SAVE_FAILED_MESSAGE };
  }

  revalidatePath(COMMON_CODES_PATH);
  return { status: 'success' };
}

/**
 * 기획 ④ 삭제 — 확인 얼럿은 화면이 띄우고 여기서는 인증과 입력만 확인한다.
 *
 * 폼 제출이 아니라 얼럿의 [삭제] 클릭에 반응하는 단발 호출이라 `useActionState`의
 * (prevState, formData) 규약 대신 식별자를 직접 받는다.
 */
export async function deleteCodeGroupAction(
  comCd: string
): Promise<CommonCodeFormState> {
  await verifySession();

  if (!comCd) {
    return { status: 'error', message: INVALID_REQUEST_MESSAGE };
  }

  try {
    await deleteCodeGroup(comCd);
  } catch {
    return { status: 'error', message: DELETE_FAILED_MESSAGE };
  }

  revalidatePath(COMMON_CODES_PATH);
  return { status: 'success' };
}

/* ── 상세코드 ─────────────────────────────────────────────────────────────── */

/** 기획 [상세코드 등록] 팝업 — 코드ID는 좌측에서 고른 공통코드다. */
export async function createCodeDetailAction(
  _prevState: CommonCodeFormState,
  formData: FormData
): Promise<CommonCodeFormState> {
  await verifySession();

  const validation = validateCommonCodeDetail({
    comCd: readString(formData, 'comCd'),
    comDtlCd: readString(formData, 'comDtlCd'),
    name: readString(formData, 'name'),
    description: readString(formData, 'description'),
    sortSeq: parseFormNumber(formData, 'sortSeq'),
  });
  if (!validation.ok) {
    return { status: 'error', errors: validation.errors };
  }

  try {
    await createCodeDetail(validation.values);
  } catch {
    return { status: 'error', message: SAVE_FAILED_MESSAGE };
  }

  revalidatePath(COMMON_CODES_PATH);
  return { status: 'success' };
}

/** 기획 [상세코드 수정] 팝업 — 코드ID·코드는 대상을 가리키는 값이라 바뀌지 않는다. */
export async function updateCodeDetailAction(
  _prevState: CommonCodeFormState,
  formData: FormData
): Promise<CommonCodeFormState> {
  await verifySession();

  const targetComCd = readString(formData, 'targetComCd').trim();
  const targetComDtlCd = readString(formData, 'targetComDtlCd').trim();
  if (!targetComCd || !targetComDtlCd) {
    return { status: 'error', message: INVALID_REQUEST_MESSAGE };
  }

  const validation = validateCommonCodeDetail({
    comCd: targetComCd,
    comDtlCd: readString(formData, 'comDtlCd'),
    name: readString(formData, 'name'),
    description: readString(formData, 'description'),
    sortSeq: parseFormNumber(formData, 'sortSeq'),
  });
  if (!validation.ok) {
    return { status: 'error', errors: validation.errors };
  }

  try {
    await updateCodeDetail(targetComCd, targetComDtlCd, validation.values);
  } catch {
    return { status: 'error', message: SAVE_FAILED_MESSAGE };
  }

  revalidatePath(COMMON_CODES_PATH);
  return { status: 'success' };
}

export async function deleteCodeDetailAction(
  comCd: string,
  comDtlCd: string
): Promise<CommonCodeFormState> {
  await verifySession();

  if (!comCd || !comDtlCd) {
    return { status: 'error', message: INVALID_REQUEST_MESSAGE };
  }

  try {
    await deleteCodeDetail(comCd, comDtlCd);
  } catch {
    return { status: 'error', message: DELETE_FAILED_MESSAGE };
  }

  revalidatePath(COMMON_CODES_PATH);
  return { status: 'success' };
}
