import { Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { google } from 'googleapis';
import { User } from '../../entities/user.entity';
import { UsersService } from '../users/users.service';

@Injectable()
export class GoogleAuthService {
  private oauth2Client: InstanceType<typeof google.auth.OAuth2>;

  constructor(
    private config: ConfigService,
    private usersService: UsersService,
  ) {
    this.oauth2Client = new google.auth.OAuth2(
      config.get('GOOGLE_CLIENT_ID'),
      config.get('GOOGLE_CLIENT_SECRET'),
      config.get('GOOGLE_CALLBACK_URL'),
    );
  }

  getAuthUrl(): string {
    return this.oauth2Client.generateAuthUrl({
      access_type: 'offline',
      prompt: 'consent',
      scope: [
        'email',
        'profile',
        'https://www.googleapis.com/auth/drive.file',
      ],
    });
  }

  getOAuth2Client() {
    return this.oauth2Client;
  }

  async getValidAccessToken(user: User): Promise<string> {
    const nowSec = Math.floor(Date.now() / 1000);
    const bufferSec = 300;
    
    // Return existing token if still valid
    if (user.tokenExpiry && user.tokenExpiry > nowSec + bufferSec) {
      return user.accessToken!;
    }

    // Check if refresh token exists
    if (!user.refreshToken) {
      throw new Error('No refresh token available. User must re-authenticate.');
    }

    // Attempt to refresh the access token
    try {
      this.oauth2Client.setCredentials({
        refresh_token: user.refreshToken,
      });
      
      const { credentials } = await this.oauth2Client.refreshAccessToken();
      
      if (!credentials.access_token) {
        throw new Error('Failed to obtain new access token');
      }

      const expiry = credentials.expiry_date
        ? Math.floor(credentials.expiry_date / 1000)
        : nowSec + 3600;

      // Update tokens in database
      await this.usersService.updateTokens(
        user.id,
        credentials.access_token,
        credentials.refresh_token ?? null,
        expiry,
      );

      return credentials.access_token;
    } catch (error: any) {
      // Handle specific Google OAuth errors
      const errorMessage = error?.message || '';
      const errorCode = error?.code || error?.response?.data?.error;
      
      // Common refresh token errors
      if (
        errorCode === 'invalid_grant' ||
        errorMessage.includes('invalid_grant') ||
        errorMessage.includes('Token has been expired or revoked') ||
        errorMessage.includes('Invalid Credentials')
      ) {
        // Clear invalid tokens from database
        await this.usersService.updateTokens(user.id, null, null, null);
        throw new Error('Refresh token expired or revoked. User must re-authenticate.');
      }

      // Network or temporary errors
      throw new Error(`Token refresh failed: ${errorMessage}`);
    }
  }
}
