// JSON-RPC-Modus für die GUI (Tauri spricht Subprozess via stdin/stdout).
// Protokoll: newline-delimited JSON.
//   Request:  { id: <num>, method: <string>, params?: <object> }
//   Response: { id: <num>, result: <any> }   ODER   { id: <num>, error: <string> }
//   Push:     { event: <string>, payload: <any> }   (vom Server initiiert, ohne id)
//
// Methods (siehe handlers unten):
//   System:    ping, version, listSources (statisch)
//   Lifecycle: configExists, initConfig({rootDir,password})
//              unlock({password}), lock, isUnlocked
//   Accounts:  listAccounts, getAccount({id}), getSubfolders({id})
//              addAccount({account}), removeAccount({id}), renameAccount({oldId,newId})
//              setAccountOption({id,field,value}), setCredentials({id,credentials})
//   Globals:   getRootDir, setRootDir({rootDir})
//              getSmtp, setSmtp({smtp}), testSmtp({smtp?})
//              getLayout, setLayout({layout})
//              getMailSettings, setMailSettings({mail})
//              changeMasterPassword({newPassword})
//   Run:       runAll({filter?}), runAccount({id})
//              abortRun({id}), abortAll, listRunning
//              resetBrowserSession({id}), openAccountFolder({id})
//   Mail:      assessMonth({year,month}), sendMonthMail({year,month,note})
//   Backup:    exportConfig({path}), importConfig({path})

import { createInterface } from 'node:readline';
import { stdin, stdout } from 'node:process';
import { createSession, SUPPORTED_SOURCES, SOURCE_DEFAULT_FOLDER, configExists, newConfig, saveConfig } from './api.mjs';
import { addLogListener, setQuiet } from './logger.mjs';

const VERSION = '1.0.0';

function send(obj) {
  stdout.write(JSON.stringify(obj) + '\n');
}

function pushEvent(event, payload) {
  send({ event, payload });
}

export function startRpc() {
  setQuiet(true); // log.info etc. nicht mehr auf stdout — geht nur als event raus
  const session = createSession();
  session.onEvent((type, payload) => pushEvent(type, payload));
  // Log-Lines auch als Events streamen (fuer Live-Log in der GUI).
  // accountId kommt vom AsyncLocalStorage-Context im Logger.
  addLogListener((level, message, accountId) => pushEvent('log', { level, message, accountId: accountId ?? null }));

  const handlers = {
    ping: () => 'pong',
    version: () => VERSION,

    listSources: () => {
      // Default-Hidden: Quellen, die nur fuer den Owner sinnvoll sind (z.B. internetx-Reseller).
      // Endanwender-Builds zeigen sie nicht im "Konto hinzufuegen"-Modal.
      // Eigene Nutzung: DOCUDOWN_SHOW_ALL_SOURCES=1 als Umgebungsvariable setzen.
      // Bestehende Accounts dieser Source bleiben funktionsfaehig — Filter greift nur in der Liste.
      const showAll = process.env.DOCUDOWN_SHOW_ALL_SOURCES === '1';
      const hidden = new Set(
        (process.env.DOCUDOWN_HIDDEN_SOURCES || 'internetx')
          .split(',').map((s) => s.trim()).filter(Boolean),
      );
      return SUPPORTED_SOURCES
        .filter((key) => showAll || !hidden.has(key))
        .map((key) => ({ key, defaultFolder: SOURCE_DEFAULT_FOLDER[key] }));
    },

    configExists: () => configExists(),
    initConfig: ({ rootDir, password }) => {
      if (configExists()) throw new Error('Config existiert bereits.');
      const cfg = newConfig(rootDir);
      saveConfig(cfg, password);
      return true;
    },

    unlock: ({ password }) => { session.unlock(password); return true; },
    lock: () => { session.lock(); return true; },
    isUnlocked: () => session.isUnlocked(),

    listAccounts: () => session.listAccounts(),
    getAccount: ({ id }) => session.getAccount(id),
    getSubfolders: ({ id }) => session.getSubfolders(id),

    addAccount: ({ account }) => { session.addAccount(account); return true; },
    removeAccount: ({ id }) => { session.removeAccount(id); return true; },
    renameAccount: ({ oldId, newId }) => { session.renameAccount(oldId, newId); return true; },
    setAccountOption: ({ id, field, value }) => session.setAccountOption(id, field, value),
    setCredentials: ({ id, credentials }) => { session.setCredentials(id, credentials); return true; },

    getRootDir: () => session.getRootDir(),
    setRootDir: ({ rootDir }) => { session.setRootDir(rootDir); return true; },
    getSmtp: () => session.getSmtp(),
    setSmtp: ({ smtp }) => { session.setSmtp(smtp); return true; },
    testSmtp: async ({ smtp }) => await session.testSmtp(smtp),
    changeMasterPassword: ({ newPassword }) => { session.changeMasterPassword(newPassword); return true; },

    runAll: async ({ filter } = {}) => await session.runAll(filter),
    runAccount: async ({ id }) => await session.runAccount(id),
    // loginOnly + sendTaxMail bleiben als Session-Methoden fuer CLI-Nutzung,
    // sind aber kein RPC-Handler — die GUI nutzt runAccount + sendMonthMail.
    assessMonth: ({ year, month }) => session.assessMonth(year, month),
    sendMonthMail: async ({ year, month, note }) => await session.sendMonthMail(year, month, note),
    abortRun: ({ id }) => session.abortRun(id),
    abortAll: () => session.abortAll(),
    listRunning: () => session.listRunning(),

    // Settings (Layout, Mail-Settings)
    getLayout: () => session.getLayout(),
    setLayout: ({ layout }) => { session.setLayout(layout); return true; },
    getMailSettings: () => session.getMailSettings(),
    setMailSettings: ({ mail }) => { session.setMailSettings(mail); return true; },

    // Config Backup/Restore
    exportConfig: ({ path }) => session.exportConfig(path),
    importConfig: ({ path }) => session.importConfig(path),

    // Browser-Profil zuruecksetzen (Cookies/Session/Cache)
    resetBrowserSession: ({ id }) => session.resetBrowserSession(id),

    // Konto-Ordner im OS-Datei-Manager oeffnen
    openAccountFolder: ({ id }) => session.openAccountFolder(id),
  };

  const rl = createInterface({ input: stdin, terminal: false });
  rl.on('line', async (line) => {
    const raw = line.trim();
    if (!raw) return;
    let req;
    try { req = JSON.parse(raw); }
    catch (e) { send({ error: `parse error: ${e.message}` }); return; }
    const { id, method, params } = req;
    const fn = handlers[method];
    if (!fn) { send({ id, error: `unknown method: ${method}` }); return; }
    try {
      const result = await fn(params || {});
      send({ id, result });
    } catch (e) {
      send({ id, error: e.message || String(e) });
    }
  });
  rl.on('close', () => process.exit(0));

  pushEvent('ready', { version: VERSION });
}
