import type { IdTokenClaims } from '@logto/node';
import { type LogtoContext, makeLogtoRemix } from '@logto/remix';
import type { LoaderFunctionArgs } from '@remix-run/node';
import { createCookieSessionStorage, redirect } from '@remix-run/node';
import { createScopedLogger } from '~/lib/.server/logger.server';
import type { LogtoUser } from '~/types/logto';

const logger = createScopedLogger('auth.server');

/**
 * 认证相关类型定义
 */
interface LogtoConfig {
  endpoint: string;
  appId: string;
  appSecret: string;
  baseUrl: string;
  scopes?: string[];
}

interface MockUser extends Pick<LogtoContext, 'isAuthenticated' | 'userInfo' | 'claims'> {}

/**
 * 虚拟用户接口，与 MockUser 类似但代表真实存在于 logto 的用户
 */
interface VirtualUser extends Pick<LogtoContext, 'isAuthenticated' | 'userInfo' | 'claims'> {
  isVirtual: true;
}

// Logto路由配置类型
interface LogtoRoutes {
  'sign-in': { path: string; redirectBackTo: string };
  'sign-in-callback': { path: string; redirectBackTo: string };
  'sign-out': { path: string; redirectBackTo: string };
  'sign-up': { path: string; redirectBackTo: string };
}

/**
 * 公共 Cookie 配置基础
 */
const baseCookieOptions = {
  httpOnly: true,
  path: '/',
  sameSite: 'lax' as const,
  secrets: [process.env.LOGTO_COOKIE_SECRET || 's3cr3t'],
};

/**
 * 创建认证 session 存储
 */
const sessionStorage = createCookieSessionStorage({
  cookie: {
    ...baseCookieOptions,
    name: 'logto_session',
    maxAge: 60 * 60 * 24 * 30, // 30 天过期
  },
});

/**
 * 创建虚拟用户 session 存储
 */
const virtualUserStorage = createCookieSessionStorage({
  cookie: {
    ...baseCookieOptions,
    name: 'virtual_user',
    maxAge: 60 * 60 * 24 * 30,
  },
});

/**
 * 创建认证错误信息 session 存储
 */
const errorSessionStorage = createCookieSessionStorage({
  cookie: {
    ...baseCookieOptions,
    name: 'auth_error',
    maxAge: 60, // 1分钟后过期，错误信息不需要长期保存
  },
});

/**
 * 创建 Logto 配置
 */
const config: LogtoConfig = {
  endpoint: process.env.LOGTO_ENDPOINT || '',
  appId: process.env.LOGTO_APP_ID || '',
  appSecret: process.env.LOGTO_APP_SECRET || '',
  baseUrl: process.env.LOGTO_BASE_URL || 'http://localhost:5173',
  scopes: ['email', 'profile'],
};

// 创建原始 Logto 实例（私有，不直接导出）
const originalLogto = makeLogtoRemix(config, { sessionStorage });

export function shouldEnforceAuth(): boolean {
  return process.env.LOGTO_ENABLE === 'true';
}

function getMockDevUser(): MockUser {
  return {
    isAuthenticated: true,
    userInfo: {
      iss: 'https://mock.issuer.com',
      sub: 'mock-user-id',
      aud: 'mock-audience',
      exp: Math.floor(Date.now() / 1000) + 3600,
      iat: Math.floor(Date.now() / 1000),
      name: 'Mock User',
      username: 'user',
      email: 'mock@example.com',
    },
  };
}

/**
 * 设置认证错误信息到会话中
 */
export async function setAuthError(errorMessage: string): Promise<string> {
  const session = await errorSessionStorage.getSession();
  session.set('authError', errorMessage);
  return errorSessionStorage.commitSession(session);
}

/**
 * 获取并清除认证错误信息
 */
export async function getAuthError(
  request: Request,
): Promise<{ errorMessage?: string; headers: { 'Set-Cookie': string } }> {
  const session = await errorSessionStorage.getSession(request.headers.get('Cookie'));
  const errorMessage = session.get('authError') as string | undefined;

  // 清除错误信息
  return {
    errorMessage,
    headers: {
      'Set-Cookie': await errorSessionStorage.destroySession(session),
    },
  };
}

