import 'server-only';
import { getSessionAccessToken } from '@/lib/auth/dal';
import { BackendRequestError, backendFetch } from '@/lib/http/backend-fetch';
import type { AdminMember } from '@/lib/domain/admin-member';
import type {
  AdminMemberQuery,
  AdminMemberSearchField,
} from '@/lib/domain/admin-member-query';

const ADMIN_MEMBER_BASE_PATH = '/api/v1/mngr/admin';
const ADMIN_MEMBER_PAGINATION_PATH = `${ADMIN_MEMBER_BASE_PATH}/pagination`;

/**
 * 검색 대상 → 백엔드 `searchCondition`. 매퍼가 이 값으로 컬럼을 고른다
 * (1=USER_NM · 2=LOGIN_ID · 3=USER_TEL_NO).
 */
const SEARCH_CONDITION_BY_FIELD: Record<AdminMemberSearchField, string> = {
  name: '1',
  loginId: '2',
  phoneNumber: '3',
};

/**
 * 이름순 정렬에만 쓰는 상한. 백엔드에 정렬 파라미터가 없어 그때만 전체를 받아 여기서
 * 정렬하고 자른다 — 검색·페이징은 백엔드가 한다.
 */
const ADMIN_MEMBER_FETCH_LIMIT = 10_000;

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

function readRequiredString(
  source: Record<string, unknown>,
  key: string
): string {
  const value = source[key];
  if (typeof value !== 'string' || value.length === 0) {
    throw new Error(`관리자 회원 응답에 ${key}가 없습니다.`);
  }
  return value;
}

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

function parseYesNo(value: unknown): boolean | null {
  if (value === 'Y') return true;
  if (value === 'N') return false;
  return null;
}

function toAdminMember(raw: unknown): AdminMember {
  if (!isRecord(raw)) {
    throw new Error('관리자 회원 응답 항목의 형식이 올바르지 않습니다.');
  }

  const loginFailCnt = raw.loginFailCnt;

  return {
    id: readRequiredString(raw, 'userId'),
    name: readRequiredString(raw, 'userNm'),
    loginId: readRequiredString(raw, 'loginId'),
    phoneNumber: readOptionalString(raw, 'userTelNo'),
    email: readOptionalString(raw, 'userEmlAddr'),
    roleCode: readOptionalString(raw, 'roleId') ?? '',
    createdAt: readOptionalString(raw, 'frstRegDtStr'),
    isLocked: parseYesNo(raw.acctLockYn),
    isActive: parseYesNo(raw.useYn),
    loginFailCount: typeof loginFailCnt === 'number' ? loginFailCnt : null,
  };
}

/** 검색 조건을 백엔드 파라미터로 옮긴다. 검색어가 비면 조건 자체를 보내지 않는다. */
function toSearchParams(query: AdminMemberQuery): Record<string, string> {
  const keyword = query.keyword.trim();
  if (!keyword) {
    return {};
  }
  return {
    searchCondition: SEARCH_CONDITION_BY_FIELD[query.searchField],
    searchKeyword: keyword,
  };
}

async function requestPage(
  params: Record<string, string | number>
): Promise<{ items: AdminMember[]; totalCount: number }> {
  const accessToken = await getSessionAccessToken();

  const result = await backendFetch<unknown>(ADMIN_MEMBER_PAGINATION_PATH, {
    method: 'GET',
    query: params,
    accessToken: accessToken ?? undefined,
    cache: 'no-store',
  });

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

  const data = result.data;
  if (!isRecord(data) || !Array.isArray(data.list)) {
    throw new Error('관리자 회원 목록 응답의 형식이 올바르지 않습니다.');
  }

  const totalCount =
    typeof data.totalCount === 'number' ? data.totalCount : data.list.length;

  return { items: data.list.map(toAdminMember), totalCount };
}

function sortItems(
  items: AdminMember[],
  query: AdminMemberQuery
): AdminMember[] {
  if (query.sort !== 'name') {
    return items;
  }
  return [...items].sort((a, b) => a.name.localeCompare(b.name, 'ko'));
}

export type AdminMemberPage = {
  items: AdminMember[];
  totalCount: number;
};

