/**
 * Google OAuth Service
 *
 * Handles Google OAuth token management and API calls for:
 * - Token refresh
 * - Google Contacts API
 * - Google People API
 *
 * @module lib/services/google-oauth.service
 */

import { integrationsAdapter } from '@/lib/adapters/integrations';
import { logger } from '@/lib/logger';

// Types for Google Contacts API response
export interface GoogleContact {
  resourceName: string;
  etag: string;
  names?: Array<{
    displayName: string;
    familyName?: string;
    givenName?: string;
    middleName?: string;
  }>;
  emailAddresses?: Array<{
    value: string;
    type?: string;
  }>;
  phoneNumbers?: Array<{
    value: string;
    type?: string;
    canonicalForm?: string;
  }>;
  photos?: Array<{
    url: string;
  }>;
  organizations?: Array<{
    name?: string;
    title?: string;
  }>;
}

export interface GoogleContactsResponse {
  connections?: GoogleContact[];
  nextPageToken?: string;
  totalPeople?: number;
  totalItems?: number;
}

export interface SimplifiedContact {
  id: string;
  name: string;
  email?: string;
  phone?: string;
  photo?: string;
  company?: string;
  jobTitle?: string;
}

/**
 * Refresh Google OAuth access token using refresh token
 */
export async function refreshGoogleToken(userId: string): Promise<{
  accessToken: string;
  expiresAt: Date;
} | null> {
  try {
    // Get the stored integration
    const integration = await integrationsAdapter.findUnique({
      where: {
        userId_type: {
          userId,
          type: 'google_oauth',
        },
      },
    });

    if (!integration || !integration.refreshToken) {
      logger.warn('No Google OAuth integration or refresh token found', { userId });
      return null;
    }

    // Check if token is still valid (with 5 minute buffer)
    if (integration.tokenExpiry && new Date(integration.tokenExpiry) > new Date(Date.now() + 5 * 60 * 1000)) {
      if (integration.accessToken) {
        return {
          accessToken: integration.accessToken,
          expiresAt: integration.tokenExpiry,
        };
      }
    }

    // Refresh the token
    const response = await fetch('https://oauth2.googleapis.com/token', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/x-www-form-urlencoded',
      },
      body: new URLSearchParams({
        client_id: process.env.GOOGLE_CLIENT_ID || '',
        client_secret: process.env.GOOGLE_CLIENT_SECRET || '',
        refresh_token: integration.refreshToken,
        grant_type: 'refresh_token',
      }),
    });

    if (!response.ok) {
      const error = await response.text();
      logger.error('Failed to refresh Google token', { userId, error });

      // Update integration status to error
      await integrationsAdapter.update({
        where: { id: integration.id },
        data: {
          status: 'error',
          lastError: `Token refresh failed: ${error}`,
          updatedAt: new Date(),
        },
      });

      return null;
    }

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

    // Update the stored token
    await integrationsAdapter.update({
      where: { id: integration.id },
      data: {
        accessToken: tokenData.access_token,
        tokenExpiry: expiresAt,
        status: 'connected',
        lastError: null,
        updatedAt: new Date(),
      },
    });

    logger.info('Google token refreshed successfully', { userId });

    return {
      accessToken: tokenData.access_token,
      expiresAt,
    };
  } catch (error) {
    logger.error('Error refreshing Google token', { userId, error });
    return null;
  }
}

/**
 * Get valid Google access token for a user
 * Refreshes if expired
 */
export async function getGoogleAccessToken(userId: string): Promise<string | null> {
  const result = await refreshGoogleToken(userId);
  return result?.accessToken || null;
}

/**
 * Fetch Google Contacts for a user
 */
