import 'server-only';

import crypto from 'crypto';

type ServiceAccount = {
  client_email: string;
  private_key: string;
};

function base64Url(input: Buffer | string) {
  const buf = Buffer.isBuffer(input) ? input : Buffer.from(input, 'utf8');
  return buf
    .toString('base64')
    .replace(/\+/g, '-')
    .replace(/\//g, '_')
    .replace(/=+$/g, '');
}

function getServiceAccount(): ServiceAccount {
  const raw =
    process.env.GOOGLE_SERVICE_ACCOUNT_JSON ??
    (process.env.GOOGLE_SERVICE_ACCOUNT_JSON_BASE64
      ? Buffer.from(process.env.GOOGLE_SERVICE_ACCOUNT_JSON_BASE64, 'base64').toString('utf8')
      : undefined);

  if (!raw) {
    throw new Error('Missing GOOGLE_SERVICE_ACCOUNT_JSON or GOOGLE_SERVICE_ACCOUNT_JSON_BASE64');
  }

  const parsed = JSON.parse(raw) as Partial<ServiceAccount>;
  if (!parsed.client_email || !parsed.private_key) {
    throw new Error('Invalid service account JSON: missing client_email/private_key');
  }

  return {
    client_email: parsed.client_email,
    private_key: parsed.private_key.replace(/\\n/g, '\n'),
  };
}

type TokenCache = {
  accessToken: string;
  expiresAtMs: number;
};

function getTokenCache(): TokenCache | null {
  const key = '__dropex_google_token_cache__';
  const globalAny = globalThis as unknown as Record<string, unknown>;
  return (globalAny[key] as TokenCache | undefined) ?? null;
}

function setTokenCache(cache: TokenCache) {
  const key = '__dropex_google_token_cache__';
  const globalAny = globalThis as unknown as Record<string, unknown>;
  globalAny[key] = cache;
}

async function getAccessToken(): Promise<string> {
  const cached = getTokenCache();
  const now = Date.now();
  if (cached && cached.expiresAtMs - 60_000 > now) return cached.accessToken;

  const sa = getServiceAccount();
  const iat = Math.floor(now / 1000);
  const exp = iat + 3600;

  const header = base64Url(JSON.stringify({ alg: 'RS256', typ: 'JWT' }));
  const claimSet = base64Url(
    JSON.stringify({
      iss: sa.client_email,
      scope: 'https://www.googleapis.com/auth/spreadsheets',
      aud: 'https://oauth2.googleapis.com/token',
      iat,
      exp,
    })
  );

  const toSign = `${header}.${claimSet}`;
  const signer = crypto.createSign('RSA-SHA256');
  signer.update(toSign);
  signer.end();
  const signature = base64Url(signer.sign(sa.private_key));
  const assertion = `${toSign}.${signature}`;

  const res = await fetch('https://oauth2.googleapis.com/token', {
    method: 'POST',
    headers: { 'content-type': 'application/x-www-form-urlencoded' },
    body: new URLSearchParams({
      grant_type: 'urn:ietf:params:oauth:grant-type:jwt-bearer',
      assertion,
    }).toString(),
  });

  if (!res.ok) {
    throw new Error('Failed to obtain Google access token');
  }

  const data = (await res.json()) as { access_token?: string; expires_in?: number };
  if (!data.access_token) throw new Error('Google token response missing access_token');

  const expiresIn = typeof data.expires_in === 'number' ? data.expires_in : 3600;
  setTokenCache({ accessToken: data.access_token, expiresAtMs: now + expiresIn * 1000 });
  return data.access_token;
}

export async function appendLeadRow(values: string[]) {
  const spreadsheetId = process.env.GOOGLE_SHEETS_SPREADSHEET_ID;
  if (!spreadsheetId) throw new Error('Missing GOOGLE_SHEETS_SPREADSHEET_ID');

  const range = process.env.GOOGLE_SHEETS_RANGE ?? 'Leads!A:Z';
  const token = await getAccessToken();

  const url = new URL(
    `https://sheets.googleapis.com/v4/spreadsheets/${encodeURIComponent(
      spreadsheetId
    )}/values/${encodeURIComponent(range)}:append`
  );
  url.searchParams.set('valueInputOption', 'USER_ENTERED');
  url.searchParams.set('insertDataOption', 'INSERT_ROWS');

  const res = await fetch(url.toString(), {
    method: 'POST',
    headers: {
      authorization: `Bearer ${token}`,
      'content-type': 'application/json',
    },
    body: JSON.stringify({ values: [values] }),
  });

  if (!res.ok) {
    throw new Error('Failed to append row to Google Sheet');
  }
}

