import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
import createMiddleware from 'next-intl/middleware';
import { locales, defaultLocale } from '@/i18n/config';

/**
 * Combined Middleware for Multi-tenant ERP System
 *
 * Features:
 * 1. Internationalization (i18n) with locale detection
 * 2. Bot Detection and blocking for security
 * 3. Authentication and authorization (Sanctum/Bearer/API-Key)
 *
 * Strategy: Moderate bot blocking + Session-based auth
 * - Allows legitimate browsers and necessary crawlers
 * - Blocks bots from accessing sensitive ERP areas
 * - Protects routes with session/token authentication
 * - Prevents Chrome security warnings by not being too aggressive
 */

// Auth configuration
import { AUTH_CONFIG } from '@/lib/api/auth/auth-config';

/**
 * 🔄 미들웨어 전용 토큰 갱신 캐시 (Edge Runtime)
 *
 * 목적: 페이지 렌더링 전에 토큰을 미리 갱신하여 race condition 방지
 * - auth/check와 serverFetch가 동시에 refresh_token을 사용하는 문제 해결
 * - 미들웨어에서 먼저 갱신하면 이후 요청들은 새 access_token 사용
 *
 * 주의: Edge Runtime에서는 모듈 레벨 변수가 요청 간 공유되지 않을 수 있음
 * 따라서 5초 캐시로 같은 요청 내 중복 갱신만 방지
 */
let middlewareRefreshCache: {
  promise: Promise<{ success: boolean; accessToken?: string; refreshToken?: string; expiresIn?: number }> | null;
  timestamp: number;
  result: { success: boolean; accessToken?: string; refreshToken?: string; expiresIn?: number } | null;
} = {
  promise: null,
  timestamp: 0,
  result: null,
};

const MIDDLEWARE_REFRESH_CACHE_TTL = 5000; // 5초

// Create i18n middleware
const intlMiddleware = createMiddleware({
  locales,
  defaultLocale,
  localePrefix: 'as-needed', // Don't show default locale in URL
});

// Common bot user-agent patterns (case-insensitive)
const BOT_PATTERNS = [
  /bot/i,
  /crawler/i,
  /spider/i,
  /scraper/i,
  /curl/i,
  /wget/i,
  /python-requests/i,
  /scrapy/i,
  /axios/i,              // Programmatic access
  /headless/i,
  /phantom/i,
  /selenium/i,
  /puppeteer/i,
  /playwright/i,         // Browser automation tools
  /go-http-client/i,
  /java/i,
  /okhttp/i,
  /apache-httpclient/i,
];

// Paths that should be protected from bots
const PROTECTED_PATHS = [
  '/dashboard',
  '/admin',
  '/api',
  '/tenant',
  '/settings',
  '/users',
  '/reports',
  '/analytics',
  '/inventory',
  '/finance',
  '/hr',
  '/crm',
  '/employee',
  '/customer',
  '/supplier',
  '/orders',
  '/invoices',
  '/payroll',
];

// Paths that are allowed for everyone (including bots)
const PUBLIC_PATHS = [
  '/',
  '/login',
  '/about',
  '/contact',
  '/robots.txt',
  '/sitemap.xml',
  '/favicon.ico',
];

/**
 * Check if user-agent matches known bot patterns
 */
function isBot(userAgent: string): boolean {
  if (!userAgent) return false;

  return BOT_PATTERNS.some(pattern => pattern.test(userAgent));
}

/**
 * Check if user-agent is Internet Explorer
 * IE 11: Contains "Trident" in user-agent
 * IE 10 and below: Contains "MSIE" in user-agent
 */
function isInternetExplorer(userAgent: string): boolean {
  if (!userAgent) return false;

  return /MSIE|Trident/.test(userAgent);
}

/**
 * Check if the path should be protected from bots
 */
function isProtectedPath(pathname: string): boolean {
  return PROTECTED_PATHS.some(path => pathname.startsWith(path));
}

/**
 * Check if the path is public and accessible to all
 * Note: Currently unused but kept for future use
 */
function _isPublicPath(pathname: string): boolean {
  return PUBLIC_PATHS.some(path => pathname === path || pathname.startsWith(path));
}

