'use server';

import { redirect } from 'next/navigation';
import { revalidatePath } from 'next/cache';
import { deleteSession, readSessionToken } from '@/lib/auth/session';
import { getSessionAccessToken } from '@/lib/auth/dal';
import { requestAdminLogout } from '@/lib/data/repositories/auth-repository';
import { LOGOUT_ERROR_QUERY_PARAM } from '@/lib/constants/logout';

/**
 * 로그아웃 Server Action — 자기 세션을 파기하는 동작이라 `verifySession()`의 전제(관리자 조회)가
 * 성립하지 않는다. 토큰 존재 여부만 확인해 이미 로그아웃된 상태의 중복 호출도 안전하게 처리한다.
 *
 * 백엔드 로그아웃 호출(GET .../auth/logout)은 best-effort다 — 실패해도(백엔드 다운,
 * accessToken 만료 등) 로컬 세션은 반드시 파기한다. 사용자가 로그아웃 자체를 못 하게 되는
 * 상황을 절대 만들지 않기 위함이다. accessToken이 애초에 없으면(getSessionAccessToken()이
 * 형식·만료·유저일치 검사에서 이미 null을 반환한 경우) 호출 자체를 건너뛴다 — 보낼 유효한
 * 토큰이 없는 것은 "실패"가 아니라 "애초에 알릴 대상이 없음"이므로 실패로 취급하지 않는다.
 *
 * 실패 알림은 이 액션이 직접 띄우지 않는다 — Server Action에서 redirect()는 즉시 던져지므로
 * (NEXT_REDIRECT) 클라이언트가 반환값을 돌려받아 토스트를 그릴 기회 자체가 없다(redirect와
 * 클라이언트 상태 갱신이 같은 턴에서 경쟁하게 됨). 대신 `/login`에 쿼리 파라미터를 실어 보내고,
 * 그 화면의 클라이언트 컴포넌트(LogoutNotice)가 마운트된 뒤(=redirect가 완전히 끝난 뒤) 토스트를
 * 띄운다 — 시점이 분리되어 있어 경쟁이 원천적으로 없다.
 */
export async function logout(): Promise<void> {
  const token = await readSessionToken();
  if (!token) {
    redirect('/login');
  }

  const accessToken = await getSessionAccessToken();
  const isBackendLogoutSuccessful = accessToken
    ? await requestAdminLogout(accessToken)
    : true;

  await deleteSession();
  revalidatePath('/', 'layout');
  redirect(
    isBackendLogoutSuccessful ? '/login' : `/login?${LOGOUT_ERROR_QUERY_PARAM}=1`
  );
}