/**
 * 검색·페이징은 백엔드가 한다 — 매퍼가 `searchCondition`으로 컬럼을 고르고 별도 카운트
 * 쿼리로 전체 건수를 센다.
 *
 * **이름순만 예외다.** 백엔드에 정렬 파라미터가 없어 그때는 전체를 받아 여기서 정렬하고
 * 자른다. 정렬이 붙으면 이 갈래를 지우면 된다.
 */
export async function fetchAdminMembers(
  query: AdminMemberQuery
): Promise<AdminMemberPage> {
  const search = toSearchParams(query);

  if (query.sort === 'name') {
    const { items } = await requestPage({
      pageIndex: 1,
      recordCountPerPage: ADMIN_MEMBER_FETCH_LIMIT,
      ...search,
    });
    const sorted = sortItems(items, query);
    const offset = (query.page - 1) * query.pageSize;
    return {
      items: sorted.slice(offset, offset + query.pageSize),
      totalCount: sorted.length,
    };
  }

  return requestPage({
    pageIndex: query.page,
    recordCountPerPage: query.pageSize,
    ...search,
  });
}

export async function findAdminMemberById(
  id: string
): Promise<AdminMember | null> {
  const accessToken = await getSessionAccessToken();
  const result = await backendFetch<unknown>(
    `${ADMIN_MEMBER_BASE_PATH}/${encodeURIComponent(id)}`,
    {
      method: 'GET',
      accessToken: accessToken ?? undefined,
      cache: 'no-store',
      canHaveNullData: true,
    }
  );

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

  return result.data === null ? null : toAdminMember(result.data);
}

export async function isAdminLoginIdTaken(loginId: string): Promise<boolean> {
  const accessToken = await getSessionAccessToken();

  const result = await backendFetch<unknown>(
    `${ADMIN_MEMBER_BASE_PATH}/duplication/${encodeURIComponent(loginId.trim())}`,
    {
      method: 'GET',
      accessToken: accessToken ?? undefined,
      cache: 'no-store',
      canHaveNullData: true,
    }
  );

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

  return result.data !== null;
}

export type CreateAdminMemberInput = {
  name: string;
  loginId: string;
  password: string;
  phoneNumber: string;
  email: string;
  roleCode: string;
};

export type UpdateAdminMemberInput = {
  password: string;
  phoneNumber: string;
  email: string;
  roleCode: string;
  isLocked: boolean;
};

function toYesNo(value: boolean): string {
  return value ? 'Y' : 'N';
}

export async function createAdminMember(
  input: CreateAdminMemberInput
): Promise<void> {
  const accessToken = await getSessionAccessToken();

  const result = await backendFetch<unknown>(ADMIN_MEMBER_BASE_PATH, {
    method: 'POST',
    // 이슈: 등록은 form(@ParameterObject), 수정은 JSON(@RequestBody)로 갈려 있다.
    form: {
      userNm: input.name,
      loginId: input.loginId,
      loginPw: input.password,
      userEmlAddr: input.email,
      userTelNo: input.phoneNumber,
      roleId: input.roleCode,
    },
    accessToken: accessToken ?? undefined,
    canHaveNullData: true,
  });

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

export async function updateAdminMember(
  id: string,
  input: UpdateAdminMemberInput
): Promise<void> {
  const accessToken = await getSessionAccessToken();

  const result = await backendFetch<unknown>(
    `${ADMIN_MEMBER_BASE_PATH}/${encodeURIComponent(id)}`,
    {
      method: 'PUT',
      body: {
        loginPw: input.password,
        userEmlAddr: input.email,
        userTelNo: input.phoneNumber,
        roleId: input.roleCode,
        acctLockYn: toYesNo(input.isLocked),
      },
      accessToken: accessToken ?? undefined,
      canHaveNullData: true,
    }
  );

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

export async function deleteAdminMember(id: string): Promise<void> {
  const accessToken = await getSessionAccessToken();

  const result = await backendFetch<unknown>(
    `${ADMIN_MEMBER_BASE_PATH}/${encodeURIComponent(id)}`,
    {
      method: 'DELETE',
      accessToken: accessToken ?? undefined,
      canHaveNullData: true,
    }
  );

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