const { app, BrowserWindow, ipcMain, shell, dialog } = require('electron');
const path = require('path');
const fs = require('fs');
const http = require('http');
const crypto = require('crypto');
const https = require('https');

const userDataDir = path.join(app.getPath('appData'), 'RechnungsManager');
const dataFilePath = path.join(userDataDir, 'rechnungsmanager-data.json');

function ensureUserDataDir() {
  if (!fs.existsSync(userDataDir)) {
    fs.mkdirSync(userDataDir, { recursive: true });
  }
}

function createWindow() {
  const win = new BrowserWindow({
    width: 1400,
    height: 900,
    minWidth: 1000,
    minHeight: 650,
    title: 'RechnungsManager',
    backgroundColor: '#F5F5F7',
    webPreferences: {
      preload: path.join(__dirname, 'preload.js'),
      contextIsolation: true,
      nodeIntegration: false,
      sandbox: false
    }
  });

  win.loadFile(path.join(__dirname, 'index.html'));

  // Externe Links (Instagram/Spotify etc.) im System-Browser öffnen statt im App-Fenster
  win.webContents.setWindowOpenHandler(({ url }) => {
    shell.openExternal(url);
    return { action: 'deny' };
  });
}

app.whenReady().then(() => {
  ensureUserDataDir();
  createWindow();

  app.on('activate', () => {
    if (BrowserWindow.getAllWindows().length === 0) createWindow();
  });
});

app.on('window-all-closed', () => {
  if (process.platform !== 'darwin') app.quit();
});

// ---------- IPC: Persistenz ----------
ipcMain.handle('load-data', async () => {
  try {
    ensureUserDataDir();
    if (fs.existsSync(dataFilePath)) {
      return fs.readFileSync(dataFilePath, 'utf8');
    }
    return null;
  } catch (err) {
    console.error('load-data failed:', err);
    return null;
  }
});

ipcMain.on('save-data', (event, json) => {
  try {
    ensureUserDataDir();
    fs.writeFileSync(dataFilePath, json, 'utf8');
  } catch (err) {
    console.error('save-data failed:', err);
  }
});

// ---------- IPC: Externe Links ----------
ipcMain.on('open-external', (event, url) => {
  if (typeof url === 'string' && /^https?:\/\//i.test(url)) {
    shell.openExternal(url);
  }
});

// ---------- IPC: Backup-Export (JSON) ----------
ipcMain.handle('export-backup', async (event, json, suggestedName) => {
  const win = BrowserWindow.fromWebContents(event.sender);
  const result = await dialog.showSaveDialog(win, {
    title: 'Datensicherung speichern',
    defaultPath: suggestedName || 'RechnungsManager_Backup.json',
    filters: [{ name: 'JSON', extensions: ['json'] }]
  });
  if (result.canceled || !result.filePath) return { canceled: true };
  fs.writeFileSync(result.filePath, json, 'utf8');
  return { canceled: false, filePath: result.filePath };
});

// ---------- IPC: Beliebige Textdatei exportieren (z.B. DATEV CSV) ----------
ipcMain.handle('export-text-file', async (event, content, suggestedName, ext, description) => {
  const win = BrowserWindow.fromWebContents(event.sender);
  const extension = (ext || 'txt').replace(/^\./, '');
  const result = await dialog.showSaveDialog(win, {
    title: description ? ('Speichern: ' + description) : 'Datei speichern',
    defaultPath: suggestedName || ('export.' + extension),
    filters: [{ name: description || extension.toUpperCase(), extensions: [extension] }]
  });
  if (result.canceled || !result.filePath) return { canceled: true };
  fs.writeFileSync(result.filePath, content, 'utf8');
  return { canceled: false, filePath: result.filePath };
});

// ---------- IPC: Google OAuth (Desktop-Flow über System-Browser) ----------
// Nutzt PKCE + einen lokalen Loopback-Server auf 127.0.0.1, damit kein
// "autorisierter Web-Origin" bei Google hinterlegt werden muss. Das ist der
// offiziell von Google vorgesehene Weg für Desktop-/installierte Apps.

function base64url(buffer) {
  return buffer.toString('base64').replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
}

