"use server";

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

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

/**
 * [FormData 추출 헬퍼]
 *
 * FormData에서 이메일과 비밀번호를 안전하게 추출합니다.
 * - 이메일은 trim()으로 공백 제거
 * - null/undefined 방지를 위해 기본값 "" 사용
 *
 * @param formData - HTML form에서 전달된 FormData 객체
 * @returns AuthFormData - 추출된 이메일과 비밀번호
 */
function extractAuthData(formData: FormData): AuthFormData {
  const email = (formData.get("email") as string)?.trim() || "";
  const password = (formData.get("password") as string) || "";

  return { email, password };
}

/**
 * [비밀번호 강도 검증 함수]
 *
 * 안전한 비밀번호인지 검사합니다.
 *
 * 비밀번호 정책:
 * - 최소 8자 이상
 * - 대문자 1개 이상 포함
 * - 소문자 1개 이상 포함
 * - 숫자 1개 이상 포함
 * - 특수문자 1개 이상 포함 (!@#$%^&*(),.?":{}|<> 등)
 *
 * @param password - 검증할 비밀번호
 * @returns AuthError | null - 에러가 있으면 에러 객체, 없으면 null
 */
function validatePassword(password: string): AuthError | null {
  // 1. 최소 길이 체크 (6자 이상)
  if (password.length < 6) {
    return {
      message: AUTH_ERROR_MESSAGES.PASSWORD_TOO_SHORT,
      type: "validation",
    };
  }

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

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

  // 모든 검증 통과
  return null;
}

/**
 * [입력값 검증 함수]
 *
 * 사용자가 입력한 이메일과 비밀번호의 유효성을 검사합니다.
 *
 * 검증 항목:
 * 1. 빈 값 체크 - 이메일 또는 비밀번호가 비어있는지 확인
 * 2. 이메일 형식 - '@' 포함 여부로 간단한 형식 검증
 * 3. 비밀번호 길이 - 최소 6자 이상인지 확인 (Supabase 기본 요구사항)
 *
 * @param email - 사용자 이메일
 * @param password - 사용자 비밀번호
 * @returns AuthError | null - 에러가 있으면 에러 객체, 없으면 null
 */
function validateAuthInput(email: string, password: string): AuthError | null {
  // 1. 빈 값 체크
  if (!email || !password) {
    return {
      message: AUTH_ERROR_MESSAGES.EMPTY_FIELDS,
      type: "validation",
    };
  }

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

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

  // 모든 검증 통과
  return null;
}

/**
 * [에러 메시지 번역 헬퍼]
 *
 * Supabase의 영문 에러 메시지를 사용자 친화적인 한글로 변환합니다.
 *
 * @param error - Supabase에서 받은 에러 메시지
 * @returns string - 한글로 번역된 에러 메시지
 */
