/**
 * @file features/auth/actions.ts
 * @description 인증 관련 서버 액션 (Server Actions) 모음
 * @remarks
 * - [레이어] Service/API (Server Actions)
 * - [역할] 로그인, 회원가입, 로그아웃, 비밀번호 재설정 등 인증 로직 처리
 * - [데이터 흐름] Client Form -> Server Action -> Supabase Auth -> Client Redirect
 * - [연관 파일] login-form.tsx, signup-form.tsx, utils/supabase/server.ts
 */

"use server";

import { revalidatePath } from "next/cache";
import { cookies } from "next/headers";
import { redirect } from "next/navigation";
import { createClient } from "@/utils/supabase/server";
import {
  AUTH_ERROR_MESSAGES,
  type AuthFormData,
  type AuthError,
  RECOVERY_COOKIE_NAME,
} from "./constants";
import { getAuthErrorMessage } from "./errors";

// ========================================
// 헬퍼 함수
// ========================================

/**
 * FormData 추출 헬퍼 (이메일/비밀번호)
 * @param formData HTML form 데이터
 * @returns 이메일(trim 적용), 비밀번호
 */
function extractAuthData(formData: FormData): AuthFormData {
  const email = (formData.get("email") as string)?.trim() || "";
  const password = (formData.get("password") as string) || "";

  return { email, password };
}

/**
 * 비밀번호 강도 검증 함수
 * @param password 검증할 비밀번호
 * @returns 에러 객체 또는 null
 * @remarks 최소 8자, 대/소문자, 숫자, 특수문자 포함 필수
 */
