import express from "express";
import type { Request, Response } from "express";
import { getDb, getUserByOpenId, upsertUser } from "../db.ts";
import { sdk } from "./sdk.ts";
import { getSessionCookieOptions } from "./cookies.ts";
import { COOKIE_NAME } from "../../shared/const.ts";
import { eq } from "drizzle-orm";
import { analyzeUserForMultiAccountFraud } from "../services/multiAccountDetection.ts";
import { detectFraud } from "../services/fraudDetection.ts";
import { extractGoogleProfile, extractGithubProfile, extractAppleProfile } from "../services/socialProfileSync.ts";

const ONE_YEAR_MS = 365 * 24 * 60 * 60 * 1000;

export function setupOAuthRoutes(app: Express) {
  // Manus OAuth callback
  app.get("/api/oauth/callback", async (req: Request, res: Response) => {
    try {
      const { code, state } = req.query;

      if (!code || !state) {
        console.warn("[OAuth] Missing code or state parameter, redirecting to login");
        res.redirect(`${req.protocol}://${req.get('host')}/login?error=oauth_missing_params`);
        return;
      }

      // Parse state to get redirect URI
      const stateStr = Array.isArray(state) ? state[0] : state;
      const codeStr = Array.isArray(code) ? code[0] : code;
      
      let redirectUri = "/dashboard";
      
      // Try to decode state - it should be URL-safe base64 JSON
      if (stateStr) {
        try {
          // Decode URL-safe base64
          const urlSafeState = stateStr
            .replace(/-/g, "+")
            .replace(/_/g, "/");
          
          // Add padding if needed
          const padding = (4 - (urlSafeState.length % 4)) % 4;
          const paddedState = urlSafeState + "=".repeat(padding);
          
          const decoded = Buffer.from(paddedState, "base64").toString();
          const stateData = JSON.parse(decoded);
          
          if (stateData && stateData.origin && stateData.returnPath) {
            redirectUri = `${stateData.origin}${stateData.returnPath}`;
            console.log("[OAuth] State decoded successfully:", { origin: stateData.origin, returnPath: stateData.returnPath });
          }
        } catch (e) {
          console.warn("[OAuth] Failed to decode state as JSON:", e);
          // Fallback to default dashboard
          redirectUri = "/dashboard";
        }
      }

      // Build the correct redirect_uri for token exchange
      // This must match the redirectUri sent to the OAuth portal
      const callbackUrl = `${req.protocol}://${req.get('host')}/api/oauth/callback`;
      
      // Exchange code for token using Manus SDK
      let tokenResponse;
      try {
        // Pass code, state, and the actual callback URL for token exchange
        tokenResponse = await sdk.exchangeCodeForToken(codeStr, stateStr, callbackUrl);
      } catch (error) {
        console.error("[OAuth] Failed to exchange code:", error);
        res.status(401).json({ error: "OAuth callback failed", details: String(error) });
        return;
      }
      
      if (!tokenResponse || !tokenResponse.accessToken) {
        res.status(401).json({ error: "Invalid token response from OAuth provider" });
        return;
      }

      // Get user info using the access token
      let userInfo;
      try {
        userInfo = await sdk.getUserInfo(tokenResponse.accessToken);
      } catch (error) {
        console.error("[OAuth] Failed to get user info:", error);
        res.status(401).json({ error: "Failed to get user info from OAuth provider", details: String(error) });
        return;
      }
      
      if (!userInfo || !userInfo.openId) {
        res.status(401).json({ error: "Invalid user info from OAuth provider" });
        return;
      }

      // Find or create user
      let user = await getUserByOpenId(userInfo.openId);
      if (!user) {
        user = await upsertUser({
          openId: userInfo.openId,
          email: userInfo.email || "",
          name: userInfo.name || "Player",
          loginMethod: "manus",
          lastSignedIn: new Date(),
        });
      } else {
        // Update last signed in
        await upsertUser({
          openId: userInfo.openId,
          lastSignedIn: new Date(),
        });
        user = await getUserByOpenId(userInfo.openId);
      }

      // Initialize wallet if it doesn't exist
      if (user) {
        try {
          const database = await getDb();
          if (database) {
            const { wallets } = await import("../../drizzle/schema");
            // Check if wallet exists
            const existingWallet = await database.select().from(wallets).where(eq(wallets.userId, user.id)).limit(1);
            if (existingWallet.length === 0) {
              // Create wallet with starting balances
              await database.insert(wallets).values({
                userId: user.id,
                gcBalance: "5000.00",
                scBalance: "10.00",
              });
              console.log(`[OAuth] Wallet initialized for user ${user.id}`);
            }
          }
        } catch (walletError) {
          console.warn(`[OAuth] Failed to initialize wallet for user ${user.id}:`, walletError);
        }
      }

      if (!user) {
        res.status(500).json({ error: "Failed to create or retrieve user" });
        return;
      }

      // Create session token
      const sessionToken = await sdk.createSessionToken(userInfo.openId, {
        name: user.name || "",
        expiresInMs: ONE_YEAR_MS,
      });

      // Set cookie
      const cookieOptions = getSessionCookieOptions(req);
      res.cookie(COOKIE_NAME, sessionToken, { ...cookieOptions, maxAge: ONE_YEAR_MS });

      // Device fingerprinting for fraud detection (deferred implementation)
      // TODO: Implement device fingerprinting helpers when needed
      try {
        const ipAddress = req.ip || req.socket.remoteAddress || "unknown";
        const userAgent = req.get("user-agent") || "unknown";
        
        // Log device info for now - full fingerprinting to be implemented later
        console.log("[OAuth] Login from IP:", ipAddress, "User-Agent:", userAgent);
        
        // Fraud detection is optional - can be enabled when helpers are implemented
        // For now, just log successful login
        console.log("[OAuth] User", user.id, "authenticated successfully");
      } catch (fingerprintError) {
        console.error("[OAuth] Device logging failed:", fingerprintError);
        // Don't fail login if device logging fails
      }

      // Redirect to dashboard or stored redirect URI
      res.redirect(redirectUri);
    } catch (error) {
      const errorMessage = error instanceof Error ? error.message : String(error);
      console.error("[OAuth] Callback failed:", errorMessage);
      res.status(500).json({ error: "OAuth callback failed", details: errorMessage });
    }
  });

  // Google OAuth callback
  app.get("/api/oauth/google/callback", async (req: Request, res: Response) => {
    try {
      const { code, state } = req.query;

      if (!code || !state) {
        res.status(400).json({ error: "Missing code or state parameter" });
        return;
      }

      // Parse state to get redirect URI
      const stateStr = Array.isArray(state) ? state[0] : state;
      const codeStr = Array.isArray(code) ? code[0] : code;
      
      let redirectUri = "/dashboard";
      if (stateStr) {
        try {
          // Check if it looks like base64
          if (/^[A-Za-z0-9+/\-_]*={0,2}$/.test(stateStr)) {
            try {
              const decoded = Buffer.from(stateStr, "base64").toString();
              if (decoded && (decoded.startsWith("/") || decoded.startsWith("http"))) {
                redirectUri = decoded;
              }
            } catch (e) {
              console.warn("[Google OAuth] Failed to decode base64 state");
            }
          }
          // If not base64, treat as plain URL
          if (redirectUri === "/dashboard" && (stateStr.startsWith("/") || stateStr.startsWith("http"))) {
            redirectUri = stateStr;
          }
        } catch (e) {
          console.error("[Google OAuth] Failed to parse state:", e);
        }
      }

      // Exchange code for token
      const tokenEndpoint = "https://oauth2.googleapis.com/token";
      const tokenResponse = await fetch(tokenEndpoint, {
        method: "POST",
        headers: { "Content-Type": "application/x-www-form-urlencoded" },
        body: new URLSearchParams({
          code: codeStr,
          client_id: process.env.GOOGLE_OAUTH_CLIENT_ID || "",
          client_secret: process.env.GOOGLE_OAUTH_CLIENT_SECRET || "",
          redirect_uri: `${req.protocol}://${req.get('host')}/api/oauth/google/callback`,
          grant_type: "authorization_code",
        }).toString(),
      });

      if (!tokenResponse.ok) {
        throw new Error("Failed to exchange Google OAuth code");
      }

      const tokenData = await tokenResponse.json() as any;

      // Get user info from Google
      const userInfoResponse = await fetch("https://www.googleapis.com/oauth2/v2/userinfo", {
        headers: { Authorization: `Bearer ${tokenData.access_token}` },
      });

      if (!userInfoResponse.ok) {
        throw new Error("Failed to get Google user info");
      }

      const googleUser = await userInfoResponse.json() as any;

      // Extract and sync profile data from Google
      const googleProfile = await extractGoogleProfile(googleUser);

      // Find or create user
      const openId = `google_${googleUser.id}`;
      let user = await getUserByOpenId(openId);

      if (!user) {
        user = await upsertUser({
          openId,
          email: googleUser.email,
          name: googleProfile.displayName || googleUser.email?.split("@")[0] || "Player",
          avatarUrl: googleProfile.avatarUrl,
          socialProvider: "google",
          socialId: googleProfile.socialId,
          socialProfileData: JSON.stringify({
            provider: "google",
            displayName: googleProfile.displayName,
            email: googleProfile.email,
            lastSyncedAt: new Date().toISOString(),
          }),
          loginMethod: "google",
          lastSignedIn: new Date(),
        });
      } else {
        // Sync profile data on re-login
        await upsertUser({
          openId,
          name: googleProfile.displayName || user.name,
          avatarUrl: googleProfile.avatarUrl || user.avatarUrl,
          socialProvider: "google",
          socialId: googleProfile.socialId,
          socialProfileData: JSON.stringify({
            provider: "google",
            displayName: googleProfile.displayName,
            email: googleProfile.email,
            lastSyncedAt: new Date().toISOString(),
          }),
          lastSignedIn: new Date(),
        });
        user = await getUserByOpenId(openId);
      }

      // Create session token
      const sessionToken = await sdk.createSessionToken(openId, {
        name: user?.name || "",
        expiresInMs: ONE_YEAR_MS,
      });

      // Set cookie
      const cookieOptions = getSessionCookieOptions(req);
      res.cookie(COOKIE_NAME, sessionToken, { ...cookieOptions, maxAge: ONE_YEAR_MS });

      res.redirect(redirectUri);
    } catch (error) {
      const errorMessage = error instanceof Error ? error.message : String(error);
      console.error("[Google OAuth] Callback failed:", errorMessage);
      res.redirect(`/login?error=${encodeURIComponent("Google login failed")}`);
    }
  });

  // GitHub OAuth callback
  app.get("/api/oauth/github/callback", async (req: Request, res: Response) => {
    try {
      const { code, state } = req.query;

      if (!code || !state) {
        res.status(400).json({ error: "Missing code or state parameter" });
        return;
      }

      // Parse state to get redirect URI
      const stateStr = Array.isArray(state) ? state[0] : state;
      const codeStr = Array.isArray(code) ? code[0] : code;
      
      let redirectUri = "/dashboard";
      if (stateStr) {
        try {
          if (/^[A-Za-z0-9+/\-_]*={0,2}$/.test(stateStr)) {
            try {
              const decoded = Buffer.from(stateStr, "base64").toString();
              if (decoded && (decoded.startsWith("/") || decoded.startsWith("http"))) {
                redirectUri = decoded;
              }
            } catch (e) {
              console.warn("[GitHub OAuth] Failed to decode base64 state");
            }
          }
          if (redirectUri === "/dashboard" && (stateStr.startsWith("/") || stateStr.startsWith("http"))) {
            redirectUri = stateStr;
          }
        } catch (e) {
          console.error("[GitHub OAuth] Failed to parse state:", e);
        }
      }

      // Exchange code for token
      const tokenEndpoint = "https://github.com/login/oauth/access_token";
      const tokenResponse = await fetch(tokenEndpoint, {
        method: "POST",
        headers: {
          "Content-Type": "application/x-www-form-urlencoded",
          Accept: "application/json",
        },
        body: new URLSearchParams({
          code: codeStr,
          client_id: process.env.GITHUB_OAUTH_CLIENT_ID || "",
          client_secret: process.env.GITHUB_OAUTH_CLIENT_SECRET || "",
          redirect_uri: `${req.protocol}://${req.get('host')}/api/oauth/github/callback`,
        }).toString(),
      });

      if (!tokenResponse.ok) {
        throw new Error("Failed to exchange GitHub OAuth code");
      }

      const tokenData = await tokenResponse.json() as any;

      // Get user info from GitHub
      const userInfoResponse = await fetch("https://api.github.com/user", {
        headers: { Authorization: `Bearer ${tokenData.access_token}` },
      });

      if (!userInfoResponse.ok) {
        throw new Error("Failed to get GitHub user info");
      }

      const githubUser = await userInfoResponse.json() as any;

      // Extract and sync profile data from GitHub
      const githubProfile = await extractGitHubProfile(githubUser);

      // Find or create user
      const openId = `github_${githubUser.id}`;
      let user = await getUserByOpenId(openId);

      if (!user) {
        user = await upsertUser({
          openId,
          email: githubUser.email,
          name: githubProfile.displayName || githubUser.login || "Player",
          avatarUrl: githubProfile.avatarUrl,
          socialProvider: "github",
          socialId: githubProfile.socialId,
          socialProfileData: JSON.stringify({
            provider: "github",
            displayName: githubProfile.displayName,
            bio: githubProfile.bio,
            location: githubProfile.location,
            company: githubProfile.company,
            website: githubProfile.website,
            lastSyncedAt: new Date().toISOString(),
          }),
          loginMethod: "github",
          lastSignedIn: new Date(),
        });
      } else {
        // Sync profile data on re-login
        await upsertUser({
          openId,
          name: githubProfile.displayName || user.name,
          avatarUrl: githubProfile.avatarUrl || user.avatarUrl,
          socialProvider: "github",
          socialId: githubProfile.socialId,
          socialProfileData: JSON.stringify({
            provider: "github",
            displayName: githubProfile.displayName,
            bio: githubProfile.bio,
            location: githubProfile.location,
            company: githubProfile.company,
            website: githubProfile.website,
            lastSyncedAt: new Date().toISOString(),
          }),
          lastSignedIn: new Date(),
        });
        user = await getUserByOpenId(openId);
      }

      // Create session token
      const sessionToken = await sdk.createSessionToken(openId, {
        name: user?.name || "",
        expiresInMs: ONE_YEAR_MS,
      });

      // Set cookie
      const cookieOptions = getSessionCookieOptions(req);
      res.cookie(COOKIE_NAME, sessionToken, { ...cookieOptions, maxAge: ONE_YEAR_MS });

      res.redirect(redirectUri);
    } catch (error) {
      const errorMessage = error instanceof Error ? error.message : String(error);
      console.error("[GitHub OAuth] Callback failed:", errorMessage);
      res.redirect(`/login?error=${encodeURIComponent("GitHub login failed")}`);
    }
  });

  // Apple OAuth callback
  app.get("/api/oauth/apple/callback", async (req: Request, res: Response) => {
    try {
      const { code, state } = req.query;

      if (!code || !state) {
        res.status(400).json({ error: "Missing code or state parameter" });
        return;
      }

      // Parse state to get redirect URI
      const stateStr = Array.isArray(state) ? state[0] : state;
      const codeStr = Array.isArray(code) ? code[0] : code;
      
      let redirectUri = "/dashboard";
      if (stateStr) {
        try {
          if (/^[A-Za-z0-9+/\-_]*={0,2}$/.test(stateStr)) {
            try {
              const decoded = Buffer.from(stateStr, "base64").toString();
              if (decoded && (decoded.startsWith("/") || decoded.startsWith("http"))) {
                redirectUri = decoded;
              }
            } catch (e) {
              console.warn("[Apple OAuth] Failed to decode base64 state");
            }
          }
          if (redirectUri === "/dashboard" && (stateStr.startsWith("/") || stateStr.startsWith("http"))) {
            redirectUri = stateStr;
          }
        } catch (e) {
          console.error("[Apple OAuth] Failed to parse state:", e);
        }
      }

      // Exchange code for token
      const tokenEndpoint = "https://appleid.apple.com/auth/token";
      const tokenResponse = await fetch(tokenEndpoint, {
        method: "POST",
        headers: { "Content-Type": "application/x-www-form-urlencoded" },
        body: new URLSearchParams({
          code: codeStr,
          client_id: process.env.APPLE_OAUTH_CLIENT_ID || "",
          client_secret: process.env.APPLE_OAUTH_CLIENT_SECRET || "",
          redirect_uri: `${req.protocol}://${req.get('host')}/api/oauth/apple/callback`,
          grant_type: "authorization_code",
        }).toString(),
      });

      if (!tokenResponse.ok) {
        throw new Error("Failed to exchange Apple OAuth code");
      }

      const tokenData = await tokenResponse.json() as any;

      // Decode ID token to get user info
      const idToken = tokenData.id_token;
      const parts = idToken.split(".");
      const payload = JSON.parse(Buffer.from(parts[1], "base64").toString());

      const appleUser = {
        id: payload.sub,
        email: payload.email,
        name: "Apple User", // Apple doesn't provide name in ID token
      };

      // Extract and sync profile data from Apple
      const appleProfile = await extractAppleProfile(appleUser);

      // Find or create user
      const openId = `apple_${appleUser.id}`;
      let user = await getUserByOpenId(openId);

      if (!user) {
        user = await upsertUser({
          openId,
          email: appleUser.email,
          name: appleProfile.displayName || "Player",
          avatarUrl: appleProfile.avatarUrl,
          socialProvider: "apple",
          socialId: appleProfile.socialId,
          socialProfileData: JSON.stringify({
            provider: "apple",
            displayName: appleProfile.displayName,
            email: appleUser.email,
            lastSyncedAt: new Date().toISOString(),
          }),
          loginMethod: "apple",
          lastSignedIn: new Date(),
        });
      } else {
        // Sync profile data on re-login
        await upsertUser({
          openId,
          name: appleProfile.displayName || user.name,
          avatarUrl: appleProfile.avatarUrl || user.avatarUrl,
          socialProvider: "apple",
          socialId: appleProfile.socialId,
          socialProfileData: JSON.stringify({
            provider: "apple",
            displayName: appleProfile.displayName,
            email: appleUser.email,
            lastSyncedAt: new Date().toISOString(),
          }),
          lastSignedIn: new Date(),
        });
        user = await getUserByOpenId(openId);
      }

      // Create session token
      const sessionToken = await sdk.createSessionToken(openId, {
        name: user?.name || "",
        expiresInMs: ONE_YEAR_MS,
      });

      // Set cookie
      const cookieOptions = getSessionCookieOptions(req);
      res.cookie(COOKIE_NAME, sessionToken, { ...cookieOptions, maxAge: ONE_YEAR_MS });

      res.redirect(redirectUri);
    } catch (error) {
      const errorMessage = error instanceof Error ? error.message : String(error);
      console.error("[Apple OAuth] Callback failed:", errorMessage);
      res.redirect(`/login?error=${encodeURIComponent("Apple login failed")}`);
    }
  });

  // OAuth authorization endpoints
  app.get("/api/oauth/authorize/google", (req: Request, res: Response) => {
    const redirectUri = `${process.env.OAUTH_REDIRECT_BASE || "http://localhost:3000"}/api/oauth/google/callback`;
    const state = Buffer.from(req.query.redirectUri as string || "/dashboard").toString("base64");
    
    const authUrl = new URL("https://accounts.google.com/o/oauth2/v2/auth");
    authUrl.searchParams.set("client_id", process.env.GOOGLE_OAUTH_CLIENT_ID || "");
    authUrl.searchParams.set("redirect_uri", redirectUri);
    authUrl.searchParams.set("response_type", "code");
    authUrl.searchParams.set("scope", "openid email profile");
    authUrl.searchParams.set("state", state);
    
    res.redirect(authUrl.toString());
  });

  app.get("/api/oauth/authorize/github", (req: Request, res: Response) => {
    const redirectUri = `${process.env.OAUTH_REDIRECT_BASE || "http://localhost:3000"}/api/oauth/github/callback`;
    const state = Buffer.from(req.query.redirectUri as string || "/dashboard").toString("base64");
    
    const authUrl = new URL("https://github.com/login/oauth/authorize");
    authUrl.searchParams.set("client_id", process.env.GITHUB_OAUTH_CLIENT_ID || "");
    authUrl.searchParams.set("redirect_uri", redirectUri);
    authUrl.searchParams.set("scope", "user:email");
    authUrl.searchParams.set("state", state);
    
    res.redirect(authUrl.toString());
  });

  app.get("/api/oauth/authorize/apple", (req: Request, res: Response) => {
    const redirectUri = `${process.env.OAUTH_REDIRECT_BASE || "http://localhost:3000"}/api/oauth/apple/callback`;
    const state = Buffer.from(req.query.redirectUri as string || "/dashboard").toString("base64");
    
    const authUrl = new URL("https://appleid.apple.com/auth/authorize");
    authUrl.searchParams.set("client_id", process.env.APPLE_OAUTH_CLIENT_ID || "");
    authUrl.searchParams.set("redirect_uri", redirectUri);
    authUrl.searchParams.set("response_type", "code");
    authUrl.searchParams.set("response_mode", "form_post");
    authUrl.searchParams.set("scope", "openid email name");
    authUrl.searchParams.set("state", state);
    
    res.redirect(authUrl.toString());
  });
}
