/**
 * Client-side WireGuard config codecs (mirror of backend wireguard-config).
 * Used for mock preview / offline export without hitting the API.
 */

export type WgParsedPeer = {
  publicKey: string
  allowedAddresses: string[]
  endpointAddress?: string
  endpointPort?: number
  persistentKeepalive?: number
  comment?: string
  name?: string
  privateKey?: string
  clientAddress?: string
  clientDns?: string
  clientEndpoint?: string
  disabled?: boolean
}

export type WgParsedInterface = {
  name: string
  listenPort?: number
  mtu?: number
  privateKey?: string
  comment?: string
  address?: string
  disabled?: boolean
}

export type WgParsedConfig = {
  format: "rsc" | "conf"
  interface: WgParsedInterface
  peers: WgParsedPeer[]
}

export type WgExportIface = {
  name: string
  listenPort: number
  mtu: number
  comment?: string
  enabled?: boolean
  privateKey?: string
  publicKey?: string
  address?: string
  serverName?: string
  peers: Array<{
    publicKey: string
    allowedIps: string[]
    endpoint?: string
    persistentKeepalive?: number
    persistent?: boolean
    comment?: string
    name?: string
    clientAddress?: string
    clientDns?: string
    clientEndpoint?: string
  }>
}

function stripQuotes(v: string): string {
  const t = v.trim()
  if ((t.startsWith('"') && t.endsWith('"')) || (t.startsWith("'") && t.endsWith("'"))) {
    return t.slice(1, -1)
  }
  return t
}

