// Google OAuth + Drive API v3 çağrıları — bilerek googleapis SDK'sı yerine
// düz fetch kullanılıyor (bu birkaç REST çağrısı için ağır bir bağımlılık
// gerekmiyor, projenin geri kalanındaki aiClient.ts ile aynı yaklaşım).
const GOOGLE_AUTH_URL = 'https://accounts.google.com/o/oauth2/v2/auth';
const GOOGLE_TOKEN_URL = 'https://oauth2.googleapis.com/token';
const GOOGLE_USERINFO_URL = 'https://www.googleapis.com/oauth2/v2/userinfo';
const GOOGLE_REVOKE_URL = 'https://oauth2.googleapis.com/revoke';
const DRIVE_API_URL = 'https://www.googleapis.com/drive/v3';

// PRD §7: mümkün olan en düşük scope. drive.file, uygulamanın SADECE kendi
// oluşturduğu/açtığı dosyalara erişmesini sağlar — kullanıcının tüm Drive'ına
// erişim istemiyoruz.
const SCOPE = [
  'https://www.googleapis.com/auth/drive.file',
  'https://www.googleapis.com/auth/userinfo.email',
].join(' ');

export interface TokenResponse {
  access_token: string;
  refresh_token?: string;
  expires_in: number;
  scope: string;
  token_type: string;
}

function getOAuthConfig() {
  const clientId = process.env.GOOGLE_CLIENT_ID;
  const clientSecret = process.env.GOOGLE_CLIENT_SECRET;
  const redirectUri = process.env.GOOGLE_REDIRECT_URI;
  if (!clientId || !clientSecret || !redirectUri) {
    throw new Error('GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET veya GOOGLE_REDIRECT_URI tanımlı değil.');
  }
  return { clientId, clientSecret, redirectUri };
}

export function buildAuthUrl(state: string): string {
  const { clientId, redirectUri } = getOAuthConfig();
  const params = new URLSearchParams({
    client_id: clientId,
    redirect_uri: redirectUri,
    response_type: 'code',
    access_type: 'offline', // refresh_token almak için gerekli
    prompt: 'consent', // refresh_token'ın HER bağlantıda dönmesini garantiler
    scope: SCOPE,
    state,
  });
  return `${GOOGLE_AUTH_URL}?${params.toString()}`;
}

export async function exchangeCodeForTokens(code: string): Promise<TokenResponse> {
  const { clientId, clientSecret, redirectUri } = getOAuthConfig();
  const res = await fetch(GOOGLE_TOKEN_URL, {
    method: 'POST',
    headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
    body: new URLSearchParams({
      code,
      client_id: clientId,
      client_secret: clientSecret,
      redirect_uri: redirectUri,
      grant_type: 'authorization_code',
    }),
  });
  const data = await res.json();
  if (!res.ok) {
    throw new Error(data.error_description || data.error || 'Google token exchange başarısız.');
  }
  return data;
}

export async function refreshAccessToken(refreshToken: string): Promise<{ access_token: string; expires_in: number; scope: string }> {
  const { clientId, clientSecret } = getOAuthConfig();
  const res = await fetch(GOOGLE_TOKEN_URL, {
    method: 'POST',
    headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
    body: new URLSearchParams({
      refresh_token: refreshToken,
      client_id: clientId,
      client_secret: clientSecret,
      grant_type: 'refresh_token',
    }),
  });
  const data = await res.json();
  if (!res.ok) {
    const err: any = new Error(data.error_description || data.error || 'Google token yenileme başarısız.');
    err.code = data.error; // 'invalid_grant' → refresh token artık geçersiz
    throw err;
  }
  return data;
}

// En iyi çaba — Google tarafında revoke başarısız olsa bile disconnect akışı
// yerel kaydı yine de siler (bkz. googleDrive.controller.ts).
export async function revokeToken(token: string): Promise<void> {
  try {
    await fetch(`${GOOGLE_REVOKE_URL}?token=${encodeURIComponent(token)}`, { method: 'POST' });
  } catch (e: any) {
    console.error('[DRIVE] Token revoke isteği başarısız:', e.message);
  }
}

export async function getUserEmail(accessToken: string): Promise<string> {
  const res = await fetch(GOOGLE_USERINFO_URL, { headers: { Authorization: `Bearer ${accessToken}` } });
  const data = await res.json();
  if (!res.ok || !data.email) {
    throw new Error('Google kullanıcı bilgisi alınamadı.');
  }
  return data.email;
}

// drive.file scope'u sadece uygulamanın oluşturduğu/açtığı dosyaları görmesine
// izin verir — bu yüzden klasörü her zaman biz oluşturuyoruz/arıyoruz, kullanıcının
// genel Drive'ında serbest arama yapamayız (zaten istemiyoruz, bkz. PRD §7).
export async function findOrCreateFolder(accessToken: string, name: string, parentId?: string): Promise<string> {
  const conditions = [`name='${name.replace(/'/g, "\\'")}'`, "mimeType='application/vnd.google-apps.folder'", 'trashed=false'];
  if (parentId) conditions.push(`'${parentId}' in parents`);

  const listRes = await fetch(`${DRIVE_API_URL}/files?q=${encodeURIComponent(conditions.join(' and '))}&fields=files(id,name)`, {
    headers: { Authorization: `Bearer ${accessToken}` },
  });
  const listData = await listRes.json();
  if (listRes.ok && Array.isArray(listData.files) && listData.files.length > 0) {
    return listData.files[0].id;
  }

  const createRes = await fetch(`${DRIVE_API_URL}/files?fields=id`, {
    method: 'POST',
    headers: { Authorization: `Bearer ${accessToken}`, 'Content-Type': 'application/json' },
    body: JSON.stringify({
      name,
      mimeType: 'application/vnd.google-apps.folder',
      parents: parentId ? [parentId] : undefined,
    }),
  });
  const createData = await createRes.json();
  if (!createRes.ok) {
    throw new Error(`Google Drive klasörü oluşturulamadı: ${name}`);
  }
  return createData.id;
}