function postForm(urlStr, formData) {
  return new Promise((resolve, reject) => {
    const body = new URLSearchParams(formData).toString();
    const url = new URL(urlStr);
    const req = https.request({
      hostname: url.hostname,
      path: url.pathname + url.search,
      method: 'POST',
      headers: {
        'Content-Type': 'application/x-www-form-urlencoded',
        'Content-Length': Buffer.byteLength(body)
      }
    }, (res) => {
      let data = '';
      res.on('data', (chunk) => { data += chunk; });
      res.on('end', () => {
        try {
          const parsed = JSON.parse(data);
          if (res.statusCode >= 200 && res.statusCode < 300) resolve(parsed);
          else reject(new Error(parsed.error_description || parsed.error || ('HTTP ' + res.statusCode)));
        } catch (e) {
          reject(new Error('Ungültige Antwort von Google: ' + data));
        }
      });
    });
    req.on('error', reject);
    req.write(body);
    req.end();
  });
}

ipcMain.handle('google-oauth-authorize', async (event, { clientId, clientSecret, scope }) => {
  if (!clientId) throw new Error('Keine Google-Client-ID hinterlegt.');

  const codeVerifier = base64url(crypto.randomBytes(32));
  const codeChallenge = base64url(crypto.createHash('sha256').update(codeVerifier).digest());
  const oauthState = base64url(crypto.randomBytes(16));

  // Lokalen Loopback-Server auf einem freien Port starten, der den Redirect
  // von Google abfängt. Liefert { code, redirectUri } zurück.
  const { code, redirectUri } = await new Promise((resolve, reject) => {
    const server = http.createServer((req, res) => {
      const reqUrl = new URL(req.url, 'http://127.0.0.1');
      if (reqUrl.pathname !== '/callback') { res.writeHead(404); res.end(); return; }
      const returnedState = reqUrl.searchParams.get('state');
      const authCode = reqUrl.searchParams.get('code');
      const error = reqUrl.searchParams.get('error');
      res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
      if (error) {
        res.end('<html><body style="font-family:sans-serif;padding:40px;text-align:center;"><h2>Anmeldung abgebrochen</h2><p>Du kannst dieses Fenster jetzt schließen.</p></body></html>');
        server.close();
        reject(new Error('Google-Anmeldung abgebrochen: ' + error));
        return;
      }
      if (returnedState !== oauthState || !authCode) {
        res.end('<html><body style="font-family:sans-serif;padding:40px;text-align:center;"><h2>Ungültige Antwort</h2></body></html>');
        server.close();
        reject(new Error('Ungültige Antwort von Google (state/code fehlt).'));
        return;
      }
      res.end('<html><body style="font-family:sans-serif;padding:40px;text-align:center;"><h2>Erfolgreich verbunden ✓</h2><p>Du kannst dieses Fenster jetzt schließen und zu RechnungsManager zurückkehren.</p></body></html>');
      const usedRedirectUri = 'http://127.0.0.1:' + server.address().port + '/callback';
      server.close();
      resolve({ code: authCode, redirectUri: usedRedirectUri });
    });
    server.on('error', reject);
    server.listen(0, '127.0.0.1', () => {
      const port = server.address().port;
      const redirectUri = 'http://127.0.0.1:' + port + '/callback';
      const authUrl = new URL('https://accounts.google.com/o/oauth2/v2/auth');
      authUrl.searchParams.set('client_id', clientId);
      authUrl.searchParams.set('redirect_uri', redirectUri);
      authUrl.searchParams.set('response_type', 'code');
      authUrl.searchParams.set('scope', scope || 'https://www.googleapis.com/auth/calendar.events');
      authUrl.searchParams.set('access_type', 'offline');
      authUrl.searchParams.set('prompt', 'consent');
      authUrl.searchParams.set('code_challenge', codeChallenge);
      authUrl.searchParams.set('code_challenge_method', 'S256');
      authUrl.searchParams.set('state', oauthState);
      shell.openExternal(authUrl.toString());
    });
  });

  const tokenParams = {
    client_id: clientId,
    code,
    code_verifier: codeVerifier,
    grant_type: 'authorization_code',
    redirect_uri: redirectUri
  };
  if (clientSecret) tokenParams.client_secret = clientSecret;

  const tokens = await postForm('https://oauth2.googleapis.com/token', tokenParams);
  return tokens; // { access_token, refresh_token, expires_in, ... }
});

ipcMain.handle('google-oauth-refresh', async (event, { clientId, clientSecret, refreshToken }) => {
  if (!clientId || !refreshToken) throw new Error('Fehlende Angaben für Token-Erneuerung.');
  const params = {
    client_id: clientId,
    refresh_token: refreshToken,
    grant_type: 'refresh_token'
  };
  if (clientSecret) params.client_secret = clientSecret;
  const tokens = await postForm('https://oauth2.googleapis.com/token', params);
  return tokens; // { access_token, expires_in, ... }
});