/**
 * Remove locale prefix from pathname for bot checking
 */
function getPathnameWithoutLocale(pathname: string): string {
  for (const locale of locales) {
    if (pathname.startsWith(`/${locale}/`) || pathname === `/${locale}`) {
      return pathname.slice(`/${locale}`.length) || '/';
    }
  }
  return pathname;
}

/**
 * 인증 체크 함수
 * 3가지 인증 방식 지원: Bearer Token/Sanctum/API-Key
 *
 * 🔄 추가: needsRefresh - access_token이 없고 refresh_token만 있는 경우
 * 이 경우 미들웨어에서 사전 갱신하여 페이지 로드 시 race condition 방지
 */
function checkAuthentication(request: NextRequest): {
  isAuthenticated: boolean;
  authMode: 'sanctum' | 'bearer' | 'api-key' | null;
  needsRefresh: boolean;
  refreshToken: string | null;
} {
  // 1. Bearer Token 확인 (쿠키에서)
  const accessToken = request.cookies.get('access_token');
  const refreshToken = request.cookies.get('refresh_token');

  // 🔄 access_token이 없고 refresh_token만 있으면 사전 갱신 필요
  if (!accessToken?.value && refreshToken?.value) {
    return {
      isAuthenticated: true,
      authMode: 'bearer',
      needsRefresh: true,
      refreshToken: refreshToken.value,
    };
  }

  // access_token이 있으면 갱신 불필요
  if (accessToken?.value) {
    return {
      isAuthenticated: true,
      authMode: 'bearer',
      needsRefresh: false,
      refreshToken: refreshToken?.value || null,
    };
  }

  // 2. Bearer Token 확인 (Authorization 헤더)
  const authHeader = request.headers.get('authorization');
  if (authHeader?.startsWith('Bearer ')) {
    return { isAuthenticated: true, authMode: 'bearer', needsRefresh: false, refreshToken: null };
  }

  // 3. Sanctum 세션 쿠키 확인 (레거시 지원)
  const sessionCookie = request.cookies.get('laravel_session');
  if (sessionCookie) {
    return { isAuthenticated: true, authMode: 'sanctum', needsRefresh: false, refreshToken: null };
  }

  // 4. API Key 확인
  const apiKey = request.headers.get('x-api-key');
  if (apiKey) {
    return { isAuthenticated: true, authMode: 'api-key', needsRefresh: false, refreshToken: null };
  }

  return { isAuthenticated: false, authMode: null, needsRefresh: false, refreshToken: null };
}

/**
 * 라우트 타입 확인 함수들
 */
function isGuestOnlyRoute(pathname: string): boolean {
  return AUTH_CONFIG.guestOnlyRoutes.some(route =>
    pathname === route || pathname.startsWith(route)
  );
}

function isPublicRoute(pathname: string): boolean {
  return AUTH_CONFIG.publicRoutes.some(route => {
    // '/' 는 정확히 일치해야만 public
    if (route === '/') {
      return pathname === '/';
    }
    // 다른 라우트는 시작 일치 허용
    return pathname === route || pathname.startsWith(route + '/');
  });
}

/**
 * 🔄 미들웨어에서 토큰 갱신 (페이지 렌더링 전)
 *
 * 목적: Race Condition 방지
 * - 문제: auth/check와 serverFetch가 동시에 refresh_token 사용
 * - 해결: 미들웨어에서 먼저 갱신하여 페이지 로드 전에 새 토큰 준비
 *
 * 5초 캐싱으로 중복 요청 방지
 */