// İlk bağlantıda AyrisLegal/Davalar klasör yapısını kurar (PRD §8). Sonraki
// bağlantılarda (aynı kullanıcı tekrar bağlanırsa) mevcut klasörler bulunup
// tekrar kullanılır, kopya oluşturulmaz.
export async function ensureAyrisLegalFolders(accessToken: string): Promise<{ rootFolderId: string; casesFolderId: string }> {
  const rootFolderId = await findOrCreateFolder(accessToken, 'AyrisLegal');
  const casesFolderId = await findOrCreateFolder(accessToken, 'Davalar', rootFolderId);
  return { rootFolderId, casesFolderId };
}

// PRD §9: local dosya yapısı (dava klasörü → belgeler) birebir Drive'a
// yansıtılıyor — dava başlığı = klasör adı. drive.file scope zaten sadece
// uygulamanın oluşturduğu klasörleri görebildiği için burada da arama/oluşturma
// findOrCreateFolder ile aynı mantıkla çalışıyor.
export async function findOrCreateCaseFolder(accessToken: string, casesFolderId: string, caseTitle: string): Promise<string> {
  return findOrCreateFolder(accessToken, caseTitle, casesFolderId);
}

interface UploadParams {
  name: string;
  parentId: string;
  mimeType: string;
  buffer: Buffer;
  // Verilirse içerik bu mevcut Drive dosyasının YENİ SÜRÜMÜ olarak yüklenir
  // (Google Drive eski sürümü otomatik revizyon geçmişinde tutar) — yeni bir
  // kopya dosya OLUŞTURULMAZ. PRD Test 4 "Modified File" tam olarak bu.
  existingFileId?: string;
}

// PRD §16: Google Drive'ın resumable upload protokolü kullanılıyor (electron-updater
// gibi burada da sıfırdan bir upload mekanizması yazmak yerine Google'ın kendi
// resumable session'ı kullanılıyor). Dosya baytları backend'e tek seferde (multer,
// bellekte buffer olarak) geldiği için PUT tek parça gönderiliyor — asıl fayda
// backend↔Google bacağının bu sayede Google'ın kendi bütünlük/aktarım garantisini
// kullanması. Not: Electron↔backend bacağı için bayt-bazlı devam etme (resume)
// henüz yok, bkz. PR açıklaması — kuyruk seviyesinde tüm dosya yeniden denenir.
export async function uploadFileResumable(accessToken: string, params: UploadParams): Promise<{ id: string; modifiedTime?: string }> {
  const isUpdate = !!params.existingFileId;
  const sessionUrl = isUpdate
    ? `https://www.googleapis.com/upload/drive/v3/files/${params.existingFileId}?uploadType=resumable`
    : `https://www.googleapis.com/upload/drive/v3/files?uploadType=resumable`;
  const metadata = isUpdate ? {} : { name: params.name, parents: [params.parentId] };

  const sessionRes = await fetch(sessionUrl, {
    method: isUpdate ? 'PATCH' : 'POST',
    headers: {
      Authorization: `Bearer ${accessToken}`,
      'Content-Type': 'application/json; charset=UTF-8',
      'X-Upload-Content-Type': params.mimeType,
      'X-Upload-Content-Length': String(params.buffer.length),
    },
    body: JSON.stringify(metadata),
  });
  if (!sessionRes.ok) {
    const err = await sessionRes.json().catch(() => ({}));
    throw new Error(err.error?.message || 'Google Drive upload oturumu başlatılamadı.');
  }
  const sessionUri = sessionRes.headers.get('location');
  if (!sessionUri) {
    throw new Error('Google Drive upload oturum adresi alınamadı.');
  }

  const uploadRes = await fetch(sessionUri, {
    method: 'PUT',
    headers: {
      'Content-Type': params.mimeType,
      'Content-Length': String(params.buffer.length),
    },
    // Node'un fetch implementasyonu (undici) Buffer'ı runtime'da sorunsuz kabul
    // ediyor — DOM tip tanımları BodyInit'e Buffer'ı dahil etmediği için sadece
    // tip seviyesinde cast gerekiyor.
    body: params.buffer as unknown as BodyInit,
  });
  const data = await uploadRes.json().catch(() => ({}));
  if (!uploadRes.ok) {
    throw new Error(data.error?.message || `Google Drive upload başarısız (HTTP ${uploadRes.status}).`);
  }
  return data;
}

// Faz 3 — Restore: bir dosyanın ham baytlarını Drive'dan indirir. Electron'un
// Google credential'ı hiç olmadığı için (upload'ta olduğu gibi) bu da backend
// üzerinden proxy'leniyor — çağıran taraf (googleDrive.controller.ts) fileId'nin
// gerçekten bu kullanıcıya ait olduğunu ÖNCE doğrulamalı.
export async function downloadFile(accessToken: string, fileId: string): Promise<Buffer> {
  const res = await fetch(`${DRIVE_API_URL}/files/${fileId}?alt=media`, {
    headers: { Authorization: `Bearer ${accessToken}` },
  });
  if (!res.ok) {
    throw new Error(`Google Drive dosyası indirilemedi (HTTP ${res.status}).`);
  }
  const arrayBuffer = await res.arrayBuffer();
  return Buffer.from(arrayBuffer);
}
