/**
 * GET /api/integrations
 * Returns all integration statuses for the authenticated user
 *
 * All data access goes through Convex. No Prisma fallback.
 *
 * Enhanced to validate token health and proactively refresh expiring tokens
 */

import { NextRequest, NextResponse } from "next/server";
import { auth } from "@/lib/auth";
import { getAuthenticatedEmail } from "@/lib/utils/auth-helpers.util";
import { logger } from "@/lib/logger";
import { convexQuery, convexMutation } from "@/lib/convex-client";
import { getErrorMessage } from "@/lib/errors";
import { decrypt, encrypt } from "@/lib/encryption";
import { fetchWithTimeout } from "@/lib/fetch-with-timeout";

// Calendar integration types that need token validation
const CALENDAR_TYPES = ["google_calendar", "outlook_calendar"];

// Token refresh buffer - refresh if expiring within 1 hour
const TOKEN_REFRESH_BUFFER_MS = 60 * 60 * 1000;

export const dynamic = "force-dynamic";

interface ConvexUser {
  _id: string;
  prismaId?: string;
  appUserId?: string;
  email: string;
  whatsappConnected?: boolean;
  whatsappNumber?: string;
}

interface ConvexIntegration {
  _id: string;
  id?: string;
  type: string;
  status: string;
  lastSyncAt?: string | null;
  lastError?: string | null;
  tokenExpiry?: string | null;
  accessToken?: string | null;
  refreshToken?: string | null;
  createdAt?: string;
  updatedAt?: string;
}

export async function GET(request: NextRequest) {
  try {
    const userEmail = await getAuthenticatedEmail(request, auth);

    if (!userEmail) {
      logger.warn("Unauthorized integrations request");
      return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
    }

    // Get user from Convex
    const convexUser = await convexQuery<ConvexUser>(
      "auth/queries:getUserByEmail",
      { email: userEmail },
    );

    if (!convexUser) {
      return NextResponse.json({ error: "User not found" }, { status: 404 });
    }

    const userId =
      convexUser.prismaId || convexUser.appUserId || convexUser._id;

    // Get effective owner ID via org state
    let effectiveUserId = userId;
    try {
      const orgState = await convexQuery<Record<string, unknown>>(
        "organizations/queries:getOrganizationStateByUserId",
        { userId },
      );
      if (orgState) {
        const userRole = orgState.userRole as string | undefined;
        const organization = orgState.organization as
          | Record<string, unknown>
          | undefined;
        if (organization && userRole && userRole !== "OWNER") {
          const ownerId = organization.ownerId as string | undefined;
          if (ownerId) effectiveUserId = ownerId;
        }
      }
    } catch {
      // Fall through with original userId
    }

    // Get all integrations from Convex
    const integrations = await convexQuery<ConvexIntegration[]>(
      "integrations/queries:getIntegrations",
      { userId: effectiveUserId },
    );

    // Transform to a map by type for easier frontend consumption
    type IntegrationEntry = {
      id: string;
      status: string;
      connected?: boolean;
      phoneNumber?: string;
      lastSyncAt: string | null;
      lastError: string | null;
      needsReconnect?: boolean;
      tokenStatus?: "valid" | "expiring_soon" | "expired" | "missing";
    };
    const integrationMap: Record<string, IntegrationEntry> = {};

    if (integrations && Array.isArray(integrations)) {
      // Collect calendar integrations that need token validation
      const calendarValidations = integrations
        .filter(
          (i) => CALENDAR_TYPES.includes(i.type) && i.status === "connected",
        )
        .map(async (integration) => {
          const tokenHealth = await validateAndRefreshToken(
            {
              id: integration._id || integration.id || "",
              type: integration.type,
              accessToken: integration.accessToken || null,
              refreshToken: integration.refreshToken || null,
              tokenExpiry: integration.tokenExpiry
                ? new Date(integration.tokenExpiry)
                : null,
            },
            effectiveUserId,
          );
          return { type: integration.type, tokenHealth };
        });

      // Validate all calendar tokens in parallel
      const tokenResults = await Promise.all(calendarValidations);
      const tokenMap = new Map(
        tokenResults.map((r) => [r.type, r.tokenHealth]),
      );

      // Build the integration map
      for (const integration of integrations) {
        let needsReconnect = false;
        let tokenStatus:
          | "valid"
          | "expiring_soon"
          | "expired"
          | "missing"
          | undefined;
        let status = integration.status;

        const tokenHealth = tokenMap.get(integration.type);
        if (tokenHealth) {
          tokenStatus = tokenHealth.status;
          needsReconnect = tokenHealth.needsReconnect;
          if (needsReconnect) {
            status = "error";
          }
        }

        integrationMap[integration.type] = {
          id: integration._id || integration.id || "",
          status,
          connected: status === "connected",
          lastSyncAt: integration.lastSyncAt || null,
          lastError: integration.lastError || null,
          ...(needsReconnect && { needsReconnect }),
          ...(tokenStatus && { tokenStatus }),
        };
      }
    }

    // WhatsApp status — check org info or user-level connection
    if (!integrationMap["whatsapp"]) {
      // Check org-level Twilio credentials
      try {
        const orgState = await convexQuery<Record<string, unknown>>(
          "organizations/queries:getOrganizationStateByUserId",
          { userId },
        );
        if (orgState) {
          const organization = orgState.organization as
            | Record<string, unknown>
            | undefined;
          if (organization) {
            const twilioAccountSid = organization.twilioAccountSid as
              | string
              | undefined;
            const twilioWhatsappNumber = organization.twilioWhatsappNumber as
              | string
              | undefined;
            const orgId =
              (organization._id as string) || (organization.id as string);
            if (twilioAccountSid && twilioWhatsappNumber) {
              integrationMap["whatsapp"] = {
                id: `whatsapp-${orgId}`,
                status: "connected",
                connected: true,
                phoneNumber: twilioWhatsappNumber || undefined,
                lastSyncAt: null,
                lastError: null,
              };
            }
          }
        }
      } catch {
        // Ignore org lookup failure
      }

      // Fallback: check user-level WhatsApp connection
      if (!integrationMap["whatsapp"] && convexUser.whatsappConnected) {
        integrationMap["whatsapp"] = {
          id: `whatsapp-n8n-${userId}`,
          status: "connected",
          connected: true,
          phoneNumber: convexUser.whatsappNumber || undefined,
          lastSyncAt: null,
          lastError: null,
        };
      }
    }

    logger.info("Integration status retrieved", {
      userEmail,
      count: integrations ? integrations.length : 0,
    });

    return NextResponse.json(integrationMap);
  } catch (error) {
    logger.error("Integrations error", {
      error: getErrorMessage(error),
    });

    return NextResponse.json(
      { error: "Failed to retrieve integration status" },
      { status: 500 },
    );
  }
}