async function refreshTokenInMiddleware(
  refreshToken: string
): Promise<{ success: boolean; accessToken?: string; refreshToken?: string; expiresIn?: number }> {
  const now = Date.now();

  // 1. 캐시된 성공 결과가 유효하면 즉시 반환
  if (middlewareRefreshCache.result && middlewareRefreshCache.result.success && now - middlewareRefreshCache.timestamp < MIDDLEWARE_REFRESH_CACHE_TTL) {
    console.log(`🔵 [Middleware] Using cached refresh result (age: ${now - middlewareRefreshCache.timestamp}ms)`);
    return middlewareRefreshCache.result;
  }

  // 2. 진행 중인 refresh가 있으면 기다림
  if (middlewareRefreshCache.promise && !middlewareRefreshCache.result && now - middlewareRefreshCache.timestamp < MIDDLEWARE_REFRESH_CACHE_TTL) {
    console.log(`🔵 [Middleware] Waiting for ongoing refresh...`);
    return middlewareRefreshCache.promise;
  }

  // 3. 이전 refresh가 실패했으면 캐시 초기화
  if (middlewareRefreshCache.result && !middlewareRefreshCache.result.success) {
    middlewareRefreshCache.promise = null;
    middlewareRefreshCache.result = null;
  }

  // 4. 새 refresh 시작
  console.log(`🔄 [Middleware] Starting pre-refresh before page render...`);
  middlewareRefreshCache.timestamp = now;
  middlewareRefreshCache.result = null;

  middlewareRefreshCache.promise = (async () => {
    try {
      const refreshUrl = `${process.env.NEXT_PUBLIC_API_URL}/api/v1/refresh`;
      const response = await fetch(refreshUrl, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
          'Accept': 'application/json',
          'X-API-KEY': process.env.API_KEY || '',
        },
        body: JSON.stringify({ refresh_token: refreshToken }),
      });

      if (!response.ok) {
        console.warn('🔴 [Middleware] Pre-refresh failed:', response.status);
        return { success: false };
      }

      const data = await response.json();
      console.log('✅ [Middleware] Pre-refresh successful');

      return {
        success: true,
        accessToken: data.access_token,
        refreshToken: data.refresh_token,
        expiresIn: data.expires_in,
      };
    } catch (error) {
      console.error('🔴 [Middleware] Pre-refresh error:', error);
      return { success: false };
    }
  })();

  middlewareRefreshCache.result = await middlewareRefreshCache.promise;
  return middlewareRefreshCache.result;
}

