import 'server-only';
import { getSessionAccessToken } from '@/lib/auth/dal';
import { BackendRequestError, backendFetch } from '@/lib/http/backend-fetch';
import type { QuizItem } from '@/lib/domain/content';
import type { ContentQuery } from '@/lib/domain/content-query';
import type { QuizFormValues } from '@/lib/domain/content-form';

/**
 * 금융OX퀴즈 Repository — 쇼츠·만화와 테이블(`TB_COM_QUIZ`)도 API도 다르다.
 *
 * ```
 * GET  /api/v1/mngr/quiz/pagination       목록
 * GET  /api/v1/mngr/quiz/excel/download   엑셀 다운로드
 * POST /api/v1/mngr/quiz/excel/upload     엑셀 일괄등록 (multipart, 파트 이름 `file`)
 * ```
 *
 * 이슈: 등록·수정·삭제·단건조회 API가 없다. 아래 쓰기 함수는 다른 관리 화면과 같은 규약으로
 *   미리 맞춰 둔 것이라 백엔드가 생기기 전에는 실패한다.
 * 이슈: 학교(`COM_SCHUL_CD`)·학년(`GRD_CD`)을 담을 컬럼이 없다. 검색 VO에 `searchSchul`/
 *   `searchGrade`가 있지만 매퍼의 `<if>` 블록이 비어 있어 필터가 조용히 무시된다.
 * 이슈: 키워드가 별도 테이블이 아니라 `quiz_title` 한 칸이다. 목록의 「키워드」 열이 이 값이고,
 *   기획의 "키워드+설명 쌍"을 담을 자리는 아직 없다.
 * 이슈: 정답 노출 문구(기획 "기본 3종 + 기타")를 담을 컬럼이 없다.
 */

const QUIZ_PATH = '/api/v1/mngr/quiz';

/** 목록에서 단건을 찾을 때 한 번에 받아올 최대 행 수(단건 조회 API가 없다). */
const DETAIL_SCAN_LIMIT = 500;

/** 엑셀 업로드는 일반 조회보다 오래 걸린다. */
const UPLOAD_TIMEOUT_MS = 60_000;

function isRecord(value: unknown): value is Record<string, unknown> {
  return value !== null && typeof value === 'object';
}

function readString(source: Record<string, unknown>, key: string): string | null {
  const value = source[key];
  if (typeof value === 'string' && value.length > 0) return value;
  if (typeof value === 'number') return String(value);
  return null;
}

function toQuizItem(raw: unknown): QuizItem {
  if (!isRecord(raw)) {
    throw new Error('퀴즈 응답 항목의 형식이 올바르지 않습니다.');
  }

  const quizSn = readString(raw, 'quizSn');
  if (!quizSn) {
    throw new Error('퀴즈 응답에 quizSn이 없습니다.');
  }

  const points = raw.quizPoint;

  return {
    quizSn,
    keywordText: readString(raw, 'quizTitle') ?? '',
    level: readString(raw, 'quizLev') ?? '',
    question: readString(raw, 'quizQuestion') ?? '',
    explanation: readString(raw, 'quizExplan') ?? '',
    answer: readString(raw, 'quizAnswer') ?? '',
    points: typeof points === 'number' ? points : null,
    isVisible: readString(raw, 'useYn') !== 'N',
    modifiedAt:
      readString(raw, 'lastMdfcnDtStr') ?? readString(raw, 'frstRegDtStr'),
  };
}

function buildListParams(
  query: ContentQuery,
  pageIndex: number,
  recordCountPerPage: number
): Record<string, string | number> {
  const keyword = query.keyword.trim();

  return {
    searchSchul: query.school,
    searchGrade: query.grade,
    searchLevel: query.level,
    searchCondition: keyword ? '1' : '',
    searchKeyword: keyword,
    searchSortOrder: query.sort,
    pageIndex,
    recordCountPerPage,
  };
}

