import { google } from 'googleapis';
import { EmailQuery, EmailExtractionRule } from '../types';
import { EmailBodyParser } from '../utils/emailBodyParser';

export interface GmailMessage {
  id?: string;
  raw?: string;
  headers?: Array<{
    name: string;
    value: string;
  }>;
  labelIds?: string[];
  snippet?: string;
  body?: string;
  subject?: string;
  from?: string;
  to?: string;
}

interface GmailConfig {
  clientId: string;
  clientSecret: string;
  refreshToken: string;
  redirectUri?: string;
}

async function getOAuth2Client(config: GmailConfig) {
  if (!config.refreshToken) {
    throw new Error('GMAIL_REFRESH_TOKEN is required in your configuration.');
  }
  const oAuth2Client = new google.auth.OAuth2(
    config.clientId,
    config.clientSecret,
    config.redirectUri || 'http://localhost'
  );
  oAuth2Client.setCredentials({ refresh_token: config.refreshToken });
  return oAuth2Client;
}

async function getGmail(config: GmailConfig) {
  const auth = await getOAuth2Client(config);
  return google.gmail({ version: 'v1', auth });
}

async function listMessages(config: GmailConfig, query: string): Promise<GmailMessage[]> {
  const gmail = await getGmail(config);
  const res = await gmail.users.messages.list({
    userId: 'me',
    q: query,
  });
  return (res.data.messages || []).map(msg => ({
    id: msg.id || undefined,
    labelIds: msg.labelIds || undefined
  }));
}

async function getMessage(config: GmailConfig, msgId: string): Promise<GmailMessage> {
  const gmail = await getGmail(config);
  const res = await gmail.users.messages.get({
    userId: 'me',
    id: msgId,
    format: 'full',
  });
  
  const headers = res.data.payload?.headers?.map(h => ({
    name: h.name || '',
    value: h.value || ''
  })) || [];

  const subject = headers.find(h => h.name.toLowerCase() === 'subject')?.value;
  const from = headers.find(h => h.name.toLowerCase() === 'from')?.value;
  const to = headers.find(h => h.name.toLowerCase() === 'to')?.value;

  let body = '';
  if (res.data.payload?.body?.data) {
    body = Buffer.from(res.data.payload.body.data, 'base64').toString('utf8');
  } else if (res.data.payload?.parts) {
    const textPart = res.data.payload.parts.find(part => part.mimeType === 'text/plain');
    if (textPart?.body?.data) {
      body = Buffer.from(textPart.body.data, 'base64').toString('utf8');
    }
  }

  return {
    id: res.data.id || undefined,
    headers,
    labelIds: res.data.labelIds || undefined,
    snippet: res.data.snippet || undefined,
    body,
    subject,
    from,
    to
  };
}

export async function fromGmail(config: GmailConfig) {
  return {
    findEmail: async ({ subject, from, to, afterTime, beforeTime }: EmailQuery): Promise<GmailMessage | null> => {
      let query = 'label:inbox';
      if (subject) query += ` subject:\"${subject}\"`;
      if (from) query += ` from:${from}`;
      if (to) query += ` to:${to}`;
      if (afterTime) {
        const timestamp = Math.floor(new Date(afterTime).getTime() / 1000);
        query += ` after:${timestamp}`;
      }
      if (beforeTime) {
        const timestamp = Math.floor(new Date(beforeTime).getTime() / 1000);
        query += ` before:${timestamp}`;
      }

      const messages = await listMessages(config, query);
      if (!messages.length || !messages[0].id) return null;

      return getMessage(config, messages[0].id);
    },
    findEmailWithDetails: async (
      config: GmailConfig,
      query: EmailQuery,
      extractionRules: EmailExtractionRule[]
    ): Promise<{ email: GmailMessage | null; extractedDetails: Record<string, string | null> }> => {
      const email = await findEmail(config, query);
      
      if (!email) {
        return { email: null, extractedDetails: {} };
      }

      const extractedDetails: Record<string, string | null> = {};
      
      for (const rule of extractionRules) {
        extractedDetails[rule.field] = EmailBodyParser.extractInfo(email, {
          type: rule.type,
          startMarker: rule.startMarker,
          endMarker: rule.endMarker,
          regex: rule.regex,
        });
      }

      return { email, extractedDetails };
    }
  };
}

export async function findEmail(config: GmailConfig, { subject, from, to, afterTime, beforeTime }: EmailQuery): Promise<GmailMessage | null> {
  let query = 'label:inbox';
  if (subject) query += ` subject:\"${subject}\"`;
  if (from) query += ` from:${from}`;
  if (to) query += ` to:${to}`;
  if (afterTime) {
    const timestamp = Math.floor(new Date(afterTime).getTime() / 1000);
    query += ` after:${timestamp}`;
  }
  if (beforeTime) {
    const timestamp = Math.floor(new Date(beforeTime).getTime() / 1000);
    query += ` before:${timestamp}`;
  }

  const messages = await listMessages(config, query);
  if (!messages.length || !messages[0].id) return null;

  return getMessage(config, messages[0].id);
}

export async function findEmailWithDetails(
  config: GmailConfig,
  query: EmailQuery,
  extractionRules: EmailExtractionRule[]
): Promise<{ email: GmailMessage | null; extractedDetails: Record<string, string | null> }> {
  const email = await findEmail(config, query);
  
  if (!email) {
    return { email: null, extractedDetails: {} };
  }

  const extractedDetails: Record<string, string | null> = {};
  
  for (const rule of extractionRules) {
    extractedDetails[rule.field] = EmailBodyParser.extractInfo(email, {
      type: rule.type,
      startMarker: rule.startMarker,
      endMarker: rule.endMarker,
      regex: rule.regex,
    });
  }

  return { email, extractedDetails };
}