import { eq } from "drizzle-orm"
import { db } from "../db/index.js"
import { servers, serverSnapshots } from "../db/schema.js"
import type { SnapshotInsert } from "../db/schema.js"
import type { SnapshotRead } from "../types/server.js"
import { MikrotikClient } from "./mikrotik.js"
import { parseRosCpuLoadPercent, parseRosDataSizeBytes } from "./ros-metric-parse.js"

// ── pollServer ─────────────────────────────────────────────────────────────────

/**
 * Connect to a RouterOS device, collect system info, persist a snapshot,
 * and sync the server's display name from system/identity.
 */
export async function pollServer(serverId: number): Promise<SnapshotRead> {
  const server = db
    .select()
    .from(servers)
    .where(eq(servers.id, serverId))
    .limit(1)
    .all()[0]

  if (!server) {
    throw new Error(`Server with id=${serverId} not found`)
  }

  const now = new Date().toISOString()
  const client = MikrotikClient.fromServer(server)
  const t0 = performance.now()

  const partialSnap: Partial<SnapshotInsert> = {
    serverId,
    polledAt: now,
    status: "offline",
    latencyMs: null,
  }

  try {
    // Fire all requests in parallel for speed
    const [identity, resource, ifaces, addresses] = await Promise.all([
      client.getIdentity(),
      client.getResource(),
      client.getInterfaces(),
      client.getIpAddresses(),
    ])

    const latencyMs = performance.now() - t0

    const cpuLoad  = parseRosCpuLoadPercent(resource["cpu-load"])
    const freeMem  = parseRosDataSizeBytes(resource["free-memory"])
    const totalMem = parseRosDataSizeBytes(resource["total-memory"])

    Object.assign(partialSnap, {
      status:         "online",
      latencyMs,
      identityName:   identity.name,
      rosVersion:     resource["version"],
      boardName:      resource["board-name"],
      uptime:         resource["uptime"],
      cpuLoad,
      freeMemory:     freeMem,
      totalMemory:    totalMem,
      rawInterfaces:  JSON.stringify(ifaces),
      rawIpAddresses: JSON.stringify(addresses),
    } satisfies Partial<SnapshotInsert>)

    // Keep server.name in sync with RouterOS identity
    db.update(servers)
      .set({ name: identity.name, updatedAt: now })
      .where(eq(servers.id, serverId))
      .run()

  } catch (err) {
    // Log but don't throw — we still persist the offline snapshot
    console.warn(`[poller] server id=${serverId} unreachable:`, (err as Error).message)
  }

  const [inserted] = db
    .insert(serverSnapshots)
    .values(partialSnap as SnapshotInsert)
    .returning()
    .all()

  return toSnapshotRead(inserted)
}

// ── helper ─────────────────────────────────────────────────────────────────────

export function toSnapshotRead(s: typeof serverSnapshots.$inferSelect): SnapshotRead {
  return {
    id:           s.id,
    serverId:     s.serverId,
    polledAt:     s.polledAt,
    status:       s.status,
    latencyMs:    s.latencyMs ?? null,
    rosVersion:   s.rosVersion ?? null,
    boardName:    s.boardName ?? null,
    uptime:       s.uptime ?? null,
    cpuLoad:      s.cpuLoad ?? null,
    freeMemory:   s.freeMemory ?? null,
    totalMemory:  s.totalMemory ?? null,
    identityName: s.identityName ?? null,
  }
}