function getErrorMessage(error: string): string {
  // Supabase 에러 메시지 패턴 매칭
  if (error.includes("Invalid login credentials")) {
    return AUTH_ERROR_MESSAGES.INVALID_CREDENTIALS;
  }
  if (error.includes("User already registered")) {
    return AUTH_ERROR_MESSAGES.USER_EXISTS;
  }
  if (error.includes("Password should be at least")) {
    return AUTH_ERROR_MESSAGES.PASSWORD_TOO_SHORT;
  }
  if (error.includes("Email not confirmed")) {
    return AUTH_ERROR_MESSAGES.EMAIL_NOT_CONFIRMED;
  }
  if (error.toLowerCase().includes("email rate limit exceeded")) {
    return AUTH_ERROR_MESSAGES.EMAIL_RATE_LIMIT_DETAILED;
  }

  // 알 수 없는 에러는 기본 메시지 반환
  return AUTH_ERROR_MESSAGES.DEFAULT;
}

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

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

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

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

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

  // 5. 로그인 성공 - 캐시 무효화 및 메인 페이지로 리다이렉트
  // revalidatePath: Next.js 캐시를 무효화하여 최신 인증 상태 반영
  revalidatePath("/", "layout");
  redirect("/");
}

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

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

  // 3. Supabase 클라이언트 생성 및 회원가입 시도
  const supabase = await createClient();
  const { data, error } = await supabase.auth.signUp({
    email,
    password,
    options: {
      // 이메일 인증 완료 후 리다이렉트될 URL
      // 로컬 개발 환경: http://localhost:3001/auth/callback
      // 프로덕션: NEXT_PUBLIC_BASE_URL 환경 변수에 설정된 주소
      emailRedirectTo: `${process.env.NEXT_PUBLIC_BASE_URL || "http://localhost:3001"}/auth/callback`,
    },
  });

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

  // 5. 회원가입 성공 - 세션 생성 여부에 따라 분기 처리
  if (data.session) {
    // 5-1. 즉시 세션이 생성된 경우 (이메일 인증 불필요)
    // → 바로 메인 페이지로 이동
    revalidatePath("/", "layout");
    redirect("/");
  }

  // 5-2. 이메일 인증이 필요한 경우
  // → 로그인 페이지로 이동 + 안내 메시지
  revalidatePath("/", "layout");
  redirect(
    `/login?message=${encodeURIComponent("회원가입이 완료되었습니다. 이메일을 확인하여 인증을 완료해 주세요.")}`,
  );
}

/**
 * [로그아웃 액션]
 *
 * 현재 세션을 종료하고 로그인 페이지로 이동합니다.
 *
 * 처리 과정:
 * 1. Supabase Auth 세션 종료
 * 2. 캐시 무효화하여 인증 상태 갱신
 * 3. 로그인 페이지로 리다이렉트
 */
export async function signout() {
  const supabase = await createClient();

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

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

  // 3. 로그인 페이지로 리다이렉트
  redirect("/login");
}

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

  // 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)}`,
    );
  }

  // 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`,
  });

  // 4. 에러 처리
  if (error) {
    console.error("Password reset error:", error.message);

    // Rate limit 오류는 사용자에게 알려줌 (보안과 무관)
    if (error.message.toLowerCase().includes("rate limit")) {
      return redirect(
        `/forgot-password?message=${encodeURIComponent(
          "이메일 발송 제한을 초과했습니다. Supabase 무료 플랜은 시간당 이메일 발송 횟수가 제한됩니다. 약 1시간 후에 다시 시도해 주세요.",
        )}`,
      );
    }

    // 그 외 에러는 보안상 동일한 메시지 표시
    // (이메일 존재 여부를 외부에 노출하지 않음)
  }

  // 5. 성공 메시지 표시
  redirect(
    `/login?message=${encodeURIComponent(AUTH_ERROR_MESSAGES.PASSWORD_RESET_SENT)}`,
  );
}

/**
 * [비밀번호 업데이트 액션]
 *
 * 비밀번호 재설정 링크를 통해 접근한 사용자의 비밀번호를 업데이트합니다.
 *
 * 처리 과정:
 * 1. FormData에서 새 비밀번호 추출
 * 2. 비밀번호 길이 검증
 * 3. Supabase를 통한 비밀번호 업데이트
 * 4. 성공 시 로그인 페이지로 리다이렉트
 *
 * @param formData - 새 비밀번호가 포함된 FormData
 */
export async function updatePassword(formData: FormData) {
  // 1. FormData에서 새 비밀번호 추출
  const password = (formData.get("password") as string) || "";

  // 2. 비밀번호 강도 검증
  const passwordValidation = validatePassword(password);
  if (passwordValidation) {
    return redirect(
      `/reset-password?message=${encodeURIComponent(passwordValidation.message)}`,
    );
  }

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

  // 4. 에러 처리
  if (error) {
    const message = getErrorMessage(error.message);
    return redirect(`/reset-password?message=${encodeURIComponent(message)}`);
  }

  // 5. 성공 - 캐시 무효화 및 로그인 페이지로 리다이렉트
  revalidatePath("/", "layout");
  redirect(
    `/login?message=${encodeURIComponent(AUTH_ERROR_MESSAGES.PASSWORD_RESET_SUCCESS)}`,
  );
}

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

