"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"

const RATE_LIMIT = 5
const RATE_LIMIT_WINDOW_MS = 60 * 1000
const rateLimitCache = new CacheContainer(new MemoryStorage())
// TODO:: Find a way to save it in Session
const JWT_SECRET = process.env.JWT_SECRET || "abrakadabra-codeflash-jwt-secret"

if (!JWT_SECRET) {
  throw new Error("JWT_SECRET is not defined in environment variables")
}

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 { getSession } = await import("@auth0/nextjs-auth0")
    const session = await 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 (error) {
    console.error("Error fetching user info:", error)
    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 (error) {
    console.error("Error fetching user organizations:", error)
    return { error: "Failed to fetch organizations" }
  }
}

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 },
    )
    console.log(`Rate limit initialized for user: ${userId}`)
    return false
  }

  if (record.count >= RATE_LIMIT) {
    console.warn(`Rate limit exceeded for user: ${userId}, count: ${record.count}`)
    return true
  }

  record.count++
  await rateLimitCache.setItem(cacheKey, record, {
    ttl: (RATE_LIMIT_WINDOW_MS - (now - record.startTime)) / 1000,
  })
  console.log(`Rate limit check passed for user: ${userId}, count: ${record.count}`)

  return false
}

export async function createOAuthState(params: {
  redirectUri: string
  codeChallenge: string
  codeChallengeMethod: string
  clientId: string
  orgId?: string
}): Promise<{ state: string; error?: string }> {
  console.log("=== Creating OAuth State (JWT) ===")
  console.log("Params:", {
    redirectUri: params.redirectUri,
    codeChallenge: params.codeChallenge.substring(0, 10) + "...",
    codeChallengeMethod: params.codeChallengeMethod,
    clientId: params.clientId,
    orgId: params.orgId,
  })

  try {
    const userId = await getUserId()
    if (!userId) {
      console.error("No user ID found - unauthorized")
      return { state: "", error: "Unauthorized" }
    }
    if (params.orgId) {
      const isMember = await organizationMemberRepository.isMember(params.orgId, userId)
      if (!isMember) {
        return { state: "", error: "Unauthorized" }
      }
    }

    console.log("User ID:", userId)

    const limited = await isRateLimited(userId)
    if (limited) {
      console.error("Rate limit exceeded for user:", userId)
      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"),
    })

    console.log("OAuth state JWT created successfully")

    return { state }
  } catch (error) {
    console.error("Error creating OAuth state:", error)
    return { state: "", error: "Internal server error" }
  }
}

export async function authorizeOAuth(state: string): Promise<{
  code?: string
  redirectUri?: string
  error?: string
}> {
  console.log("=== Authorizing OAuth (JWT) ===")

  try {
    const userId = await getUserId()
    if (!userId) {
      console.error("No user ID found - unauthorized")
      return { error: "Unauthorized" }
    }

    console.log("User ID:", userId)

    let oauthState: OAuthStatePayload
    try {
      oauthState = jwt.verify(state, JWT_SECRET) as OAuthStatePayload
    } catch (error) {
      console.error("JWT verification failed:", error instanceof Error ? error.message : error)
      return { error: "Invalid or expired state" }
    }

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

    console.log("OAuth state JWT verified successfully")

    if (oauthState.userId !== userId) {
      console.error("User mismatch:", { expected: oauthState.userId, actual: 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"),
    })

    console.log("Authorization code JWT created successfully")

    return {
      code,
      redirectUri: oauthState.redirectUri,
    }
  } catch (error) {
    console.error("Error authorizing OAuth:", error)
    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 }> {
  console.log("=== Exchanging Code for Token (JWT) ===")
  console.log("Params:", {
    codeVerifier: params.codeVerifier.substring(0, 10) + "...",
    redirectUri: params.redirectUri,
    clientId: params.clientId,
  })

  try {
    let codeData: AuthCodePayload
    try {
      codeData = jwt.verify(params.code, JWT_SECRET) as AuthCodePayload
    } catch (error) {
      console.error("JWT verification failed:", error instanceof Error ? error.message : error)
      return { error: "Invalid or expired authorization code" }
    }

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

    console.log("✓ Authorization code JWT verified successfully!")
    console.log("Code data:", {
      userId: codeData.userId,
      redirectUri: codeData.redirectUri,
      clientId: codeData.clientId,
    })

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

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

    console.log("Computing code challenge...")
    const computedChallenge = crypto
      .createHash(codeData.codeChallengeMethod)
      .update(params.codeVerifier)
      .digest("base64url")

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

    console.log("✓ PKCE validation successful")
    console.log("Generating API token for userId:", codeData.userId, "orgId:", codeData.orgId)

    try {
      const apiKey = await generateTokenForVsCode(codeData.userId, codeData.orgId)

      console.log("API token generated successfully")
      console.log("=== Token Exchange Completed Successfully ===")

      return { accessToken: apiKey.token }
    } catch (tokenError: unknown) {
      if (tokenError instanceof Error && tokenError.message === "NEXT_REDIRECT") {
        console.error("Caught redirect error during token generation")
        return { error: "Authentication required" }
      }

      console.error("Error generating token:", tokenError)
      return { error: "Failed to generate API token" }
    }
  } catch (error) {
    console.error("=== Token Exchange Failed ===")
    console.error("Error:", error)
    return { error: "Internal server error" }
  }
}
