## test_variant_compiler.nim
## Tests for compiler flag resolution system
## Ensures proper flag resolution with priority ordering

import std/[unittest, tables, strutils, sequtils]
import ../src/nimpak/variant_compiler
import ../src/nimpak/variant_types
import ../src/nimpak/config

suite "Compiler Flag Resolution Tests":

  let baseFlags = CompilerFlags(
    cflags: "-O2 -pipe",
    cxxflags: "-O2 -pipe",
    ldflags: "-Wl,-O1",
    makeflags: "-j4"
  )

  test "Empty domains return base flags":
    var domains = initTable[string, seq[string]]()
    let resolved = resolveCompilerFlags(domains, baseFlags)

    check resolved.cflags == baseFlags.cflags
    check resolved.ldflags == baseFlags.ldflags

  test "Optimization LTO flag adds compiler flags":
    var domains = initTable[string, seq[string]]()
    domains["optimization"] = @["lto"]

    let resolved = resolveCompilerFlags(domains, baseFlags)

    check "-flto=full" in resolved.cflags
    check "-flto" in resolved.ldflags
    check "-fuse-ld=mold" in resolved.ldflags

  test "Optimization march-native flag":
    var domains = initTable[string, seq[string]]()
    domains["optimization"] = @["march-native"]

    let resolved = resolveCompilerFlags(domains, baseFlags)

    check "-march=native" in resolved.cflags

  test "Security PIE flag":
    var domains = initTable[string, seq[string]]()
    domains["security"] = @["pie"]

    let resolved = resolveCompilerFlags(domains, baseFlags)

    check "-fPIE" in resolved.cflags
    check "-pie" in resolved.ldflags

  test "Security RELRO flag":
    var domains = initTable[string, seq[string]]()
    domains["security"] = @["relro"]

    let resolved = resolveCompilerFlags(domains, baseFlags)

    check "-Wl,-z,relro,-z,now" in resolved.ldflags

  test "Security hardened flag":
    var domains = initTable[string, seq[string]]()
    domains["security"] = @["hardened"]

    let resolved = resolveCompilerFlags(domains, baseFlags)

    check "-D_FORTIFY_SOURCE=2" in resolved.cflags
    check "-fstack-protector-strong" in resolved.cflags

  test "Multiple flags in same domain":
    var domains = initTable[string, seq[string]]()
    domains["security"] = @["pie", "relro", "hardened"]

    let resolved = resolveCompilerFlags(domains, baseFlags)

    check "-fPIE" in resolved.cflags
    check "-D_FORTIFY_SOURCE=2" in resolved.cflags
    check "-pie" in resolved.ldflags
    check "-Wl,-z,relro,-z,now" in resolved.ldflags


suite "Priority Ordering Tests":

  let baseFlags = CompilerFlags(
    cflags: "-O2",
    ldflags: "-Wl,-O1"
  )

  test "Security flags applied before optimization":
    var domains = initTable[string, seq[string]]()
    domains["security"] = @["pie"]
    domains["optimization"] = @["lto"]

    let resolved = resolveCompilerFlags(domains, baseFlags)

    # Security flags should appear before optimization flags
    let cflagsStr = resolved.cflags
    let piePos = cflagsStr.find("-fPIE")
    let ltoPos = cflagsStr.find("-flto")

    check piePos >= 0
    check ltoPos >= 0
    check piePos < ltoPos  # PIE should come before LTO

  test "Multiple domains with priority ordering":
    var domains = initTable[string, seq[string]]()
    domains["optimization"] = @["lto", "march-native"]
    domains["security"] = @["pie", "relro"]

    let resolved = resolveCompilerFlags(domains, baseFlags)

    # All flags should be present
    check "-fPIE" in resolved.cflags
    check "-flto=full" in resolved.cflags
    check "-march=native" in resolved.cflags
    check "-pie" in resolved.ldflags
    check "-Wl,-z,relro,-z,now" in resolved.ldflags

suite "Flag Rule Query Tests":

  test "hasCompilerFlagRule detects existing rules":
    check hasCompilerFlagRule("optimization", "lto") == true
    check hasCompilerFlagRule("security", "pie") == true

  test "hasCompilerFlagRule returns false for non-existent rules":
    check hasCompilerFlagRule("optimization", "nonexistent") == false
    check hasCompilerFlagRule("nonexistent", "lto") == false

  test "getCompilerFlagRule returns correct rule":
    let rule = getCompilerFlagRule("optimization", "lto")
    check rule.condition == "lto"
    check rule.cflags == "-flto=full"
    check rule.ldflags == "-flto -fuse-ld=mold"

  test "getDomainRules returns all rules for domain":
    let rules = getDomainRules("optimization")
    check rules.hasKey("lto")
    check rules.hasKey("march-native")
    check rules.hasKey("pgo")

  test "getDomainRules returns empty for unknown domain":
    let rules = getDomainRules("unknown_domain")
    check rules.len == 0

