## test_variant_parser.nim
## Tests for CLI flag parser
## Ensures proper parsing of domain-scoped and legacy flags

import std/[unittest, tables, strutils]
import ../src/nimpak/variant_parser
import ../src/nimpak/variant_types
import ../src/nimpak/variant_validator

suite "Domain-Scoped Flag Parsing Tests":

  test "Parse simple domain flag":
    let flag = parseDomainFlag("+init=dinit")
    check flag.domain == "init"
    check flag.name == "dinit"
    check flag.enabled == true
    check flag.value == "dinit"

  test "Parse domain flag with multiple values":
    let flag = parseDomainFlag("+security=pie,relro,hardened")
    check flag.domain == "security"
    check flag.enabled == true
    check flag.value == "pie,relro,hardened"

  test "Parse disabled domain flag":
    let flag = parseDomainFlag("-optimization=debug")
    check flag.domain == "optimization"
    check flag.enabled == false

  test "Parse domain flag without prefix defaults to enabled":
    let flag = parseDomainFlag("runtime=ssl")
    check flag.domain == "runtime"
    check flag.enabled == true

  test "Parse domain flag with spaces":
    let flag = parseDomainFlag("+ security = pie , relro ")
    check flag.domain == "security"
    check flag.value.contains("pie")
    check flag.value.contains("relro")

  test "Invalid domain raises error":
    expect(ParseError):
      discard parseDomainFlag("+invalid_domain=value")

  test "Invalid value raises error":
    expect(ParseError):
      discard parseDomainFlag("+init=invalid_init_system")

  test "Empty flag raises error":
    expect(ParseError):
      discard parseDomainFlag("")

  test "Flag without value raises error":
    expect(ParseError):
      discard parseDomainFlag("+init=")

suite "Legacy Flag Parsing Tests":

  test "Parse legacy enabled flag":
    let flag = parseLegacyFlag("+lto")
    check flag.name == "lto"
    check flag.enabled == true
    check flag.domain == "optimization"

  test "Parse legacy disabled flag":
    let flag = parseLegacyFlag("-systemd")
    check flag.name == "systemd"
    check flag.enabled == false
    check flag.domain == "init"

  test "Parse legacy flag without prefix":
    let flag = parseLegacyFlag("ssl")
    check flag.name == "ssl"
    check flag.enabled == true

  test "Legacy flag maps to correct domain - init":
    let flag = parseLegacyFlag("+dinit")
    check flag.domain == "init"

  test "Legacy flag maps to correct domain - security":
    let flag = parseLegacyFlag("+pie")
    check flag.domain == "security"

  test "Legacy flag maps to correct domain - graphics":
    let flag = parseLegacyFlag("+wayland")
    check flag.domain == "graphics"

  test "Legacy flag maps to correct domain - audio":
    let flag = parseLegacyFlag("+pipewire")
    check flag.domain == "audio"

  test "Unknown legacy flag maps to runtime":
    let flag = parseLegacyFlag("+unknown_flag")
    check flag.domain == "runtime"

  test "Empty legacy flag raises error":
    expect(ParseError):
      discard parseLegacyFlag("")

  test "Invalid legacy flag raises error":
    expect(ParseError):
      discard parseLegacyFlag("+")

suite "Auto-Detection Tests":

  test "Domain-scoped flag detected correctly":
    let flag = parseDomainFlag("+init=dinit")
    check flag.domain == "init"

  test "Legacy flag auto-detected and parsed":
    let flag = parseDomainFlag("+lto")
    check flag.domain == "optimization"
    check flag.name == "lto"


suite "Multi-Flag Parsing Tests":

  test "Parse multiple flags":
    let flags = parseFlags(@["+init=dinit", "+security=pie,relro", "+optimization=lto"])
    check flags.len == 3
    check flags[0].domain == "init"
    check flags[1].domain == "security"
    check flags[2].domain == "optimization"

  test "Parse mixed domain and legacy flags":
    let flags = parseFlags(@["+init=dinit", "+lto", "-systemd"])
    check flags.len == 3
    check flags[0].domain == "init"
    check flags[1].domain == "optimization"
    check flags[2].domain == "init"

  test "Parse flags from string":
    let flags = parseFlagsString("+init=dinit +security=pie,relro -optimization=debug")
    check flags.len == 3

  test "Empty flags list returns empty":
    let flags = parseFlags(@[])
    check flags.len == 0

  test "Flags with empty strings are skipped":
    let flags = parseFlags(@["+init=dinit", "", "+security=pie"])
    check flags.len == 2