/**
 * Validate token health and proactively refresh if expiring soon
 */
async function validateAndRefreshToken(
  integration: {
    id: string;
    type: string;
    accessToken: string | null;
    refreshToken: string | null;
    tokenExpiry: Date | null;
  },
  userId: string,
): Promise<{
  status: "valid" | "expiring_soon" | "expired" | "missing";
  needsReconnect: boolean;
}> {
  // Check if access token exists
  if (!integration.accessToken) {
    return { status: "missing", needsReconnect: true };
  }

  // Check if we can decrypt the token
  try {
    decrypt(integration.accessToken);
  } catch {
    logger.warn("Token decryption failed during health check", {
      integrationId: integration.id,
    });
    return { status: "missing", needsReconnect: true };
  }

  // Check token expiry
  const now = Date.now();
  const tokenExpiry = integration.tokenExpiry
    ? new Date(integration.tokenExpiry).getTime()
    : 0;

  if (tokenExpiry === 0) {
    logger.warn("Integration has no token expiry set", {
      integrationId: integration.id,
    });
    return { status: "valid", needsReconnect: false };
  }

  // Token is expired
  if (tokenExpiry < now) {
    const refreshResult = await tryProactiveRefresh(integration, userId);
    if (!refreshResult.success) {
      return { status: "expired", needsReconnect: true };
    }
    return { status: "valid", needsReconnect: false };
  }

  // Token is expiring soon (within buffer)
  if (tokenExpiry < now + TOKEN_REFRESH_BUFFER_MS) {
    const refreshResult = await tryProactiveRefresh(integration, userId);
    if (!refreshResult.success) {
      return { status: "expiring_soon", needsReconnect: false };
    }
    return { status: "valid", needsReconnect: false };
  }

  return { status: "valid", needsReconnect: false };
}

/**
 * Proactively refresh a token before it expires
 */
async function tryProactiveRefresh(
  integration: {
    id: string;
    type: string;
    refreshToken: string | null;
  },
  userId: string,
): Promise<{ success: boolean }> {
  if (!integration.refreshToken) {
    return { success: false };
  }

  try {
    let refreshToken: string;
    try {
      refreshToken = decrypt(integration.refreshToken);
    } catch {
      return { success: false };
    }

    const config = getTokenRefreshConfig(integration.type);
    if (!config) {
      return { success: false };
    }

    const response = await fetchWithTimeout(
      config.tokenUrl,
      {
        method: "POST",
        headers: {
          "Content-Type": "application/x-www-form-urlencoded",
        },
        body: new URLSearchParams({
          client_id: config.clientId,
          client_secret: config.clientSecret,
          refresh_token: refreshToken,
          grant_type: "refresh_token",
        }),
      },
      15000,
    );

    if (!response.ok) {
      logger.warn("Proactive token refresh failed", {
        integrationId: integration.id,
        status: response.status,
      });
      return { success: false };
    }

    const tokenData = await response.json();
    const expiresAt = new Date(
      Date.now() + (tokenData.expires_in || 3600) * 1000,
    );

    // Update tokens via Convex
    await convexMutation("integrations/mutations:updateIntegration", {
      id: integration.id,
      accessToken: encrypt(tokenData.access_token),
      tokenExpiry: expiresAt.toISOString(),
      status: "connected",
      lastError: null,
    });

    logger.info("Proactive token refresh successful", {
      integrationId: integration.id,
      userId,
    });
    return { success: true };
  } catch (error) {
    logger.error("Proactive token refresh error", {
      integrationId: integration.id,
      error,
    });
    return { success: false };
  }
}

/**
 * Get token refresh configuration for a provider
 */
function getTokenRefreshConfig(
  type: string,
): { tokenUrl: string; clientId: string; clientSecret: string } | null {
  switch (type) {
    case "google_calendar":
      if (!process.env.GOOGLE_CLIENT_ID || !process.env.GOOGLE_CLIENT_SECRET)
        return null;
      return {
        tokenUrl: "https://oauth2.googleapis.com/token",
        clientId: process.env.GOOGLE_CLIENT_ID,
        clientSecret: process.env.GOOGLE_CLIENT_SECRET,
      };
    case "outlook_calendar":
      if (
        !process.env.MICROSOFT_CLIENT_ID ||
        !process.env.MICROSOFT_CLIENT_SECRET
      )
        return null;
      return {
        tokenUrl: "https://login.microsoftonline.com/common/oauth2/v2.0/token",
        clientId: process.env.MICROSOFT_CLIENT_ID,
        clientSecret: process.env.MICROSOFT_CLIENT_SECRET,
      };
    default:
      return null;
  }
}