async function requestList(
  query: ContentQuery,
  pageIndex: number,
  recordCountPerPage: number
): Promise<{ items: QuizItem[]; reportedTotalCount: number }> {
  const accessToken = await getSessionAccessToken();

  const result = await backendFetch<unknown>(`${QUIZ_PATH}/pagination`, {
    method: 'GET',
    query: buildListParams(query, pageIndex, recordCountPerPage),
    accessToken: accessToken ?? undefined,
    cache: 'no-store',
    canHaveNullData: true,
  });

  if (!result.ok) {
    throw new BackendRequestError(result);
  }

  const data = result.data;
  if (data === null || data === undefined) {
    return { items: [], reportedTotalCount: 0 };
  }
  if (!isRecord(data) || !Array.isArray(data.list)) {
    throw new Error('퀴즈 목록 응답의 형식이 올바르지 않습니다.');
  }

  return {
    items: data.list.map(toQuizItem),
    reportedTotalCount:
      typeof data.totalCount === 'number' ? data.totalCount : data.list.length,
  };
}

export type QuizPage = {
  items: QuizItem[];
  totalCount: number;
  isTotalCountExact: boolean;
};

export async function fetchQuizzes(query: ContentQuery): Promise<QuizPage> {
  const { items, reportedTotalCount } = await requestList(
    query,
    query.page,
    query.pageSize
  );

  const confirmedCount = (query.page - 1) * query.pageSize + items.length;

  return {
    items,
    totalCount: Math.max(reportedTotalCount, confirmedCount),
    isTotalCountExact:
      items.length < query.pageSize || reportedTotalCount > confirmedCount,
  };
}

/** TODO: `GET /api/v1/mngr/quiz/{quizSn}`이 생기면 이 스캔을 그 호출로 교체한다. */
export async function fetchQuizDetail(quizSn: string): Promise<QuizItem | null> {
  const { items } = await requestList(
    {
      school: '',
      grade: '',
      level: '',
      searchField: '',
      keyword: '',
      sort: 'created',
      page: 1,
      pageSize: 10,
    },
    1,
    DETAIL_SCAN_LIMIT
  );

  return items.find((item) => item.quizSn === quizSn) ?? null;
}

function buildQuizPayload(values: QuizFormValues) {
  return {
    quizTitle: values.keywords.map((item) => item.keyword).join(', '),
    quizLev: values.level,
    quizQuestion: values.question,
    quizExplan: values.explanation,
    quizAnswer: values.answer,
    schulCd: values.schoolCode,
    gradeCd: values.grades.join(','),
    answerMssageCd: values.answerMessageCode,
    answerMssage: values.answerMessageText,
    useYn: values.isVisible ? 'Y' : 'N',
  };
}

async function sendWrite(
  path: string,
  method: 'POST' | 'PUT' | 'DELETE',
  body?: unknown
): Promise<string | null> {
  const accessToken = await getSessionAccessToken();

  const result = await backendFetch<unknown>(path, {
    method,
    ...(body === undefined ? {} : { body }),
    accessToken: accessToken ?? undefined,
    cache: 'no-store',
    canHaveNullData: true,
    canHaveEmptyBody: true,
  });

  if (!result.ok) {
    throw new BackendRequestError(result);
  }

  return typeof result.data === 'string' ? result.data : null;
}

export async function createQuiz(values: QuizFormValues): Promise<string | null> {
  return sendWrite(QUIZ_PATH, 'POST', buildQuizPayload(values));
}

export async function updateQuiz(
  quizSn: string,
  values: QuizFormValues
): Promise<void> {
  await sendWrite(
    `${QUIZ_PATH}/${encodeURIComponent(quizSn)}`,
    'PUT',
    buildQuizPayload(values)
  );
}

export async function deleteQuiz(quizSn: string): Promise<void> {
  await sendWrite(`${QUIZ_PATH}/${encodeURIComponent(quizSn)}`, 'DELETE');
}

/** 엑셀 일괄등록. 한 행이라도 검증에 실패하면 백엔드가 전체를 되돌린다. */
export async function uploadQuizExcel(file: File): Promise<void> {
  const accessToken = await getSessionAccessToken();

  const multipart = new FormData();
  multipart.set('file', file);

  const result = await backendFetch<unknown>(`${QUIZ_PATH}/excel/upload`, {
    method: 'POST',
    multipart,
    accessToken: accessToken ?? undefined,
    cache: 'no-store',
    timeoutMs: UPLOAD_TIMEOUT_MS,
    canHaveNullData: true,
  });

  if (!result.ok) {
    throw new BackendRequestError(result);
  }
}