/**
 * 增强版的 Logto 对象，添加错误处理和开发环境跳过
 */
export const logto = {
  ...originalLogto,

  /**
   * 增强版的 handleAuthRoutes 方法
   */
  handleAuthRoutes: (routes: LogtoRoutes) => {
    const originalHandler = originalLogto.handleAuthRoutes(routes);

    return async (args: LoaderFunctionArgs) => {
      try {
        // 特殊处理退出登录路由
        const { request } = args;
        const url = new URL(request.url);
        const path = url.pathname;

        // 如果是退出登录路由，检查是否是虚拟用户
        if (path === routes['sign-out'].path) {
          const virtualUser = await getVirtualUser(request);

          if (virtualUser?.isVirtual) {
            logger.info('[Auth] 虚拟用户退出登录');
            const clearCookie = await clearVirtualUser();
            return redirect(routes['sign-out'].redirectBackTo, {
              headers: {
                'Set-Cookie': clearCookie,
              },
            });
          }
        }
        return await originalHandler(args);
      } catch (error) {
        logger.error('[Auth] 认证服务错误:', error);
        return handleAuthError(error, args);
      }
    };
  },

  getContext: originalLogto.getContext,
};

/**
 * 处理认证过程中的错误
 */
async function handleAuthError(error: unknown, args: LoaderFunctionArgs) {
  // 判断错误类型
  let errorMessage = '认证服务暂时不可用，请稍后再试';

  // 处理网络错误（认证服务器不可用）
  if (
    error instanceof Error &&
    (error.message.includes('fetch failed') ||
      error.message.includes('network') ||
      error.message.includes('ECONNREFUSED') ||
      error.message.includes('timeout'))
  ) {
    errorMessage = '无法连接到认证服务器，请检查网络连接';
  }
  // 其他类型的错误
  else if (error instanceof Error) {
    errorMessage = '登录服务出现异常，请稍后再试';
  }

  // 确定重定向URL
  const redirectUrl = determineRedirectUrl(args.request);

  // 生成带有错误信息的Cookie
  const cookie = await setAuthError(errorMessage);

  // 重定向回原始页面，同时携带错误会话Cookie
  return redirect(redirectUrl, {
    headers: {
      'Set-Cookie': cookie,
    },
  });
}

/**
 * 根据请求确定最合适的重定向URL
 */
function determineRedirectUrl(request: Request): string {
  // 默认重定向到首页
  const defaultUrl = '/';

  try {
    const url = new URL(request.url);
    const redirectTo = url.searchParams.get('redirectTo');

    if (redirectTo && redirectTo.startsWith('/') && !redirectTo.startsWith('//')) {
      // 确保只重定向到应用内部URL
      return redirectTo;
    }

    // 如果有referer头，也可以考虑使用它
    const referer = request.headers.get('referer');
    if (referer) {
      try {
        const refererUrl = new URL(referer);
        // 确保是同一域名下的URL
        if (refererUrl.hostname === url.hostname) {
          return refererUrl.pathname + refererUrl.search;
        }
      } catch {
        // 忽略无效的referer
      }
    }
  } catch {
    // 如果URL解析失败，使用默认的根路径
  }

  return defaultUrl;
}

/**
 * 设置虚拟用户信息到 cookie
 * @param userInfo 用户信息
 * @returns cookie 字符串，可用于 HTTP 响应头
 */
export async function setVirtualUser(userInfo: LogtoUser): Promise<string> {
  const session = await virtualUserStorage.getSession();

  const virtualUserInfo = {
    id: userInfo.id,
    iss: process.env.LOGTO_ENDPOINT || 'https://auth.upage.io',
    sub: userInfo.id,
    aud: process.env.LOGTO_APP_ID || 'virtual-app',
    // 30天后过期
    exp: Math.floor(Date.now() / 1000) + 60 * 60 * 24 * 30,
    iat: Math.floor(Date.now() / 1000),
    name: userInfo.name || null,
    email: userInfo.primaryEmail || null,
    phone_number: userInfo.primaryPhone || null,
    username: userInfo.username || null,
    picture: userInfo.avatar || null,
  } as IdTokenClaims;

  session.set('userInfo', virtualUserInfo);
  session.set('isAuthenticated', true);

  return virtualUserStorage.commitSession(session);
}