export async function middleware(request: NextRequest) {
  const { pathname } = request.nextUrl;
  const userAgent = request.headers.get('user-agent') || '';

  // 🚨 -1️⃣ Next.js 내부 요청 필터링
  // 동적 라우트 세그먼트가 리터럴로 포함된 요청은 Next.js 내부 컴파일/prefetch
  // 예: /[locale]/settings/... 형태의 요청은 실제 사용자 요청이 아님
  if (pathname.includes('[') && pathname.includes(']')) {
    // console.log(`[Internal Request Skip] Dynamic segment in path: ${pathname}`);
    return NextResponse.next();
  }

  // 🚨 0️⃣ Internet Explorer Detection (최우선 처리)
  // IE 사용자는 지원 안내 페이지로 리다이렉트
  if (isInternetExplorer(userAgent)) {
    // unsupported-browser.html 페이지 자체는 제외 (무한 리다이렉트 방지)
    if (!pathname.includes('unsupported-browser')) {
      console.log(`[IE Blocked] ${userAgent} attempted to access ${pathname}`);
      return NextResponse.redirect(new URL('/unsupported-browser.html', request.url));
    }
  }

  // 1️⃣ 로케일 제거
  const pathnameWithoutLocale = getPathnameWithoutLocale(pathname);

  // 2️⃣ Bot Detection (기존 로직)
  const isBotRequest = isBot(userAgent);

  // Bot Detection: Block bots from protected paths
  if (isBotRequest && (isProtectedPath(pathname) || isProtectedPath(pathnameWithoutLocale))) {
    console.log(`[Bot Blocked] ${userAgent} attempted to access ${pathname}`);

    return new NextResponse(
      JSON.stringify({
        error: 'Access Denied',
        message: 'Automated access to this resource is not permitted.',
        code: 'BOT_ACCESS_DENIED'
      }),
      {
        status: 403,
        headers: {
          'Content-Type': 'application/json',
          'X-Robots-Tag': 'noindex, nofollow, noarchive, nosnippet',
        },
      }
    );
  }

  // 3️⃣ 정적 파일 제외
  if (
    pathname.includes('/_next/') ||
    pathname.includes('/api/') ||
    pathname.match(/\.(ico|png|jpg|jpeg|svg|gif|webp)$/)
  ) {
    return intlMiddleware(request);
  }

  // 4️⃣ 인증 체크
  const { isAuthenticated, authMode, needsRefresh, refreshToken } = checkAuthentication(request);

  // 4.5️⃣ MVP: /signup 접근 차단 → /login 리다이렉트 (2025-12-04)
  // 회원가입 기능은 운영 페이지로 이동 예정
  if (pathnameWithoutLocale === '/signup' || pathnameWithoutLocale.startsWith('/signup/')) {
    console.log(`[Signup Blocked] Redirecting to /login from ${pathname}`);
    return NextResponse.redirect(new URL('/login', request.url));
  }

  // 5️⃣ 게스트 전용 라우트 (로그인/회원가입)
  // 대전제: "게스트 전용" = 로그인 안 한 사람만 접근 가능
  // 이미 로그인한 사람이 오면 → /dashboard로 보냄
  if (isGuestOnlyRoute(pathnameWithoutLocale)) {
    // 🔄 needsRefresh인 경우: 먼저 refresh 시도해서 "진짜 로그인 상태"인지 확인
    // refresh_token만 있는 상태 = "로그인 가능성 있음" (확정 아님)
    if (needsRefresh && refreshToken) {
      console.log(`🔄 [Middleware] Verifying auth status on guest route: ${pathname}`);

      const refreshResult = await refreshTokenInMiddleware(refreshToken);

      if (refreshResult.success && refreshResult.accessToken) {
        // ✅ refresh 성공 = 진짜 로그인됨 → /dashboard로 (게스트 전용이니까)
        console.log(`✅ [Middleware] Authenticated, redirecting to dashboard from guest route`);

        const isProduction = process.env.NODE_ENV === 'production';
        const response = NextResponse.redirect(new URL(AUTH_CONFIG.redirects.afterLogin, request.url));

        // 새 쿠키 설정
        response.headers.append('Set-Cookie', [
          `access_token=${refreshResult.accessToken}`,
          'HttpOnly',
          ...(isProduction ? ['Secure'] : []),
          'SameSite=Lax',
          'Path=/',
          `Max-Age=${refreshResult.expiresIn || 7200}`,
        ].join('; '));

        response.headers.append('Set-Cookie', [
          `refresh_token=${refreshResult.refreshToken}`,
          'HttpOnly',
          ...(isProduction ? ['Secure'] : []),
          'SameSite=Lax',
          'Path=/',
          'Max-Age=604800',
        ].join('; '));

        response.headers.append('Set-Cookie', [
          'is_authenticated=true',
          ...(isProduction ? ['Secure'] : []),
          'SameSite=Lax',
          'Path=/',
          `Max-Age=${refreshResult.expiresIn || 7200}`,
        ].join('; '));

        return response;
      } else {
        // ❌ refresh 실패 = 로그인 안 됨 → 쿠키 삭제 후 로그인 페이지 표시 (왕복 없이!)
        console.log(`🔴 [Middleware] Not authenticated, showing guest page directly`);

        const isProduction = process.env.NODE_ENV === 'production';
        const intlResponse = intlMiddleware(request);

        // 만료된 쿠키 삭제
        intlResponse.headers.append('Set-Cookie', [
          'access_token=',
          'HttpOnly',
          ...(isProduction ? ['Secure'] : []),
          'SameSite=Lax',
          'Path=/',
          'Max-Age=0',
        ].join('; '));

        intlResponse.headers.append('Set-Cookie', [
          'refresh_token=',
          'HttpOnly',
          ...(isProduction ? ['Secure'] : []),
          'SameSite=Lax',
          'Path=/',
          'Max-Age=0',
        ].join('; '));

        intlResponse.headers.append('Set-Cookie', [
          'is_authenticated=',
          ...(isProduction ? ['Secure'] : []),
          'SameSite=Lax',
          'Path=/',
          'Max-Age=0',
        ].join('; '));

        return intlResponse;
      }
    }

    // access_token 있음 = 확실히 로그인됨 → /dashboard로
    if (isAuthenticated) {
      console.log(`[Already Authenticated] Redirecting to /dashboard from ${pathname}`);
      return NextResponse.redirect(new URL(AUTH_CONFIG.redirects.afterLogin, request.url));
    }

    // 쿠키 아예 없음 = 비로그인 → 로그인 페이지 표시
    return intlMiddleware(request);
  }

  // 6️⃣ 공개 라우트 (명시적으로 지정된 경우만)
  if (isPublicRoute(pathnameWithoutLocale)) {
    return intlMiddleware(request);
  }

  // 7️⃣ 기본 정책: 모든 페이지는 인증 필요
  // guestOnlyRoutes와 publicRoutes가 아닌 모든 경로는 보호됨
  if (!isAuthenticated) {
    console.log(`[Auth Required] Redirecting to /login from ${pathname}`);

    const url = new URL('/login', request.url);
    url.searchParams.set('redirect', pathname);
    return NextResponse.redirect(url);
  }

  // 7️⃣.5️⃣ 🔄 토큰 사전 갱신 (Race Condition 방지)
  // access_token이 없고 refresh_token만 있는 경우, 페이지 렌더링 전에 미리 갱신
  //
  // 🔴 중요: refresh 성공 후 같은 페이지로 리다이렉트해야 함!
  // - 미들웨어에서 Set-Cookie를 설정해도 동시에 발생하는 API 요청은 이전 쿠키 사용
  // - 리다이렉트하면 브라우저가 새 쿠키를 적용한 후 다시 요청
  // - 이렇게 해야 클라이언트의 API 호출이 새 토큰을 사용
  if (needsRefresh && refreshToken) {
    // 🔄 무한 리다이렉트 방지: 이미 refresh 시도 후 돌아온 요청인지 확인
    const url = new URL(request.url);
    if (url.searchParams.has('_refreshed')) {
      // 이미 리프레시 시도 후 돌아왔는데도 needsRefresh=true면 쿠키 저장 실패
      // 무한 루프 방지를 위해 로그인 페이지로 리다이렉트
      console.warn(`🔴 [Middleware] Cookie not saved after refresh, redirecting to login`);

      const isProduction = process.env.NODE_ENV === 'production';
      const loginUrl = new URL('/login', request.url);

      const response = NextResponse.redirect(loginUrl);

      // 쿠키 삭제
      response.headers.append('Set-Cookie', [
        'access_token=', 'HttpOnly', ...(isProduction ? ['Secure'] : []),
        'SameSite=Lax', 'Path=/', 'Max-Age=0',
      ].join('; '));
      response.headers.append('Set-Cookie', [
        'refresh_token=', 'HttpOnly', ...(isProduction ? ['Secure'] : []),
        'SameSite=Lax', 'Path=/', 'Max-Age=0',
      ].join('; '));
      response.headers.append('Set-Cookie', [
        'is_authenticated=', ...(isProduction ? ['Secure'] : []),
        'SameSite=Lax', 'Path=/', 'Max-Age=0',
      ].join('; '));

      return response;
    }

    console.log(`🔄 [Middleware] Pre-refreshing token before page render: ${pathname}`);

    const refreshResult = await refreshTokenInMiddleware(refreshToken);

    if (refreshResult.success && refreshResult.accessToken) {
      const isProduction = process.env.NODE_ENV === 'production';

      // 🆕 리다이렉트로 새 쿠키 적용 후 다시 로드
      // 이렇게 해야 클라이언트의 useEffect에서 호출하는 API들이 새 토큰을 사용
      url.searchParams.set('_refreshed', '1');
      const response = NextResponse.redirect(url);

      // 새 access_token 쿠키 설정
      const accessTokenCookie = [
        `access_token=${refreshResult.accessToken}`,
        'HttpOnly',
        ...(isProduction ? ['Secure'] : []),
        'SameSite=Lax',
        'Path=/',
        `Max-Age=${refreshResult.expiresIn || 7200}`,
      ].join('; ');

      // 새 refresh_token 쿠키 설정
      const refreshTokenCookie = [
        `refresh_token=${refreshResult.refreshToken}`,
        'HttpOnly',
        ...(isProduction ? ['Secure'] : []),
        'SameSite=Lax',
        'Path=/',
        'Max-Age=604800', // 7 days
      ].join('; ');

      // 인증 상태 쿠키
      const isAuthenticatedCookie = [
        'is_authenticated=true',
        ...(isProduction ? ['Secure'] : []),
        'SameSite=Lax',
        'Path=/',
        `Max-Age=${refreshResult.expiresIn || 7200}`,
      ].join('; ');

      response.headers.append('Set-Cookie', accessTokenCookie);
      response.headers.append('Set-Cookie', refreshTokenCookie);
      response.headers.append('Set-Cookie', isAuthenticatedCookie);

      console.log(`✅ [Middleware] Pre-refresh complete, redirecting to apply new cookies`);
      return response;
    } else {
      // 갱신 실패 시 쿠키 삭제 후 로그인 페이지로
      // 🔴 CRITICAL: 쿠키를 삭제하지 않으면 무한 리다이렉트 루프 발생
      // - /login 접근 시 refresh_token 있으면 isAuthenticated=true 판정
      // - "Already Authenticated" → /dashboard로 리다이렉트
      // - 다시 needsRefresh=true → pre-refresh 시도 → 401 실패 → /login
      // - 무한 루프!
      console.warn(`🔴 [Middleware] Pre-refresh failed, clearing cookies and redirecting to login`);

      const isProduction = process.env.NODE_ENV === 'production';
      const url = new URL('/login', request.url);
      url.searchParams.set('redirect', pathname);

      const response = NextResponse.redirect(url);

      // 쿠키 삭제 (Max-Age=0으로 만료 처리)
      const clearAccessToken = [
        'access_token=',
        'HttpOnly',
        ...(isProduction ? ['Secure'] : []),
        'SameSite=Lax',
        'Path=/',
        'Max-Age=0',
      ].join('; ');

      const clearRefreshToken = [
        'refresh_token=',
        'HttpOnly',
        ...(isProduction ? ['Secure'] : []),
        'SameSite=Lax',
        'Path=/',
        'Max-Age=0',
      ].join('; ');

      const clearIsAuthenticated = [
        'is_authenticated=',
        ...(isProduction ? ['Secure'] : []),
        'SameSite=Lax',
        'Path=/',
        'Max-Age=0',
      ].join('; ');

      response.headers.append('Set-Cookie', clearAccessToken);
      response.headers.append('Set-Cookie', clearRefreshToken);
      response.headers.append('Set-Cookie', clearIsAuthenticated);

      return response;
    }
  }

  // 8️⃣ 인증 모드 로깅 (디버깅용)
  if (isAuthenticated) {
    console.log(`[Authenticated] Mode: ${authMode}, Path: ${pathname}`);
  }

  // 9️⃣ i18n 미들웨어 실행
  const intlResponse = intlMiddleware(request);

  // 🔟 보안 헤더 추가
  intlResponse.headers.set('X-Robots-Tag', 'noindex, nofollow, noarchive, nosnippet');
  intlResponse.headers.set('X-Content-Type-Options', 'nosniff');
  intlResponse.headers.set('X-Frame-Options', 'DENY');
  intlResponse.headers.set('Referrer-Policy', 'strict-origin-when-cross-origin');

  // Bot 로깅 (모니터링용)
  if (isBotRequest) {
    console.log(`[Bot Allowed] ${userAgent} accessed ${pathname}`);
  }

  return intlResponse;
}

/**
 * Configure which paths the middleware should run on
 *
 * Matcher configuration:
 * - Excludes static files and assets
 * - Includes all app routes
 */
export const config = {
  matcher: [
    /*
     * Match all pathnames except:
     * - api routes
     * - _next/static (static files)
     * - _next/image (image optimization files)
     * - favicon.ico, robots.txt
     * - files with extensions (images, etc.)
     */
    '/((?!api|_next/static|_next/image|favicon.ico|.*\\..*|robots\\.txt).*)',
  ],
};