"use server"
import { generateTokenForVsCode } from "@/app/(dashboard)/apikeys/tokenfuncs"
import { getUserOrganizations } from "@/components/dashboard/action"
import { getUserId } from "@/app/utils/auth"
import crypto from "crypto"
import jwt from "jsonwebtoken"
import { CacheContainer } from "node-ts-cache"
import { MemoryStorage } from "node-ts-cache-storage-memory"
import { organizationMemberRepository } from "@codeflash-ai/common"
import { cookies } from "next/headers"

const RATE_LIMIT = 5
const RATE_LIMIT_WINDOW_MS = 60 * 1000
const rateLimitCache = new CacheContainer(new MemoryStorage())
function getJwtSecret(): string {
  const secret = process.env.JWT_SECRET
  if (!secret) {
    throw new Error("JWT_SECRET environment variable is required")
  }
  return secret
}
const JWT_SECRET: string = getJwtSecret()

interface OAuthStatePayload {
  userId: string
  redirectUri: string
  codeChallenge: string
  codeChallengeMethod: string
  clientId: string
  orgId?: string
  type: "oauth_state"
}

interface AuthCodePayload {
  userId: string
  codeChallenge: string
  codeChallengeMethod: string
  redirectUri: string
  clientId: string
  orgId?: string
  type: "auth_code"
}

export interface Organization {
  id: string
  name: string
  role: "admin" | "owner" | "member"
  avatarUrl?: string
}

export interface UserInfo {
  id: string
  name: string
  email: string
  avatarUrl?: string
}

export async function fetchUserInfo(): Promise<{
  user?: UserInfo
  error?: string
}> {
  try {
    const { auth0 } = await import("@/lib/auth0")
    const session = await auth0.getSession()

    if (!session?.user) {
      return { error: "Unauthorized" }
    }

    return {
      user: {
        id: session.user.sub,
        name: session.user.name || session.user.nickname || "User",
        email: session.user.email || "",
        avatarUrl: session.user.picture,
      },
    }
  } catch {
    return { error: "Failed to fetch user info" }
  }
}

export async function fetchUserOrganizations(): Promise<{
  organizations?: Organization[]
  error?: string
}> {
  try {
    const userId = await getUserId()
    if (!userId) {
      return { error: "Unauthorized" }
    }

    const result = await getUserOrganizations(userId)
    if (!result.success) {
      return { error: result.error || "Failed to fetch organizations" }
    }

    return { organizations: result.organizations }
  } catch {
    return { error: "Failed to fetch organizations" }
  }
}

const OAUTH_COOKIE_NAME = "oauth_params"

interface OAuthParams {
  redirectUri: string
  codeChallenge: string
  codeChallengeMethod: string
  clientId: string
  vscodeState: string
}

export async function storeOAuthParams(params: OAuthParams): Promise<{ error?: string }> {
  try {
    const userId = await getUserId()
    if (!userId) {
      return { error: "Unauthorized" }
    }

    const signed = jwt.sign({ ...params, type: "oauth_params" }, JWT_SECRET, {
      expiresIn: "10m",
    })

    const cookieStore = await cookies()
    cookieStore.set(OAUTH_COOKIE_NAME, signed, {
      httpOnly: true,
      sameSite: "lax",
      secure: process.env.NODE_ENV === "production",
      path: "/codeflash/auth",
      maxAge: 600,
    })

    return {}
  } catch {
    return { error: "Failed to store OAuth parameters" }
  }
}

export async function getStoredOAuthParams(): Promise<{
  params?: OAuthParams
  error?: string
}> {
  try {
    const cookieStore = await cookies()
    const cookie = cookieStore.get(OAUTH_COOKIE_NAME)
    if (!cookie?.value) {
      return { error: "Session expired. Please refresh the page and try again." }
    }

    const payload = jwt.verify(cookie.value, JWT_SECRET) as unknown as OAuthParams & {
      type: string
    }
    if (payload.type !== "oauth_params") {
      return { error: "Invalid session" }
    }

    return {
      params: {
        redirectUri: payload.redirectUri,
        codeChallenge: payload.codeChallenge,
        codeChallengeMethod: payload.codeChallengeMethod,
        clientId: payload.clientId,
        vscodeState: payload.vscodeState,
      },
    }
  } catch {
    return { error: "Session expired. Please refresh the page and try again." }
  }
}

async function clearOAuthCookie() {
  const cookieStore = await cookies()
  cookieStore.delete(OAUTH_COOKIE_NAME)
}

