import { invoke } from "@tauri-apps/api/core";

// ─── Types (mirror src-tauri Rust structs) ───────────────────────────────────

export interface DaemonStatus {
  running: boolean;
  apiUrl: string | null;
  keyPath: string | null;
  configPath: string | null;
  /** Full overlay IPv6 (e.g. `43d:956e:7877:d933:eecc:b305:21ff:77f9`). */
  overlayIp: string | null;
}

export interface NodeInfo {
  nodeSubnet: string;
  nodePubkey: string;
}

export interface SidecarConfig {
  peers: string[];
  tunName: string | null;
  noTun: boolean;
}

// ─── Type-safe invoke wrappers ───────────────────────────────────────────────

const cmd = <T>(name: string, args?: Record<string, unknown>) =>
  invoke<T>(name, args);

export interface PeerEndpoint {
  proto: string;
  socketAddr: string;
}

export type PeerKind = "static" | "inbound" | "linkLocalDiscovery" | string;
export type ConnectionState = "alive" | "connecting" | "dead" | string;

export interface PeerInfo {
  endpoint: PeerEndpoint;
  type: PeerKind;
  connectionState: ConnectionState;
  txBytes: number;
  rxBytes: number;
}

export interface AggregatedStats {
  peersTotal: number;
  peersAlive: number;
  peersConnecting: number;
  peersDead: number;
  txBytes: number;
  rxBytes: number;
}

// Routes
//
// `metric` is either a non-negative integer (number of hops) or the literal
// string "infinite" — preserved verbatim from the daemon JSON.
export type Metric = number | string;

export interface Route {
  subnet: string;
  nextHop: string;
  metric: Metric;
  seqno: number;
}

export interface QueriedSubnet {
  subnet: string;
  expiration: string;
}

export interface RoutesSnapshot {
  selected: Route[];
  fallback: Route[];
  queried: QueriedSubnet[];
}

// Messages & topics

export type MessageDestination = { ip: string } | { pk: string };

export interface PushMessageReceipt {
  id: string;
}

export interface IncomingMessage {
  id: string;
  srcIp: string;
  srcPk: string;
  dstIp: string;
  dstPk: string;
  /** base64-encoded topic bytes */
  topic: string;
  /** base64-encoded payload bytes */
  payload: string;
}

export interface DefaultAction {
  accept: boolean;
}

export interface PubkeyLookup {
  NodePubKey: string;
}

export const api = {
  daemonStatus: () => cmd<DaemonStatus>("daemon_status"),
  startDaemon: (config?: SidecarConfig) =>
    cmd<DaemonStatus>("start_daemon", { config: config ?? null }),
  stopDaemon: () => cmd<DaemonStatus>("stop_daemon"),
  nodeInfo: () => cmd<NodeInfo>("node_info"),
  sidecarLogs: () => cmd<string[]>("sidecar_logs"),

  peersList: () => cmd<PeerInfo[]>("peers_list"),
  peerAdd: (endpoint: string) => cmd<void>("peer_add", { endpoint }),
  peerRemove: (endpoint: string) => cmd<void>("peer_remove", { endpoint }),
  peersStats: () => cmd<AggregatedStats>("peers_stats"),

  routesSnapshot: () => cmd<RoutesSnapshot>("routes_snapshot"),

  sendMessage: (
    destination: MessageDestination,
    topicB64: string,
    payloadB64: string,
  ) =>
    cmd<PushMessageReceipt>("send_message", {
      destination,
      topicB64,
      payloadB64,
    }),
  replyMessage: (
    id: string,
    destination: MessageDestination,
    topicB64: string,
    payloadB64: string,
  ) =>
    cmd<PushMessageReceipt>("reply_message", {
      id,
      destination,
      topicB64,
      payloadB64,
    }),
  messageStatus: (id: string) =>
    cmd<Record<string, unknown>>("message_status", { id }),
  inboxMessages: () => cmd<IncomingMessage[]>("inbox_messages"),
  inboxClear: () => cmd<void>("inbox_clear"),

  topicsDefaultGet: () => cmd<DefaultAction>("topics_default_get"),
  topicsDefaultSet: (accept: boolean) =>
    cmd<void>("topics_default_set", { accept }),
  topicsList: () => cmd<string[]>("topics_list"),
  topicAdd: (topicB64: string) => cmd<void>("topic_add", { topicB64 }),
  topicRemove: (topicB64: string) => cmd<void>("topic_remove", { topicB64 }),
  topicSourcesList: (topicB64: string) =>
    cmd<string[]>("topic_sources_list", { topicB64 }),
  topicSourceAdd: (topicB64: string, subnet: string) =>
    cmd<void>("topic_source_add", { topicB64, subnet }),
  topicSourceRemove: (topicB64: string, subnet: string) =>
    cmd<void>("topic_source_remove", { topicB64, subnet }),
  topicForwardGet: (topicB64: string) =>
    cmd<string | null>("topic_forward_get", { topicB64 }),
  topicForwardSet: (topicB64: string, socketPath: string) =>
    cmd<void>("topic_forward_set", { topicB64, socketPath }),
  topicForwardRemove: (topicB64: string) =>
    cmd<void>("topic_forward_remove", { topicB64 }),

  lookupPubkey: (ip: string) => cmd<PubkeyLookup>("lookup_pubkey", { ip }),
  regenerateIdentity: () => cmd<void>("regenerate_identity"),
};

/** Format the canonical peer endpoint string the API expects. */
export function endpointString(e: PeerEndpoint): string {
  // socketAddr already contains host:port (with brackets for v6); proto is
  // the URL scheme.
  return `${e.proto}://${e.socketAddr}`;
}