function validatePassword(password: string): AuthError | null {
  // [Step 1] 최소 길이 체크 (8자 이상)
  if (password.length < 8) {
    return {
      message: AUTH_ERROR_MESSAGES.PASSWORD_TOO_SHORT,
      type: "validation",
    };
  }

  // [Step 2] 대문자 포함 여부
  if (!/[A-Z]/.test(password)) {
    return {
      message: AUTH_ERROR_MESSAGES.PASSWORD_TOO_WEAK,
      type: "validation",
    };
  }

  // [Step 3] 소문자 포함 여부
  if (!/[a-z]/.test(password)) {
    return {
      message: AUTH_ERROR_MESSAGES.PASSWORD_TOO_WEAK,
      type: "validation",
    };
  }

  // [Step 4] 숫자 포함 여부
  if (!/[0-9]/.test(password)) {
    return {
      message: AUTH_ERROR_MESSAGES.PASSWORD_TOO_WEAK,
      type: "validation",
    };
  }

  // [Step 5] 특수문자 포함 여부
  if (!/[!@#$%^&*(),.?":{}|<>]/.test(password)) {
    return {
      message: AUTH_ERROR_MESSAGES.PASSWORD_TOO_WEAK,
      type: "validation",
    };
  }

  // [Step 6] 모든 검증 통과
  return null;
}

/**
 * 입력값 유효성 검증 함수
 * @param email 사용자 이메일
 * @param password 사용자 비밀번호
 * @returns 에러 객체 또는 null
 * @see login - 로그인 액션에서 호출
 * @see signup - 회원가입 액션에서 호출
 */
function validateAuthInput(email: string, password: string): AuthError | null {
  // [Step 1] 빈 값 체크
  if (!email || !password) {
    return {
      message: AUTH_ERROR_MESSAGES.EMPTY_FIELDS,
      type: "validation",
    };
  }

  // [Step 2] 이메일 형식 체크 (간단한 @ 포함 여부 확인)
  if (!email.includes("@")) {
    return {
      message: AUTH_ERROR_MESSAGES.INVALID_EMAIL,
      type: "validation",
    };
  }

  // [Step 3] 비밀번호 강도 체크
  const passwordValidation = validatePassword(password);
  if (passwordValidation) {
    return passwordValidation;
  }

  // [Step 4] 검증 통과
  return null;
}

// ========================================
// Server Actions (서버 액션)
// ========================================

/**
 * [로그인 액션]
 *
 * 사용자가 입력한 이메일/비밀번호로 로그인을 시도합니다.
 *
 * 처리 과정:
 * 1. FormData에서 이메일/비밀번호 추출
 * 2. 입력값 유효성 검증 (빈 값, 이메일 형식, 비밀번호 길이)
 * 3. Supabase Auth를 통한 로그인 시도
 * 4. 로그인 실패 시 에러 메시지와 함께 로그인 페이지로 리다이렉트
 * 5. 로그인 성공 시 캐시 무효화 및 메인 페이지로 리다이렉트
 *
 * @param formData 이메일, 비밀번호가 포함된 FormData
 * @see login-form.tsx - 로그인 폼 제출 시 호출
 */
export async function login(formData: FormData) {
  // [Step 1] FormData에서 이메일/비밀번호 추출
  const { email, password } = extractAuthData(formData);

  // [Step 2] 입력값 유효성 검증
  const validationError = validateAuthInput(email, password);
  if (validationError) {
    return redirect(
      `/login?message=${encodeURIComponent(validationError.message)}`,
    );
  }

  // [Step 3] Supabase 클라이언트 생성 및 로그인 시도
  const supabase = await createClient();
  const { error } = await supabase.auth.signInWithPassword({
    email,
    password,
  });

  // [Step 4] 로그인 실패 시 에러 처리
  if (error) {
    const message = getAuthErrorMessage(error);
    return redirect(`/login?message=${encodeURIComponent(message)}`);
  }

  // [Step 5] 로그인 성공 - 캐시 무효화 및 메인 페이지로 리다이렉트
  revalidatePath("/", "layout");
  redirect("/");
}

/**
 * [회원가입 액션]
 *
 * 새로운 사용자를 등록합니다.
 *
 * 처리 과정:
 * 1. FormData에서 이메일/비밀번호 추출
 * 2. 입력값 유효성 검증
 * 3. Supabase Auth를 통한 회원가입 시도
 * 4. 이메일 인증 리다이렉트 URL 설정 (확인 링크 클릭 시 돌아올 주소)
 * 5-1. 즉시 세션 생성 시: 메인 페이지로 리다이렉트 (바로 로그인됨)
 * 5-2. 이메일 인증 필요 시: 로그인 페이지로 리다이렉트 (안내 메시지 포함)
 *
 * @param formData 이메일, 비밀번호가 포함된 FormData
 * @see signup-form.tsx - 회원가입 폼 제출 시 호출
 */
export async function signup(formData: FormData) {
  // [Step 1] FormData에서 이메일/비밀번호 추출
  const { email, password } = extractAuthData(formData);

  // [Step 2] 입력값 유효성 검증
  const validationError = validateAuthInput(email, password);
  if (validationError) {
    return redirect(
      `/signup?message=${encodeURIComponent(validationError.message)}`,
    );
  }

  // [Step 3] Supabase 클라이언트 생성 및 회원가입 시도
  const supabase = await createClient();
  const { data, error } = await supabase.auth.signUp({
    email,
    password,
    options: {
      // 이메일 인증 완료 후 리다이렉트될 URL
      emailRedirectTo: `${process.env.NEXT_PUBLIC_BASE_URL || "http://localhost:3001"}/auth/callback?auth_type=signup`,
    },
  });

  // [Step 4] 회원가입 실패 시 에러 처리
  if (error) {
    const message = getAuthErrorMessage(error);
    return redirect(`/signup?message=${encodeURIComponent(message)}`);
  }

  // [Step 5] 회원가입 성공 처리
  if (data.session) {
    // [Case 1] 즉시 세션 생성됨 (이메일 인증 불필요)
    revalidatePath("/", "layout");
    redirect("/");
  }

  // [Case 2] 이메일 인증 필요 (로그인 페이지로 이동)
  revalidatePath("/", "layout");
  redirect(
    `/login?message=${encodeURIComponent("회원가입이 완료되었습니다. 이메일을 확인하여 인증을 완료해 주세요.")}`,
  );
}

/**
 * [로그아웃 액션]
 *
 * 현재 세션을 종료하고 로그인 페이지로 이동합니다.
 *
 * 처리 과정:
 * 1. Supabase Auth 세션 종료 (서버 + 클라이언트 쿠키 삭제)
 * 2. Next.js 캐시 무효화하여 인증 상태 갱신
 * 3. 로그인 페이지로 리다이렉트
 *
 * @see user-menu.tsx - 로그아웃 메뉴 클릭 시 호출
 * @see session-manager.tsx - 세션 타임아웃 시 호출
 */
export async function signout() {
  const supabase = await createClient();

  // [Step 1] Supabase 세션 종료 (서버 + 클라이언트 쿠키 삭제)
  await supabase.auth.signOut();

  // [Step 2] Next.js 캐시 무효화
  revalidatePath("/", "layout");

  // [Step 3] 로그인 페이지로 리다이렉트
  redirect("/");
}

/**
 * [비밀번호 재설정 요청 액션]
 *
 * 사용자 이메일로 비밀번호 재설정 링크를 발송합니다.
 * 보안을 위해 이메일 존재 여부와 관계없이 동일한 메시지를 표시합니다.
 *
 * 처리 과정:
 * 1. FormData에서 이메일 추출
 * 2. 이메일 형식 검증
 * 3. Supabase를 통한 재설정 링크 발송
 * 4. 성공 메시지와 함께 로그인 페이지로 리다이렉트
 *
 * @param formData 이메일 포함
 * @see forgot-password/page.tsx - 비밀번호 찾기 폼 제출 시 호출
 */
export async function requestPasswordReset(formData: FormData) {
  // [Step 1] FormData에서 이메일 추출
  const email = (formData.get("email") as string)?.trim() || "";

  // [Step 2] 이메일 유효성 검증
  if (!email) {
    return redirect(
      `/forgot-password?message=${encodeURIComponent(AUTH_ERROR_MESSAGES.EMPTY_EMAIL)}`,
    );
  }

  if (!email.includes("@")) {
    return redirect(
      `/forgot-password?message=${encodeURIComponent(AUTH_ERROR_MESSAGES.INVALID_EMAIL)}`,
    );
  }

  // [Step 3] Supabase를 통한 재설정 링크 발송
  const supabase = await createClient();
  const { error } = await supabase.auth.resetPasswordForEmail(email, {
    redirectTo: `${process.env.NEXT_PUBLIC_BASE_URL || "http://localhost:3001"}/reset-password`,
  });

  // [Step 4] 에러 처리
  if (error) {
    console.error("Password reset error:", error.message);
    const message = getAuthErrorMessage(error);
    return redirect(`/forgot-password?message=${encodeURIComponent(message)}`);
  }

  // [Step 5] 성공 메시지 표시 (보안상 항상 성공 메시지 리턴 권장)
  redirect(
    `/login?message=${encodeURIComponent(AUTH_ERROR_MESSAGES.PASSWORD_RESET_SENT)}`,
  );
}

/**
 * [비밀번호 업데이트 액션]
 *
 * 비밀번호 재설정 링크를 통해 접근한 사용자의 비밀번호를 업데이트합니다.
 *
 * 처리 과정:
 * 1. FormData에서 새 비밀번호 추출
 * 2. 비밀번호 길이 및 강도 검증
 * 3. Supabase를 통한 비밀번호 업데이트
 * 4. 실패 시 에러 메시지 반환
 * 5. 성공 시 세션/쿠키 정리 후 로그아웃 및 캐시 무효화
 * 6. 성공 결과 반환
 *
 * @param formData 새 비밀번호 포함
 * @see reset-password-form.tsx - 비밀번호 재설정 폼 제출 시 호출
 */
export async function updatePassword(formData: FormData) {
  // [Step 1] 새 비밀번호 추출
  const password = (formData.get("password") as string) || "";

  // [Step 2] 비밀번호 강도 검증
  const passwordValidation = validatePassword(password);
  if (passwordValidation) {
    return { ok: false, message: passwordValidation.message };
  }

  // [Step 3] Supabase를 통한 비밀번호 업데이트
  const supabase = await createClient();
  const { error } = await supabase.auth.updateUser({
    password: password,
  });

  // [Step 4] 에러 처리
  if (error) {
    const message = getAuthErrorMessage(error);
    return { ok: false, message };
  }

  // [Step 5] 세션 및 쿠키 정리 후 로그아웃
  const cookieStore = await cookies();
  cookieStore.delete(RECOVERY_COOKIE_NAME);
  await supabase.auth.signOut();
  revalidatePath("/", "layout");

  // [Step 6] 성공 응답 반환
  return {
    ok: true,
    message: AUTH_ERROR_MESSAGES.PASSWORD_RESET_SUCCESS,
  };
}

// ========================================
// 소셜 로그인 (OAuth)
// ========================================

/**
 * [OAuth 로그인 공통 헬퍼]
 *
 * 처리 과정:
 * 1. Supabase OAuth 로그인 URL 생성 (PKCE)
 * 2. 생성 중 에러 발생 시 로그인 페이지로 리다이렉트 (에러 메시지 포함)
 * 3. 성공 시 해당 OAuth 제공자 페이지(data.url)로 리다이렉트
 *
 * @param provider 'google' | 'kakao'
 * @param extraOptions 추가 옵션 (예: prompt)
 * @see signInWithGoogle
 * @see signInWithKakao
 */
async function signInWithProvider(
  provider: "google" | "kakao",
  extraOptions: { queryParams?: { [key: string]: string } } = {},
) {
  const supabase = await createClient();

  // [Step 1] OAuth 인증 시작 (URL 생성)
  const { data, error } = await supabase.auth.signInWithOAuth({
    provider,
    options: {
      // PKCE 플로우를 위한 콜백 URL
      redirectTo: `${process.env.NEXT_PUBLIC_BASE_URL || "http://localhost:3001"}/auth/callback`,
      ...extraOptions,
    },
  });

  // [Step 2] 에러 처리
  if (error) {
    console.error(`[${provider} OAuth] 로그인 실패:`, error.message);
    const message = getAuthErrorMessage(error);
    return redirect(`/login?message=${encodeURIComponent(message)}`);
  }

  // [Step 3] OAuth 제공자 로그인 페이지로 리다이렉트
  if (data.url) {
    redirect(data.url);
  }

  // [Step 4] URL 생성 실패 시 에러 처리
  redirect(
    `/login?message=${encodeURIComponent("로그인 처리 중 오류가 발생했습니다.")}`,
  );
}

/**
 * [Google 로그인 액션]
 * @see login-form.tsx - 구글 로그인 버튼 클릭 시 호출
 */
export async function signInWithGoogle() {
  return signInWithProvider("google");
}

/**
 * [Kakao 로그인 액션]
 * @see login-form.tsx - 카카오 로그인 버튼 클릭 시 호출
 */
export async function signInWithKakao() {
  return signInWithProvider("kakao", { queryParams: { prompt: "login" } });
}
