pragma Singleton

import QtQuick
import Quickshell
import Quickshell.Io
import qs.Commons
import qs.Services.UI

Singleton {
  id: root

  property var connections: ({})
  property bool refreshing: false
  property bool connecting: false
  property bool disconnecting: false
  property string connectingUuid: ""
  property string disconnectingUuid: ""
  property string lastError: ""
  property bool refreshPending: false

  readonly property var activeConnections: {
    const result = [];
    const map = connections;
    for (const key in map) {
      const conn = map[key];
      if (conn && conn.active) {
        result.push(conn);
      }
    }
    return result;
  }

  readonly property var inactiveConnections: {
    const result = [];
    const map = connections;
    for (const key in map) {
      const conn = map[key];
      if (conn && !conn.active) {
        result.push(conn);
      }
    }
    return result;
  }

  readonly property bool hasActiveConnection: activeConnections.length > 0

  Timer {
    id: refreshTimer
    interval: 5000
    running: true
    repeat: true
    onTriggered: refresh()
  }

  Timer {
    id: delayedRefreshTimer
    interval: 1000
    repeat: false
    onTriggered: refresh()
  }

  Component.onCompleted: {
    Logger.i("VPN", "Service started");
    refresh();
  }

  function refresh() {
    if (refreshing) {
      refreshPending = true;
      return;
    }
    refreshing = true;
    lastError = "";
    refreshProcess.running = true;
  }

  function connect(uuid) {
    if (connecting || !uuid) {
      return;
    }
    const conn = connections[uuid];
    if (!conn) {
      return;
    }
    connecting = true;
    connectingUuid = uuid;
    lastError = "";
    connectProcess.uuid = uuid;
    connectProcess.name = conn.name;
    connectProcess.running = true;
  }

  function disconnect(uuid) {
    if (disconnecting || !uuid) {
      return;
    }
    const conn = connections[uuid];
    if (!conn) {
      return;
    }
    disconnecting = true;
    disconnectingUuid = uuid;
    lastError = "";
    disconnectProcess.uuid = uuid;
    disconnectProcess.name = conn.name;
    disconnectProcess.running = true;
  }

  function toggle(uuid) {
    const conn = connections[uuid];
    if (!conn) {
      return;
    }
    if (conn.active) {
      disconnect(uuid);
    } else {
      connect(uuid);
    }
  }

  function setConnection(uuid, data) {
    if (!uuid) {
      return;
    }
    const map = Object.assign({}, connections);
    if (map[uuid]) {
      map[uuid] = Object.assign({}, map[uuid], data);
      connections = map;
    }
  }

  function scheduleRefresh(interval) {
    delayedRefreshTimer.interval = interval;
    delayedRefreshTimer.restart();
  }

  Process {
    id: refreshProcess
    running: false
    command: ["nmcli", "-t", "-f", "NAME,UUID,TYPE,DEVICE", "connection", "show"]

    stdout: StdioCollector {
      onStreamFinished: {
        const lines = text.split("\n");
        const map = {};
        for (let i = 0; i < lines.length; ++i) {
          const line = lines[i].trim();
          if (!line) {
            continue;
          }
          const lastColonIdx = line.lastIndexOf(":");
          if (lastColonIdx === -1) {
            continue;
          }
          const device = line.substring(lastColonIdx + 1);
          const remaining = line.substring(0, lastColonIdx);
          const secondLastColonIdx = remaining.lastIndexOf(":");
          if (secondLastColonIdx === -1) {
            continue;
          }
          const type = remaining.substring(secondLastColonIdx + 1);
          if (type !== "vpn" && type !== "wireguard") {
            continue;
          }
          const remaining2 = remaining.substring(0, secondLastColonIdx);
          const thirdLastColonIdx = remaining2.lastIndexOf(":");
          if (thirdLastColonIdx === -1) {
            continue;
          }
          const uuid = remaining2.substring(thirdLastColonIdx + 1);
          const name = remaining2.substring(0, thirdLastColonIdx);
          if (!uuid || !name) {
            continue;
          }
          const active = device && device !== "--";
          map[uuid] = {
            "uuid": uuid,
            "name": name,
            "device": device,
            "active": active
          };
        }
        connections = map;
        const pending = refreshPending;
        refreshing = false;
        refreshPending = false;
        if (pending) {
          scheduleRefresh(200);
        }
      }
    }

    stderr: StdioCollector {
      onStreamFinished: {
        const pending = refreshPending;
        refreshing = false;
        refreshPending = false;
        if (text.trim()) {
          lastError = text.split("\n")[0].trim();
          Logger.w("VPN", "Refresh error: " + text);
        }
        if (pending) {
          scheduleRefresh(2000);
        }
      }
    }
  }

  Process {
    id: connectProcess
    property string uuid: ""
    property string name: ""
    running: false
    command: ["nmcli", "connection", "up", "uuid", uuid]

    stdout: StdioCollector {
      onStreamFinished: {
        const output = text.trim();
        if (!output || (!output.includes("successfully activated") && !output.includes("Connection successfully"))) {
          return;
        }
        setConnection(connectProcess.uuid, {
                        "active": true
                      });
        connecting = false;
        connectingUuid = "";
        lastError = "";
        Logger.i("VPN", "Connected to " + connectProcess.name);
        ToastService.showNotice(connectProcess.name, I18n.tr("toast.vpn.connected", {
                                                               "name": connectProcess.name
                                                             }), "shield-lock");
        scheduleRefresh(1000);
      }
    }

    stderr: StdioCollector {
      onStreamFinished: {
        const trimmed = text.trim();
        if (trimmed) {
          lastError = trimmed.split("\n")[0].trim();
          Logger.w("VPN", "Connect error: " + trimmed);
          ToastService.showWarning(connectProcess.name, lastError);
        }
        connecting = false;
        connectingUuid = "";
      }
    }
  }

  Process {
    id: disconnectProcess
    property string uuid: ""
    property string name: ""
    running: false
    command: ["nmcli", "connection", "down", "uuid", uuid]

    stdout: StdioCollector {
      onStreamFinished: {
        Logger.i("VPN", "Disconnected from " + disconnectProcess.name);
        setConnection(disconnectProcess.uuid, {
                        "active": false,
                        "device": ""
                      });
        disconnecting = false;
        disconnectingUuid = "";
        lastError = "";
        ToastService.showNotice(disconnectProcess.name, I18n.tr("toast.vpn.disconnected", {
                                                                  "name": disconnectProcess.name
                                                                }), "shield-off");
        scheduleRefresh(1000);
      }
    }

    stderr: StdioCollector {
      onStreamFinished: {
        const trimmed = text.trim();
        if (trimmed) {
          lastError = trimmed.split("\n")[0].trim();
          Logger.w("VPN", "Disconnect error: " + trimmed);
          ToastService.showWarning(disconnectProcess.name, lastError);
        }
        disconnecting = false;
        disconnectingUuid = "";
      }
    }
  }
}