/**
 * 获取虚拟用户信息
 * @param request 请求对象
 * @returns 虚拟用户信息，如果不存在则返回 null
 */
export async function getVirtualUser(request: Request): Promise<VirtualUser | null> {
  const cookieHeader = request.headers.get('Cookie');
  const session = await virtualUserStorage.getSession(cookieHeader);

  const isAuthenticated = session.get('isAuthenticated');
  const userInfo = session.get('userInfo') as IdTokenClaims;

  if (!isAuthenticated || !userInfo) {
    return null;
  }

  // 验证用户信息是否有效
  const now = Math.floor(Date.now() / 1000);
  if (userInfo.exp && userInfo.exp < now) {
    // 用户信息已过期，清除 session
    await clearVirtualUser();
    return null;
  }

  // 获取上次验证时间
  const lastVerified = session.get('lastVerified') || 0;
  const verifyInterval = 60 * 60 * 1000;

  // 如果距离上次验证时间不足验证间隔，则跳过 Logto 验证
  if (now * 1000 - lastVerified < verifyInterval) {
    return {
      isAuthenticated: true,
      userInfo,
      isVirtual: true,
    };
  }

  try {
    // 更新验证时间
    session.set('lastVerified', Date.now());
    await virtualUserStorage.commitSession(session);

    return {
      isAuthenticated: true,
      userInfo,
      isVirtual: true,
    };
  } catch (error) {
    logger.error('[Auth] 验证虚拟用户失败:', error);
    // 如果验证过程中出现错误，仍然返回用户信息
    return {
      isAuthenticated: true,
      userInfo,
      isVirtual: true,
    };
  }
}

/**
 * 清除虚拟用户信息
 * @returns cookie 字符串，用于清除虚拟用户 cookie
 */
export async function clearVirtualUser(): Promise<string> {
  const session = await virtualUserStorage.getSession();
  return virtualUserStorage.destroySession(session);
}

/**
 * 检查用户是否已认证，如果未认证，则重定向到登录页面
 */
export async function requireUser(request: Request) {
  const context = await getUser(request);

  if (!context.isAuthenticated) {
    return redirect('/api/auth/sign-in');
  }

  return context;
}

/**
 * 获取当前用户信息
 * 按优先级依次检查：
 * 1. 开发环境模拟用户
 * 2. 虚拟用户
 * 3. Logto 认证用户
 */
export async function getUser(request: Request) {
  // 首先检查是否为开发环境
  if (!shouldEnforceAuth()) {
    return getMockDevUser();
  }

  // 检查是否存在虚拟用户
  const virtualUser = await getVirtualUser(request);
  if (virtualUser) {
    return virtualUser;
  }

  // 继续原有的 logto 认证流程
  return await logto.getContext({
    fetchUserInfo: true,
    getAccessToken: true,
  })(request);
}

/**
 * 通用权限验证中间件
 * 用于API和页面路由的权限验证
 *
 * 返回json错误或重定向到登录页面
 */
export async function requireAuth(request: Request, options: { isApi?: boolean; redirectTo?: string } = {}) {
  const { isApi = false, redirectTo = '/api/auth/sign-in' } = options;

  const context = await getUser(request);

  if (!context.isAuthenticated) {
    if (isApi) {
      // API路由返回JSON错误
      return new Response(
        JSON.stringify({
          error: 'Unauthorized',
          message: '请先登录',
          code: 401,
        }),
        {
          status: 401,
          headers: {
            'Content-Type': 'application/json',
          },
        },
      );
    }

    // 页面路由重定向到登录页面
    return redirect(redirectTo);
  }

  return context;
}
