/**
 * GET /api/integrations/callback
 * OAuth callback handler for Google Calendar and Outlook Calendar integrations
 *
 * Processes the authorization code, exchanges it for tokens,
 * encrypts and stores them in the database
 */

import { NextRequest, NextResponse } from "next/server";
import { logger } from "@/lib/logger";
import prisma from "@/lib/db";
import { integrationsAdapter } from "@/lib/adapters/integrations";
import { encrypt } from "@/lib/encryption";

export const dynamic = "force-dynamic";

// Token exchange configurations
const TOKEN_CONFIG: Record<
  string,
  {
    tokenUrl: string;
    clientIdEnv: string;
    clientSecretEnv: string;
  }
> = {
  google_calendar: {
    tokenUrl: "https://oauth2.googleapis.com/token",
    clientIdEnv: "GOOGLE_CLIENT_ID",
    clientSecretEnv: "GOOGLE_CLIENT_SECRET",
  },
  outlook_calendar: {
    tokenUrl: "https://login.microsoftonline.com/common/oauth2/v2.0/token",
    clientIdEnv: "MICROSOFT_CLIENT_ID",
    clientSecretEnv: "MICROSOFT_CLIENT_SECRET",
  },
};

interface OAuthState {
  userId: string;
  type: string;
}

interface TokenResponse {
  access_token: string;
  refresh_token?: string;
  expires_in: number;
  token_type: string;
  scope?: string;
}

export async function GET(request: NextRequest) {
  const searchParams = request.nextUrl.searchParams;
  const code = searchParams.get("code");
  const state = searchParams.get("state");
  const error = searchParams.get("error");
  const errorDescription = searchParams.get("error_description");

  // Validate NEXTAUTH_URL is configured
  const baseUrl = process.env.NEXTAUTH_URL;
  if (!baseUrl) {
    logger.error("NEXTAUTH_URL not configured");
    return NextResponse.json(
      { error: "Server configuration error" },
      { status: 500 },
    );
  }

  // Default to English if no language can be determined
  let lang = "en";

  try {
    // Handle user denial or OAuth errors
    if (error) {
      const errorMsg = errorDescription || error;
      logger.warn("OAuth callback error", { error, errorDescription });

      return NextResponse.redirect(
        `${baseUrl}/${lang}/dashboard?tab=integrations&integration=error&error=${encodeURIComponent(errorMsg)}`,
      );
    }

    // Validate required parameters
    if (!code || !state) {
      logger.warn("OAuth callback missing required parameters", {
        hasCode: !!code,
        hasState: !!state,
      });

      return NextResponse.redirect(
        `${baseUrl}/${lang}/dashboard?tab=integrations&integration=error&error=missing_parameters`,
      );
    }

    // Decode and validate state parameter
    let stateData: OAuthState;
    try {
      const decodedState = Buffer.from(state, "base64").toString("utf8");
      stateData = JSON.parse(decodedState);

      if (!stateData.userId || !stateData.type) {
        throw new Error("Invalid state structure");
      }
    } catch (err) {
      logger.error("Invalid state parameter", { state, error: err });

      return NextResponse.redirect(
        `${baseUrl}/${lang}/dashboard?tab=integrations&integration=error&error=invalid_state`,
      );
    }

    const { userId, type } = stateData;

    // Verify user exists
    const user = await prisma.users.findUnique({
      where: { id: userId },
      select: { id: true, preferredLanguage: true },
    });

    if (!user) {
      logger.error("User not found in OAuth callback", { userId });

      return NextResponse.redirect(
        `${baseUrl}/${lang}/dashboard?tab=integrations&integration=error&error=user_not_found`,
      );
    }

    // Use user's preferred language
    lang = user.preferredLanguage || "en";

    // Validate integration type
    const config = TOKEN_CONFIG[type];
    if (!config) {
      logger.error("Invalid integration type in callback", { type });

      return NextResponse.redirect(
        `${baseUrl}/${lang}/dashboard?tab=integrations&integration=error&error=invalid_type`,
      );
    }

    const clientId = process.env[config.clientIdEnv];
    const clientSecret = process.env[config.clientSecretEnv];

    if (!clientId || !clientSecret) {
      logger.error("OAuth credentials not configured", { type });

      return NextResponse.redirect(
        `${baseUrl}/${lang}/dashboard?tab=integrations&integration=error&error=not_configured`,
      );
    }

    // Exchange authorization code for tokens
    const redirectUri = `${baseUrl}/api/integrations/callback`;

    logger.info("Exchanging OAuth code for tokens", { userId, type });

    const tokenResponse = await fetch(config.tokenUrl, {
      method: "POST",
      headers: {
        "Content-Type": "application/x-www-form-urlencoded",
      },
      body: new URLSearchParams({
        code,
        client_id: clientId,
        client_secret: clientSecret,
        redirect_uri: redirectUri,
        grant_type: "authorization_code",
      }),
    });

    if (!tokenResponse.ok) {
      const errorText = await tokenResponse.text();
      logger.error("Token exchange failed", {
        userId,
        type,
        status: tokenResponse.status,
        error: errorText,
      });

      return NextResponse.redirect(
        `${baseUrl}/${lang}/dashboard?tab=integrations&integration=error&error=token_exchange_failed`,
      );
    }

    const tokenData: TokenResponse = await tokenResponse.json();

    // Validate token response
    if (!tokenData.access_token) {
      logger.error("No access token in response", { userId, type });

      return NextResponse.redirect(
        `${baseUrl}/${lang}/dashboard?tab=integrations&integration=error&error=no_access_token`,
      );
    }

    // Calculate token expiry
    const expiresIn = tokenData.expires_in || 3600; // Default to 1 hour
    const tokenExpiry = new Date(Date.now() + expiresIn * 1000);

    // Encrypt tokens before storing
    const encryptedAccessToken = encrypt(tokenData.access_token);
    const encryptedRefreshToken = tokenData.refresh_token
      ? encrypt(tokenData.refresh_token)
      : null;

    // Store tokens in database
    await prisma.integrations.upsert({
      where: {
        userId_type: {
          userId,
          type,
        },
      },
      create: {
        userId,
        type,
        status: "connected",
        accessToken: encryptedAccessToken,
        refreshToken: encryptedRefreshToken,
        tokenExpiry,
        scope: tokenData.scope || null,
        config: {
          connectedAt: new Date().toISOString(),
          tokenType: tokenData.token_type,
        },
        lastSyncAt: new Date(),
        updatedAt: new Date(),
      },
      update: {
        status: "connected",
        accessToken: encryptedAccessToken,
        refreshToken: encryptedRefreshToken,
        tokenExpiry,
        scope: tokenData.scope || null,
        config: {
          connectedAt: new Date().toISOString(),
          tokenType: tokenData.token_type,
        },
        lastError: null,
        lastSyncAt: new Date(),
        updatedAt: new Date(),
      },
    });

    logger.info("Integration connected successfully", {
      userId,
      type,
      hasRefreshToken: !!tokenData.refresh_token,
      expiresAt: tokenExpiry.toISOString(),
    });

    // Redirect to dashboard with success message
    return NextResponse.redirect(
      `${baseUrl}/${lang}/dashboard?tab=integrations&integration=success&type=${type}`,
    );
  } catch (error) {
    logger.error("OAuth callback error", {
      error: error instanceof Error ? error.message : "Unknown error",
      stack: error instanceof Error ? error.stack : undefined,
    });

    return NextResponse.redirect(
      `${baseUrl}/${lang}/dashboard?tab=integrations&integration=error&error=unexpected_error`,
    );
  }
}