suite "Flag Analysis Tests":

  test "analyzeCompilerFlags lists applied flags":
    var domains = initTable[string, seq[string]]()
    domains["optimization"] = @["lto"]
    domains["security"] = @["pie"]

    let analysis = analyzeCompilerFlags(domains)

    check analysis.len == 2
    check any(analysis, proc(s: string): bool = "optimization.lto" in s)
    check any(analysis, proc(s: string): bool = "security.pie" in s)

  test "explainFlag provides detailed information":
    let explanation = explainFlag("optimization", "lto")

    check "lto" in explanation
    check "CFLAGS" in explanation
    check "LDFLAGS" in explanation
    check "-flto=full" in explanation

  test "explainFlag handles non-existent flag":
    let explanation = explainFlag("optimization", "nonexistent")

    check "No compiler flag rule" in explanation

suite "Conflict Detection Tests":

  test "No conflicts in normal configuration":
    var domains = initTable[string, seq[string]]()
    domains["optimization"] = @["lto", "march-native"]
    domains["security"] = @["pie", "relro"]

    let conflicts = detectFlagConflicts(domains)
    check conflicts.len == 0
    check hasConflicts(domains) == false

  test "Detect debug + strip conflict":
    var domains = initTable[string, seq[string]]()
    domains["optimization"] = @["debug", "strip"]

    let conflicts = detectFlagConflicts(domains)
    check conflicts.len > 0
    check hasConflicts(domains) == true
    check any(conflicts, proc(s: string): bool = "debug" in s and "strip" in s)

  test "Detect debug + lto warning":
    var domains = initTable[string, seq[string]]()
    domains["optimization"] = @["debug", "lto"]

    let conflicts = detectFlagConflicts(domains)
    check conflicts.len > 0
    check any(conflicts, proc(s: string): bool = "debug" in s and "lto" in s)

suite "Helper Function Tests":

  test "mergeCompilerFlags combines flags":
    let base = CompilerFlags(
      cflags: "-O2",
      ldflags: "-Wl,-O1"
    )

    let additional = CompilerFlags(
      cflags: "-march=native",
      ldflags: "-flto"
    )

    let merged = mergeCompilerFlags(base, additional)

    check "-O2" in merged.cflags
    check "-march=native" in merged.cflags
    check "-Wl,-O1" in merged.ldflags
    check "-flto" in merged.ldflags

  test "cleanCompilerFlags removes extra spaces":
    let dirty = CompilerFlags(
      cflags: "-O2   -pipe  -march=native",
      ldflags: "-Wl,-O1  -flto"
    )

    let clean = cleanCompilerFlags(dirty)

    check clean.cflags == "-O2 -pipe -march=native"
    check clean.ldflags == "-Wl,-O1 -flto"

suite "Real-World Configuration Tests":

  let baseFlags = CompilerFlags(
    cflags: "-O2 -pipe",
    ldflags: "-Wl,-O1"
  )

  test "Fleet node configuration":
    var domains = initTable[string, seq[string]]()
    domains["security"] = @["pie", "relro", "hardened"]
    domains["optimization"] = @["lto", "march-native"]

    let resolved = resolveCompilerFlags(domains, baseFlags)

    # Security flags
    check "-fPIE" in resolved.cflags
    check "-D_FORTIFY_SOURCE=2" in resolved.cflags
    check "-pie" in resolved.ldflags
    check "-Wl,-z,relro,-z,now" in resolved.ldflags

    # Optimization flags
    check "-flto=full" in resolved.cflags
    check "-march=native" in resolved.cflags

  test "Debug build configuration":
    var domains = initTable[string, seq[string]]()
    domains["optimization"] = @["debug"]

    let resolved = resolveCompilerFlags(domains, baseFlags)

    check "-g" in resolved.cflags
    check "-O0" in resolved.cflags

  test "Minimal configuration":
    var domains = initTable[string, seq[string]]()
    domains["security"] = @["pie"]

    let resolved = resolveCompilerFlags(domains, baseFlags)

    check "-fPIE" in resolved.cflags
    check "-pie" in resolved.ldflags
    # Base flags should still be present
    check "-O2" in resolved.cflags

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