export async function fetchGoogleContacts(
  userId: string,
  options: {
    pageSize?: number;
    pageToken?: string;
    query?: string;
  } = {}
): Promise<{
  contacts: SimplifiedContact[];
  nextPageToken?: string;
  totalItems?: number;
} | null> {
  try {
    const accessToken = await getGoogleAccessToken(userId);

    if (!accessToken) {
      logger.warn('No valid Google access token available', { userId });
      return null;
    }

    const { pageSize = 100, pageToken, query } = options;

    // Build the API URL
    const params = new URLSearchParams({
      personFields: 'names,emailAddresses,phoneNumbers,photos,organizations',
      pageSize: pageSize.toString(),
    });

    if (pageToken) {
      params.append('pageToken', pageToken);
    }

    // Use search endpoint if query is provided
    const baseUrl = query
      ? 'https://people.googleapis.com/v1/people:searchContacts'
      : 'https://people.googleapis.com/v1/people/me/connections';

    if (query) {
      params.append('query', query);
      params.append('readMask', 'names,emailAddresses,phoneNumbers,photos,organizations');
    }

    const response = await fetch(`${baseUrl}?${params}`, {
      headers: {
        Authorization: `Bearer ${accessToken}`,
      },
    });

    if (!response.ok) {
      const error = await response.text();
      logger.error('Failed to fetch Google contacts', { userId, error, status: response.status });

      // If unauthorized, mark integration as needing reconnection
      if (response.status === 401) {
        await integrationsAdapter.update({
          where: {
            userId_type: {
              userId,
              type: 'google_oauth',
            },
          },
          data: {
            status: 'error',
            lastError: 'Authentication expired. Please reconnect your Google account.',
            updatedAt: new Date(),
          },
        });
      }

      return null;
    }

    const data: GoogleContactsResponse = await response.json();

    // Transform to simplified format
    const contacts: SimplifiedContact[] = (data.connections || []).map((contact) => ({
      id: contact.resourceName,
      name: contact.names?.[0]?.displayName || 'Unknown',
      email: contact.emailAddresses?.[0]?.value,
      phone: contact.phoneNumbers?.[0]?.value,
      photo: contact.photos?.[0]?.url,
      company: contact.organizations?.[0]?.name,
      jobTitle: contact.organizations?.[0]?.title,
    }));

    // Update last sync time
    await integrationsAdapter.update({
      where: {
        userId_type: {
          userId,
          type: 'google_oauth',
        },
      },
      data: {
        lastSyncAt: new Date(),
        updatedAt: new Date(),
      },
    });

    logger.info('Google contacts fetched successfully', {
      userId,
      count: contacts.length,
      hasMore: !!data.nextPageToken,
    });

    return {
      contacts,
      nextPageToken: data.nextPageToken,
      totalItems: data.totalPeople || data.totalItems,
    };
  } catch (error) {
    logger.error('Error fetching Google contacts', { userId, error });
    return null;
  }
}

/**
 * Fetch "Other Contacts" from Google (contacts without a label)
 * These are contacts saved from email interactions
 */
export async function fetchGoogleOtherContacts(
  userId: string,
  options: {
    pageSize?: number;
    pageToken?: string;
  } = {}
): Promise<{
  contacts: SimplifiedContact[];
  nextPageToken?: string;
} | null> {
  try {
    const accessToken = await getGoogleAccessToken(userId);

    if (!accessToken) {
      return null;
    }

    const { pageSize = 100, pageToken } = options;

    const params = new URLSearchParams({
      readMask: 'names,emailAddresses,phoneNumbers,photos',
      pageSize: pageSize.toString(),
    });

    if (pageToken) {
      params.append('pageToken', pageToken);
    }

    const response = await fetch(
      `https://people.googleapis.com/v1/otherContacts?${params}`,
      {
        headers: {
          Authorization: `Bearer ${accessToken}`,
        },
      }
    );

    if (!response.ok) {
      const error = await response.text();
      logger.error('Failed to fetch Google other contacts', { userId, error });
      return null;
    }

    const data = await response.json();

    const contacts: SimplifiedContact[] = (data.otherContacts || []).map((contact: GoogleContact) => ({
      id: contact.resourceName,
      name: contact.names?.[0]?.displayName || contact.emailAddresses?.[0]?.value || 'Unknown',
      email: contact.emailAddresses?.[0]?.value,
      phone: contact.phoneNumbers?.[0]?.value,
      photo: contact.photos?.[0]?.url,
    }));

    return {
      contacts,
      nextPageToken: data.nextPageToken,
    };
  } catch (error) {
    logger.error('Error fetching Google other contacts', { userId, error });
    return null;
  }
}

/**
 * Check if user has a valid Google OAuth connection
 */
export async function hasGoogleOAuthConnection(userId: string): Promise<{
  connected: boolean;
  needsReconnect: boolean;
  hasContactsScope: boolean;
}> {
  try {
    const integration = await integrationsAdapter.findUnique({
      where: {
        userId_type: {
          userId,
          type: 'google_oauth',
        },
      },
    });

    if (!integration) {
      return {
        connected: false,
        needsReconnect: false,
        hasContactsScope: false,
      };
    }

    const hasContactsScope = integration.scope?.includes('contacts') || false;
    const needsReconnect = integration.status === 'error' || !integration.refreshToken;

    return {
      connected: integration.status === 'connected',
      needsReconnect,
      hasContactsScope,
    };
  } catch (error) {
    logger.error('Error checking Google OAuth connection', { userId, error });
    return {
      connected: false,
      needsReconnect: false,
      hasContactsScope: false,
    };
  }
}

/**
 * Disconnect Google OAuth for a user
 */
export async function disconnectGoogleOAuth(userId: string): Promise<boolean> {
  try {
    await integrationsAdapter.delete({
      where: {
        userId_type: {
          userId,
          type: 'google_oauth',
        },
      },
    });

    logger.info('Google OAuth disconnected', { userId });
    return true;
  } catch (error) {
    logger.error('Error disconnecting Google OAuth', { userId, error });
    return false;
  }
}