function parseKvLine(line: string): Record<string, string> {
  const out: Record<string, string> = {}
  const re = /([a-zA-Z0-9_-]+)=("(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*'|[^\s\\]+)/g
  let m: RegExpExecArray | null
  while ((m = re.exec(line)) !== null) {
    out[m[1]] = stripQuotes(m[2])
  }
  return out
}

function joinContinuedLines(text: string): string[] {
  const raw = text.replace(/\r\n/g, "\n").replace(/\r/g, "\n").split("\n")
  const lines: string[] = []
  let buf = ""
  for (const line of raw) {
    const trimmedEnd = line.replace(/\s+$/, "")
    if (trimmedEnd.endsWith("\\")) {
      buf += trimmedEnd.slice(0, -1).trimEnd() + " "
      continue
    }
    buf += trimmedEnd
    if (buf.trim()) lines.push(buf.trim())
    buf = ""
  }
  if (buf.trim()) lines.push(buf.trim())
  return lines
}

export function detectWgConfigFormat(content: string): "rsc" | "conf" {
  const t = content.trim()
  if (/\[Interface\]/i.test(t) || /\[Peer\]/i.test(t)) return "conf"
  if (/\/interface\s+wireguard/i.test(t) || /\/interface\/wireguard/i.test(t)) return "rsc"
  if (/PrivateKey\s*=/i.test(t) || /PublicKey\s*=/i.test(t)) return "conf"
  return "rsc"
}

export function parseNativeConf(content: string): WgParsedConfig {
  const lines = content.replace(/\r\n/g, "\n").split("\n")
  let section: "interface" | "peer" | null = null
  const iface: WgParsedInterface = { name: "wg0" }
  const peers: WgParsedPeer[] = []
  let currentPeer: WgParsedPeer | null = null

  const flushPeer = () => {
    if (currentPeer?.publicKey) peers.push(currentPeer)
    currentPeer = null
  }

  for (const raw of lines) {
    const line = raw.trim()
    if (!line || line.startsWith("#") || line.startsWith(";")) continue
    if (/^\[Interface\]$/i.test(line)) {
      flushPeer()
      section = "interface"
      continue
    }
    if (/^\[Peer\]$/i.test(line)) {
      flushPeer()
      section = "peer"
      currentPeer = { publicKey: "", allowedAddresses: [] }
      continue
    }
    const eq = line.indexOf("=")
    if (eq < 0) continue
    const key = line.slice(0, eq).trim().toLowerCase()
    const value = line.slice(eq + 1).trim()

    if (section === "interface") {
      if (key === "privatekey") iface.privateKey = value
      else if (key === "address") iface.address = value.split(",")[0]?.trim()
      else if (key === "listenport") iface.listenPort = Number.parseInt(value, 10) || undefined
      else if (key === "mtu") iface.mtu = Number.parseInt(value, 10) || undefined
      else if (key === "name") iface.name = value || iface.name
    } else if (section === "peer" && currentPeer) {
      if (key === "publickey") currentPeer.publicKey = value
      else if (key === "allowedips") {
        currentPeer.allowedAddresses = value
          .split(",")
          .map((s) => s.trim())
          .filter(Boolean)
      } else if (key === "endpoint") {
        const lastColon = value.lastIndexOf(":")
        if (lastColon > 0 && !value.includes("]:")) {
          currentPeer.endpointAddress = value.slice(0, lastColon)
          currentPeer.endpointPort = Number.parseInt(value.slice(lastColon + 1), 10) || undefined
        } else {
          currentPeer.endpointAddress = value
        }
      } else if (key === "persistentkeepalive") {
        currentPeer.persistentKeepalive = Number.parseInt(value, 10) || undefined
      }
    }
  }
  flushPeer()
  return { format: "conf", interface: iface, peers }
}

export function parseMikrotikRsc(content: string): WgParsedConfig {
  const lines = joinContinuedLines(content)
  const iface: WgParsedInterface = { name: "wg0" }
  const peers: WgParsedPeer[] = []
  let foundIface = false

  for (const line of lines) {
    if (line.startsWith("#")) continue
    const lower = line.toLowerCase()

    if (
      lower.startsWith("/interface wireguard add") ||
      lower.startsWith("/interface/wireguard add")
    ) {
      const kv = parseKvLine(line)
      if (kv.name) iface.name = kv.name
      if (kv["listen-port"]) iface.listenPort = Number.parseInt(kv["listen-port"], 10) || undefined
      if (kv.mtu) iface.mtu = Number.parseInt(kv.mtu, 10) || undefined
      if (kv["private-key"]) iface.privateKey = kv["private-key"]
      if (kv.comment) iface.comment = kv.comment
      if (kv.disabled === "yes") iface.disabled = true
      foundIface = true
      continue
    }

    if (
      lower.startsWith("/interface wireguard peers add") ||
      lower.startsWith("/interface/wireguard/peers add")
    ) {
      const kv = parseKvLine(line)
      const allowed = (kv["allowed-address"] ?? "")
        .split(",")
        .map((s) => s.trim())
        .filter(Boolean)
      peers.push({
        publicKey: kv["public-key"] ?? "",
        allowedAddresses: allowed.length ? allowed : ["0.0.0.0/0"],
        endpointAddress: kv["endpoint-address"],
        endpointPort: kv["endpoint-port"]
          ? Number.parseInt(kv["endpoint-port"], 10) || undefined
          : undefined,
        persistentKeepalive: kv["persistent-keepalive"]
          ? Number.parseInt(kv["persistent-keepalive"], 10) || undefined
          : undefined,
        comment: kv.comment,
        name: kv.name,
        clientAddress: kv["client-address"],
        clientDns: kv["client-dns"],
        clientEndpoint: kv["client-endpoint"],
        disabled: kv.disabled === "yes",
      })
      continue
    }

    if (lower.startsWith("/ip address add") || lower.startsWith("/ip/address add")) {
      const kv = parseKvLine(line)
      if (kv.address) iface.address = kv.address
    }
  }

  if (!foundIface && peers.length === 0) {
    throw new Error("Не удалось распознать RouterOS WireGuard .rsc")
  }
  return { format: "rsc", interface: iface, peers }
}

export function parseWgConfig(
  content: string,
  format: "auto" | "rsc" | "conf" = "auto",
): WgParsedConfig {
  const detected = format === "auto" ? detectWgConfigFormat(content) : format
  if (detected === "conf") return parseNativeConf(content)
  return parseMikrotikRsc(content)
}

export function generateNativeConf(iface: WgExportIface): string {
  const lines: string[] = []
  lines.push(`[Interface]`)
  if (iface.privateKey) lines.push(`PrivateKey = ${iface.privateKey}`)
  else lines.push(`# PrivateKey = <заполните приватный ключ с роутера>`)
  if (iface.address) lines.push(`Address = ${iface.address}`)
  lines.push(`ListenPort = ${iface.listenPort}`)
  if (iface.mtu) lines.push(`MTU = ${iface.mtu}`)
  lines.push(``)

  for (const p of iface.peers) {
    lines.push(`[Peer]`)
    lines.push(`PublicKey = ${p.publicKey}`)
    lines.push(`AllowedIPs = ${p.allowedIps.join(", ")}`)
    if (p.endpoint) lines.push(`Endpoint = ${p.endpoint}`)
    const ka = p.persistentKeepalive ?? (p.persistent ? 25 : undefined)
    if (ka != null && ka > 0) lines.push(`PersistentKeepalive = ${ka}`)
    if (p.comment) lines.push(`# ${p.comment}`)
    lines.push(``)
  }
  return lines.join("\n").trimEnd() + "\n"
}

export function generateMikrotikRsc(iface: WgExportIface): string {
  const lines: string[] = []
  lines.push(`# WireGuard — ${iface.name}${iface.serverName ? ` · ${iface.serverName}` : ""}`)
  lines.push(`# RouterOS 7.x · MikrotikManager`)
  lines.push(``)
  lines.push(`/interface wireguard add \\`)
  lines.push(`  name=${iface.name} \\`)
  lines.push(`  listen-port=${iface.listenPort} \\`)
  lines.push(`  mtu=${iface.mtu} \\`)
  if (iface.privateKey) lines.push(`  private-key="${iface.privateKey}" \\`)
  if (iface.comment) lines.push(`  comment="${iface.comment.replace(/"/g, '\\"')}" \\`)
  if (iface.enabled === false) lines.push(`  disabled=yes \\`)
  if (lines[lines.length - 1]?.endsWith(" \\")) {
    lines[lines.length - 1] = lines[lines.length - 1]!.slice(0, -2)
  }
  lines.push(``)

  if (iface.address) {
    lines.push(`/ip address add \\`)
    lines.push(`  address=${iface.address} \\`)
    lines.push(`  interface=${iface.name}`)
    lines.push(``)
  }

  for (const p of iface.peers) {
    lines.push(`/interface wireguard peers add \\`)
    lines.push(`  interface=${iface.name} \\`)
    lines.push(`  public-key="${p.publicKey}" \\`)
    lines.push(`  allowed-address=${p.allowedIps.join(",")} \\`)
    if (p.endpoint) {
      const host = p.endpoint.includes(":") ? p.endpoint.slice(0, p.endpoint.lastIndexOf(":")) : p.endpoint
      const port = p.endpoint.includes(":")
        ? p.endpoint.slice(p.endpoint.lastIndexOf(":") + 1)
        : "13231"
      lines.push(`  endpoint-address=${host} \\`)
      lines.push(`  endpoint-port=${port} \\`)
    }
    const ka = p.persistentKeepalive ?? (p.persistent ? 25 : undefined)
    if (ka != null && ka > 0) lines.push(`  persistent-keepalive=${ka} \\`)
    if (p.name) lines.push(`  name=${p.name} \\`)
    if (p.clientAddress) lines.push(`  client-address=${p.clientAddress} \\`)
    if (p.clientDns) lines.push(`  client-dns=${p.clientDns} \\`)
    if (p.clientEndpoint) lines.push(`  client-endpoint=${p.clientEndpoint} \\`)
    if (p.comment) lines.push(`  comment="${p.comment.replace(/"/g, '\\"')}" \\`)
    if (lines[lines.length - 1]?.endsWith(" \\")) {
      lines[lines.length - 1] = lines[lines.length - 1]!.slice(0, -2)
    }
    lines.push(``)
  }
  return lines.join("\n")
}

export function generatePeerClientConf(args: {
  peerAddress?: string
  peerDns?: string
  serverPublicKey: string
  allowedIps?: string[]
  endpoint?: string
  persistentKeepalive?: number
}): string {
  const lines: string[] = []
  lines.push(`[Interface]`)
  lines.push(`# PrivateKey = <ключ клиента>`)
  if (args.peerAddress) lines.push(`Address = ${args.peerAddress}`)
  if (args.peerDns) lines.push(`DNS = ${args.peerDns}`)
  lines.push(``)
  lines.push(`[Peer]`)
  lines.push(`PublicKey = ${args.serverPublicKey}`)
  lines.push(`AllowedIPs = ${(args.allowedIps?.length ? args.allowedIps : ["0.0.0.0/0"]).join(", ")}`)
  if (args.endpoint) lines.push(`Endpoint = ${args.endpoint}`)
  if (args.persistentKeepalive != null && args.persistentKeepalive > 0) {
    lines.push(`PersistentKeepalive = ${args.persistentKeepalive}`)
  }
  lines.push(``)
  return lines.join("\n")
}