suite "Flag Grouping Tests":

  test "Group flags by domain":
    let flags = parseFlags(@["+init=dinit", "+security=pie", "+security=relro"])
    let grouped = groupFlagsByDomain(flags)

    check grouped.hasKey("init")
    check grouped.hasKey("security")
    check grouped["init"] == @["dinit"]
    check "pie" in grouped["security"]
    check "relro" in grouped["security"]

  test "Disabled flags are excluded from grouping":
    let flags = parseFlags(@["+init=dinit", "-security=pie"])
    let grouped = groupFlagsByDomain(flags)

    check grouped.hasKey("init")
    check not grouped.hasKey("security")

  test "Multiple values in single flag are split":
    let flags = parseFlags(@["+security=pie,relro,hardened"])
    let grouped = groupFlagsByDomain(flags)

    check grouped["security"].len == 3
    check "pie" in grouped["security"]
    check "relro" in grouped["security"]
    check "hardened" in grouped["security"]

  test "Duplicate values are deduplicated":
    let flags = parseFlags(@["+security=pie", "+security=pie"])
    let grouped = groupFlagsByDomain(flags)

    check grouped["security"].len == 1
    check grouped["security"] == @["pie"]

suite "Parse and Validate Tests":

  test "Valid flags pass validation":
    let domains = parseAndValidate(@["+init=dinit", "+security=pie,relro"])
    check domains.hasKey("init")
    check domains.hasKey("security")

  test "Invalid domain fails validation":
    expect(ParseError):
      discard parseAndValidate(@["+invalid_domain=value"])

  test "Invalid value fails validation":
    expect(ParseError):
      discard parseAndValidate(@["+init=invalid_init"])

  test "Conflicting flags fail validation":
    expect(VariantError):
      discard parseAndValidate(@["+init=dinit", "+init=systemd"])

  test "Parse and validate string":
    let domains = parseAndValidateString("+init=dinit +security=pie")
    check domains.hasKey("init")
    check domains.hasKey("security")

suite "Helper Function Tests":

  test "isDomainScopedFlag detects domain syntax":
    check isDomainScopedFlag("+init=dinit") == true
    check isDomainScopedFlag("+lto") == false

  test "isLegacyFlag detects legacy syntax":
    check isLegacyFlag("+lto") == true
    check isLegacyFlag("+init=dinit") == false

  test "extractDomainFromFlag extracts domain":
    check extractDomainFromFlag("+init=dinit") == "init"
    check extractDomainFromFlag("+security=pie,relro") == "security"
    check extractDomainFromFlag("+lto") == "optimization"

  test "extractValuesFromFlag extracts values":
    let values1 = extractValuesFromFlag("+security=pie,relro,hardened")
    check values1.len == 3
    check "pie" in values1

    let values2 = extractValuesFromFlag("+lto")
    check values2.len == 1
    check values2[0] == "lto"

  test "extractValuesFromFlag handles spaces":
    let values = extractValuesFromFlag("+security= pie , relro ")
    check values.len == 2
    check "pie" in values
    check "relro" in values

suite "Edge Cases Tests":

  test "Flag with trailing comma":
    let flag = parseDomainFlag("+security=pie,relro,")
    check flag.value.contains("pie")
    check flag.value.contains("relro")

  test "Flag with leading comma is handled":
    # This should work or raise a clear error
    try:
      let flag = parseDomainFlag("+security=,pie,relro")
      check "pie" in flag.value
    except ParseError:
      check true  # Expected behavior

  test "Flag with only commas raises error":
    expect(ParseError):
      discard parseDomainFlag("+security=,,,")

  test "Very long flag value":
    let longValue = "pie,relro,hardened,fortify,stack-protector"
    let flag = parseDomainFlag("+security=" & longValue)
    check flag.value == longValue

  test "Unicode in flag name is rejected":
    expect(ParseError):
      discard parseDomainFlag("+init=dînit")

suite "Real-World Usage Tests":

  test "Fleet node configuration":
    let flags = parseFlagsString("+init=dinit +runtime=ssl,http3,zstd +security=pie,relro,hardened +optimization=lto,march-native +network=ipv6,wireguard")
    let domains = groupFlagsByDomain(flags)

    check domains.hasKey("init")
    check domains.hasKey("runtime")
    check domains.hasKey("security")
    check domains.hasKey("optimization")
    check domains.hasKey("network")

  test "Gaming rig configuration":
    let flags = parseFlagsString("+init=systemd +graphics=wayland +audio=pipewire +runtime=steam +optimization=lto,march-native")
    let domains = groupFlagsByDomain(flags)

    check domains["init"] == @["systemd"]
    check domains["graphics"] == @["wayland"]
    check domains["audio"] == @["pipewire"]

  test "Minimal configuration":
    let flags = parseFlagsString("+init=dinit")
    let domains = groupFlagsByDomain(flags)

    check domains.len == 1
    check domains["init"] == @["dinit"]

  test "Mixed legacy and modern syntax":
    let flags = parseFlagsString("+init=dinit +lto +pie -systemd")
    let domains = groupFlagsByDomain(flags)

    check domains.hasKey("init")
    check domains.hasKey("optimization")
    check domains.hasKey("security")

when isMainModule:
  echo "Running variant parser tests..."