/**
 * [OAuth 로그인 헬퍼 함수]
 *
 * Google, Kakao 등의 소셜 로그인 제공자를 통해 인증을 수행합니다.
 *
 * PKCE (Proof Key for Code Exchange) 플로우:
 * 1. 이 함수가 signInWithOAuth를 호출하면 Supabase가 OAuth URL 반환
 * 2. 사용자를 해당 OAuth 제공자(Google/Kakao) 로그인 페이지로 리다이렉트
 * 3. 사용자가 로그인 및 권한 동의
 * 4. OAuth 제공자가 /auth/callback?code=xxx로 리다이렉트
 * 5. callback 라우트에서 code를 세션으로 교환 (exchangeCodeForSession)
 * 6. 메인 페이지로 최종 리다이렉트
 *
 * 자동 회원가입:
 * - 첫 로그인 시 auth.users 테이블에 자동으로 사용자 생성
 * - 이메일, provider, metadata(프로필 사진, 이름 등) 저장
 * - 기존 사용자는 그냥 로그인
 *
 * @param provider - OAuth 제공자 ('google' | 'kakao')
 */
async function signInWithProvider(provider: "google" | "kakao") {
  const supabase = await createClient();

  // 1. OAuth 인증 시작
  const { data, error } = await supabase.auth.signInWithOAuth({
    provider,
    options: {
      // PKCE 플로우를 위한 콜백 URL
      // OAuth 제공자 인증 후 이 URL로 code와 함께 리다이렉트됨
      redirectTo: `${process.env.NEXT_PUBLIC_BASE_URL || "http://localhost:3001"}/auth/callback`,
    },
  });

  // 2. 에러 처리
  if (error) {
    console.error(`[${provider} OAuth] 로그인 실패:`, error.message);
    return redirect(
      `/login?message=${encodeURIComponent(`${provider} 로그인에 실패했습니다. 다시 시도해 주세요.`)}`,
    );
  }

  // 3. OAuth 제공자 로그인 페이지로 리다이렉트
  // data.url은 Google/Kakao의 인증 페이지 URL
  if (data.url) {
    redirect(data.url);
  }

  // 4. URL이 없는 경우 (예상치 못한 상황)
  redirect(
    `/login?message=${encodeURIComponent("로그인 처리 중 오류가 발생했습니다.")}`,
  );
}

/**
 * [Google 로그인 액션]
 *
 * Google 계정으로 로그인합니다.
 * "Google로 로그인" 버튼에서 호출됩니다.
 *
 * 처리 과정:
 * 1. signInWithOAuth 호출하여 Google OAuth URL 받기
 * 2. Google 로그인 페이지로 리다이렉트
 * 3. (사용자가 Google에서 로그인 및 권한 동의)
 * 4. /auth/callback으로 돌아와서 세션 생성
 * 5. 메인 페이지로 이동
 */
export async function signInWithGoogle() {
  return signInWithProvider("google");
}

/**
 * [Kakao 로그인 액션]
 *
 * Kakao 계정으로 로그인합니다.
 * "Kakao로 로그인" 버튼에서 호출됩니다.
 *
 * 처리 과정:
 * 1. signInWithOAuth 호출하여 Kakao OAuth URL 받기
 * 2. Kakao 로그인 페이지로 리다이렉트
 * 3. (사용자가 Kakao에서 로그인 및 권한 동의)
 * 4. /auth/callback으로 돌아와서 세션 생성
 * 5. 메인 페이지로 이동
 */
export async function signInWithKakao() {
  return signInWithProvider("kakao");
}