export async function isRateLimited(userId: string): Promise<boolean> {
  const cacheKey = `rate_limit_vsc_signin_${userId}`
  const record = await rateLimitCache.getItem<{ count: number; startTime: number }>(cacheKey)
  const now = Date.now()

  if (!record || now - record.startTime > RATE_LIMIT_WINDOW_MS) {
    await rateLimitCache.setItem(
      cacheKey,
      { count: 1, startTime: now },
      { ttl: RATE_LIMIT_WINDOW_MS / 1000 },
    )
    return false
  }

  if (record.count >= RATE_LIMIT) {
    return true
  }

  record.count++
  await rateLimitCache.setItem(cacheKey, record, {
    ttl: (RATE_LIMIT_WINDOW_MS - (now - record.startTime)) / 1000,
  })

  return false
}

export async function createOAuthState(params: {
  redirectUri: string
  codeChallenge: string
  codeChallengeMethod: string
  clientId: string
  orgId?: string
}): Promise<{ state: string; error?: string }> {
  try {
    const userId = await getUserId()
    if (!userId) {
      return { state: "", error: "Unauthorized" }
    }
    if (params.orgId) {
      const isMember = await organizationMemberRepository.isMember(params.orgId, userId)
      if (!isMember) {
        return { state: "", error: "Unauthorized" }
      }
    }

    const limited = await isRateLimited(userId)
    if (limited) {
      return { state: "", error: "Rate limit exceeded" }
    }

    const statePayload: OAuthStatePayload = {
      userId,
      redirectUri: params.redirectUri,
      codeChallenge: params.codeChallenge,
      codeChallengeMethod: params.codeChallengeMethod,
      clientId: params.clientId,
      orgId: params.orgId,
      type: "oauth_state",
    }

    const state = jwt.sign(statePayload, JWT_SECRET, {
      expiresIn: "2m",
      jwtid: crypto.randomBytes(16).toString("hex"),
    })

    return { state }
  } catch {
    return { state: "", error: "Internal server error" }
  }
}

export async function authorizeOAuth(state: string): Promise<{
  code?: string
  redirectUri?: string
  error?: string
}> {
  try {
    const userId = await getUserId()
    if (!userId) {
      return { error: "Unauthorized" }
    }

    let oauthState: OAuthStatePayload
    try {
      oauthState = jwt.verify(state, JWT_SECRET) as unknown as OAuthStatePayload
    } catch {
      return { error: "Invalid or expired state" }
    }

    if (oauthState.type !== "oauth_state") {
      return { error: "Invalid state token" }
    }

    if (oauthState.userId !== userId) {
      return { error: "User mismatch" }
    }

    const authCodePayload: AuthCodePayload = {
      userId,
      codeChallenge: oauthState.codeChallenge,
      codeChallengeMethod: oauthState.codeChallengeMethod,
      redirectUri: oauthState.redirectUri,
      clientId: oauthState.clientId,
      orgId: oauthState.orgId,
      type: "auth_code",
    }

    const code = jwt.sign(authCodePayload, JWT_SECRET, {
      expiresIn: "2m",
      jwtid: crypto.randomBytes(16).toString("hex"),
    })

    await clearOAuthCookie()

    return {
      code,
      redirectUri: oauthState.redirectUri,
    }
  } catch {
    return { error: "Internal server error" }
  }
}

interface TokenExchangeParams {
  code: string
  codeVerifier: string
  redirectUri: string
  clientId: string
}

export async function exchangeCodeForToken(
  params: TokenExchangeParams,
): Promise<{ accessToken?: string; error?: string }> {
  try {
    let codeData: AuthCodePayload
    try {
      codeData = jwt.verify(params.code, JWT_SECRET) as unknown as AuthCodePayload
    } catch {
      return { error: "Invalid or expired authorization code" }
    }

    if (codeData.type !== "auth_code") {
      return { error: "Invalid authorization code" }
    }

    if (codeData.clientId !== params.clientId) {
      return { error: "Client ID mismatch" }
    }

    if (codeData.redirectUri !== params.redirectUri) {
      return { error: "Redirect URI mismatch" }
    }

    const computedChallenge = crypto
      .createHash(codeData.codeChallengeMethod)
      .update(params.codeVerifier)
      .digest("base64url")

    if (computedChallenge !== codeData.codeChallenge) {
      return { error: "Code verifier validation failed" }
    }

    try {
      const apiKey = await generateTokenForVsCode(codeData.userId, codeData.orgId)
      return { accessToken: apiKey.token }
    } catch (tokenError: unknown) {
      if (tokenError instanceof Error && tokenError.message === "NEXT_REDIRECT") {
        return { error: "Authentication required" }
      }
      return { error: "Failed to generate API token" }
    }
  } catch {
    return { error: "Internal server error" }
  }
}
