import { eq } from "drizzle-orm"
import { db } from "../db/index.js"
import { servers } from "../db/schema.js"
import { listUsers } from "../modules/users/service/users-service.js"
import { readServerSamplesInRange } from "./traffic-collector.js"
import {
  buildTrafficFromSamples,
  mergeBuiltTraffic,
  type BuiltTrafficSeries,
} from "./traffic-rate.js"

export interface BoundIfaceTrafficDto {
  id: string
  bindingId: string
  userId: string
  userLogin: string
  userName: string
  interfaceName: string
  interfaceType: string
  comment: string
  serverId: string
  serverName: string
  serverSite: string
  serverCountry: string
  rxNow: number
  txNow: number
  rxPeak: number
  txPeak: number
  rxTotal: number
  txTotal: number
  rxSeries: number[]
  txSeries: number[]
  status: "online" | "offline"
}

export interface UserTrafficDto {
  id: string
  login: string
  displayName: string
  role: string
  active: boolean
  interfaces: BoundIfaceTrafficDto[]
  rxNow: number
  txNow: number
  rxPeak: number
  txPeak: number
  rxTotal: number
  txTotal: number
  rxSeries: number[]
  txSeries: number[]
}

function seriesFromBuilt(built: BuiltTrafficSeries) {
  return {
    rxNow: built.rxNow,
    txNow: built.txNow,
    rxPeak: built.rxPeak,
    txPeak: built.txPeak,
    rxTotal: built.rxTotalGiB,
    txTotal: built.txTotalGiB,
    rxSeries: built.rxSeries,
    txSeries: built.txSeries,
  }
}

function serverStatus(serverId: number): "online" | "offline" {
  const row = db.select().from(servers).where(eq(servers.id, serverId)).limit(1).all()[0]
  if (!row?.enabled) return "offline"
  return "online"
}

export function buildUserTrafficList(rangeStartMs: number, rangeEndMs: number): UserTrafficDto[] {
  const sinceIso = new Date(rangeStartMs).toISOString()
  const users = listUsers()
  const sampleCache = new Map<number, ReturnType<typeof readServerSamplesInRange>>()

  return users.map((user) => {
    const parts: BuiltTrafficSeries[] = []
    const interfaces: BoundIfaceTrafficDto[] = []
    const byServer = new Map<number, string[]>()
    for (const b of user.bindings) {
      const arr = byServer.get(b.serverId) ?? []
      arr.push(b.interfaceName)
      byServer.set(b.serverId, arr)
    }

    for (const [serverId, names] of byServer) {
      let rows = sampleCache.get(serverId)
      if (!rows) {
        rows = readServerSamplesInRange(serverId, sinceIso)
        sampleCache.set(serverId, rows)
      }
      const built = buildTrafficFromSamples(rows, rangeStartMs, rangeEndMs, names)
      parts.push(built)
    }

    for (const b of user.bindings) {
      let rows = sampleCache.get(b.serverId)
      if (!rows) {
        rows = readServerSamplesInRange(b.serverId, sinceIso)
        sampleCache.set(b.serverId, rows)
      }
      const built = buildTrafficFromSamples(rows, rangeStartMs, rangeEndMs, b.interfaceName)
      const last = [...rows.filter((r) => r.interfaceName === b.interfaceName)]
        .sort((a, c) => a.sampledAt.localeCompare(c.sampledAt))
        .at(-1)
      const running = Boolean(last?.running) && !last?.disabled
      interfaces.push({
        id: `${b.userId}:${b.serverId}:${b.interfaceName}`,
        bindingId: b.id,
        userId: user.id,
        userLogin: user.login,
        userName: user.name,
        interfaceName: b.interfaceName,
        interfaceType: b.interfaceType,
        comment: b.comment,
        serverId: String(b.serverId),
        serverName: b.serverName,
        serverSite: b.serverSite,
        serverCountry: b.serverCountry,
        ...seriesFromBuilt(built),
        status: running && serverStatus(b.serverId) === "online" ? "online" : "offline",
      })
    }

    const merged = mergeBuiltTraffic(parts)
    return {
      id: user.id,
      login: user.login,
      displayName: user.name,
      role: user.role,
      active: user.active,
      interfaces,
      ...seriesFromBuilt(merged),
    }
  })
}

export function buildBoundInterfaceTraffic(rangeStartMs: number, rangeEndMs: number): BoundIfaceTrafficDto[] {
  return buildUserTrafficList(rangeStartMs, rangeEndMs).flatMap((u) => u.interfaces)
}
