## config.nim
## Configuration management for NIP MVP
## Simple key-value configuration format

import std/[os, strutils, tables, strformat, posix]

type
  UseFlag* = object
    name*: string
    enabled*: bool
    category*: string

  CompilerFlags* = object
    cflags*: string
    cxxflags*: string
    ldflags*: string
    makeflags*: string

  BuildProfile* = object
    name*: string
    description*: string
    baseProfile*: string
    useFlags*: seq[UseFlag]
    compilerFlags*: CompilerFlags

  PackageConfig* = object
    name*: string
    useFlags*: seq[UseFlag]
    compilerFlags*: CompilerFlags

  NipConfig* = object
    programsDir*: string
    linksDir*: string
    cacheDir*: string
    dbFile*: string
    autoSymlink*: bool
    checkConflicts*: bool
    verbose*: bool
    adapters*: Table[string, AdapterConfig]
    # USE Flags and Build Settings
    globalUseFlags*: seq[UseFlag]
    compilerFlags*: CompilerFlags
    activeProfile*: string
    profiles*: Table[string, BuildProfile]
    packageConfigs*: Table[string, PackageConfig]
    # Variant System Settings (Task 13.1)
    defaultToolchain*: string        # e.g., "gcc-13.2.0"
    defaultTarget*: string            # e.g., "x86_64-linux"
    profileSearchPaths*: seq[string]  # Paths to search for variant profiles

  AdapterConfig* = object
    enabled*: bool
    priority*: int

proc defaultConfig*(): NipConfig =
  ## Create default configuration
  ## Automatically detects if running as root or user and sets appropriate paths
  ## Follows XDG Base Directory specification for user installations

  # Detect if running as root (UID 0) or regular user
  let isRoot = getuid() == 0

  # Set paths based on user context
  let (programsDir, linksDir, cacheDir, dbFile, profilePaths) =
    if isRoot:
      # System-wide installation (root)
      ("/Programs",
       "/System/Links",
       "/var/cache/nip",
       "/var/lib/nip/packages.json",
       @["/etc/nip/profiles"])
    else:
      # User-local installation (non-root) - XDG compliant
      let homeDir = getHomeDir()
      let xdgDataHome = getEnv("XDG_DATA_HOME", homeDir / ".local" / "share")
      let xdgConfigHome = getEnv("XDG_CONFIG_HOME", homeDir / ".config")
      let xdgCacheHome = getEnv("XDG_CACHE_HOME", homeDir / ".cache")

      (xdgDataHome / "nip" / "Programs",
       xdgDataHome / "nip" / "Links",
       xdgCacheHome / "nip",
       xdgDataHome / "nip" / "db" / "packages.json",
       @[xdgConfigHome / "nip" / "profiles",
         xdgDataHome / "nip" / "profiles"])

  result = NipConfig(
    programsDir: programsDir,
    linksDir: linksDir,
    cacheDir: cacheDir,
    dbFile: dbFile,
    autoSymlink: true,
    checkConflicts: true,
    verbose: false,
    adapters: initTable[string, AdapterConfig](),
    globalUseFlags: @[],
    compilerFlags: CompilerFlags(
      cflags: "-O2 -pipe",
      cxxflags: "-O2 -pipe",
      ldflags: "-Wl,-O1",
      makeflags: "-j4"
    ),
    activeProfile: "default",
    profiles: initTable[string, BuildProfile](),
    packageConfigs: initTable[string, PackageConfig](),
    # Variant system defaults (Task 13.1)
    defaultToolchain: "gcc-13.2.0",
    defaultTarget: "x86_64-linux",
    profileSearchPaths: profilePaths
  )

  # Default adapter priorities
  result.adapters["nix"] = AdapterConfig(enabled: true, priority: 10)
  result.adapters["pkgsrc"] = AdapterConfig(enabled: true, priority: 20)
  result.adapters["pacman"] = AdapterConfig(enabled: true, priority: 30)

  # Default build profiles
  result.profiles["default"] = BuildProfile(
    name: "default",
    description: "Default balanced profile",
    baseProfile: "",
    useFlags: @[],
    compilerFlags: CompilerFlags(
      cflags: "-O2 -pipe",
      cxxflags: "-O2 -pipe",
      ldflags: "-Wl,-O1",
      makeflags: "-j4"
    )
  )

  result.profiles["minimal"] = BuildProfile(
    name: "minimal",
    description: "Minimal features, small size",
    baseProfile: "default",
    useFlags: @[],
    compilerFlags: CompilerFlags(
      cflags: "-Os -pipe",
      cxxflags: "-Os -pipe",
      ldflags: "-Wl,-O1 -Wl,--as-needed",
      makeflags: "-j4"
    )
  )

  result.profiles["performance"] = BuildProfile(
    name: "performance",
    description: "Maximum performance optimizations",
    baseProfile: "default",
    useFlags: @[
      UseFlag(name: "lto", enabled: true, category: "optimization"),
      UseFlag(name: "pgo", enabled: true, category: "optimization")
    ],
    compilerFlags: CompilerFlags(
      cflags: "-O3 -march=native -flto -pipe",
      cxxflags: "-O3 -march=native -flto -pipe",
      ldflags: "-Wl,-O1 -Wl,--as-needed -flto",
      makeflags: "-j8"
    )
  )

  result.profiles["desktop"] = BuildProfile(
    name: "desktop",
    description: "Desktop system with GUI support",
    baseProfile: "default",
    useFlags: @[
      UseFlag(name: "wayland", enabled: true, category: "gui"),
      UseFlag(name: "gtk", enabled: true, category: "gui"),
      UseFlag(name: "pulseaudio", enabled: true, category: "audio"),
      UseFlag(name: "dbus", enabled: true, category: "features")
    ],
    compilerFlags: CompilerFlags(
      cflags: "-O2 -pipe",
      cxxflags: "-O2 -pipe",
      ldflags: "-Wl,-O1",
      makeflags: "-j4"
    )
  )

proc parseConfigLine(line: string): tuple[key: string, value: string] =
  ## Parse a single configuration line
  let trimmed = line.strip()

  # Skip comments and empty lines
  if trimmed.len == 0 or trimmed.startsWith("#"):
    return ("", "")

  # Split on first =
  let parts = trimmed.split('=', 1)
  if parts.len != 2:
    return ("", "")

  result.key = parts[0].strip()
  result.value = parts[1].strip()

  # Remove quotes if present
  if result.value.startsWith('"') and result.value.endsWith('"'):
    result.value = result.value[1..^2]

proc parseConfigFile*(path: string): NipConfig =
  ## Parse configuration file
  result = defaultConfig()

  if not fileExists(path):
    return

  try:
    let content = readFile(path)
    for line in content.splitLines():
      let (key, value) = parseConfigLine(line)
      if key == "":
        continue

      case key
      of "programs-dir", "programs_dir":
        result.programsDir = value
      of "links-dir", "links_dir":
        result.linksDir = value
      of "cache-dir", "cache_dir":
        result.cacheDir = value
      of "db-file", "db_file":
        result.dbFile = value
      of "auto-symlink", "auto_symlink":
        result.autoSymlink = value.toLower() in ["true", "yes", "1"]
      of "check-conflicts", "check_conflicts":
        result.checkConflicts = value.toLower() in ["true", "yes", "1"]
      of "verbose":
        result.verbose = value.toLower() in ["true", "yes", "1"]
      of "nix-enabled", "nix_enabled":
        if result.adapters.hasKey("nix"):
          result.adapters["nix"].enabled = value.toLower() in ["true", "yes", "1"]
      of "nix-priority", "nix_priority":
        if result.adapters.hasKey("nix"):
          try:
            result.adapters["nix"].priority = parseInt(value)
          except:
            discard
      of "pkgsrc-enabled", "pkgsrc_enabled":
        if result.adapters.hasKey("pkgsrc"):
          result.adapters["pkgsrc"].enabled = value.toLower() in ["true", "yes", "1"]
      of "pkgsrc-priority", "pkgsrc_priority":
        if result.adapters.hasKey("pkgsrc"):
          try:
            result.adapters["pkgsrc"].priority = parseInt(value)
          except:
            discard
      of "pacman-enabled", "pacman_enabled":
        if result.adapters.hasKey("pacman"):
          result.adapters["pacman"].enabled = value.toLower() in ["true", "yes", "1"]
      of "pacman-priority", "pacman_priority":
        if result.adapters.hasKey("pacman"):
          try:
            result.adapters["pacman"].priority = parseInt(value)
          except:
            discard
      # Variant system configuration (Task 13.2)
      of "default-toolchain", "default_toolchain":
        result.defaultToolchain = value
      of "default-target", "default_target":
        result.defaultTarget = value
      of "profile-search-paths", "profile_search_paths":
        # Parse comma-separated or colon-separated paths
        let separator = if ':' in value: ':' else: ','
        result.profileSearchPaths = @[]
        for path in value.split(separator):
          let trimmedPath = path.strip()
          if trimmedPath.len > 0:
            result.profileSearchPaths.add(trimmedPath)
      else:
        discard

  except IOError:
    echo fmt"Warning: Could not read config file: {path}"

proc loadConfig*(): NipConfig =
  ## Load configuration from system and user config files
  result = defaultConfig()

  # Load global config
  let globalConfig = "/etc/nip/nip.conf"
  if fileExists(globalConfig):
    result = parseConfigFile(globalConfig)

  # Load user config (overrides global)
  # Check both ~/.nip/config (simple) and XDG location
  let simpleUserConfig = getHomeDir() / ".nip" / "config"
  let xdgConfigHome = getEnv("XDG_CONFIG_HOME", getHomeDir() / ".config")
  let xdgUserConfig = xdgConfigHome / "nip" / "config"

  # Prefer simple ~/.nip/config, fall back to XDG
  let userConfig = if fileExists(simpleUserConfig): simpleUserConfig else: xdgUserConfig

  if fileExists(userConfig):
    let userCfg = parseConfigFile(userConfig)

    # Merge user config (user settings override global)
    if userCfg.programsDir != defaultConfig().programsDir:
      result.programsDir = userCfg.programsDir
    if userCfg.linksDir != defaultConfig().linksDir:
      result.linksDir = userCfg.linksDir
    if userCfg.cacheDir != defaultConfig().cacheDir:
      result.cacheDir = userCfg.cacheDir
    if userCfg.dbFile != defaultConfig().dbFile:
      result.dbFile = userCfg.dbFile

    result.autoSymlink = userCfg.autoSymlink
    result.checkConflicts = userCfg.checkConflicts
    result.verbose = userCfg.verbose

    # Merge adapter configs
    for name, cfg in userCfg.adapters:
      result.adapters[name] = cfg

    # Merge variant system settings (Task 13.2)
    if userCfg.defaultToolchain != defaultConfig().defaultToolchain:
      result.defaultToolchain = userCfg.defaultToolchain
    if userCfg.defaultTarget != defaultConfig().defaultTarget:
      result.defaultTarget = userCfg.defaultTarget
    if userCfg.profileSearchPaths.len > 0 and userCfg.profileSearchPaths != defaultConfig().profileSearchPaths:
      result.profileSearchPaths = userCfg.profileSearchPaths

proc generateExampleConfig*(): string =
  ## Generate an example configuration file
  ## Follows XDG Base Directory specification
  let isRoot = getuid() == 0
  let homeDir = getHomeDir()

  # Generate appropriate example based on user context
  let (programsDir, linksDir, cacheDir, dbFile, profilePaths) =
    if isRoot:
      ("/Programs",
       "/System/Links",
       "/var/cache/nip",
       "/var/lib/nip/packages.json",
       "/etc/nip/profiles")
    else:
      let xdgDataHome = getEnv("XDG_DATA_HOME", homeDir / ".local" / "share")
      let xdgConfigHome = getEnv("XDG_CONFIG_HOME", homeDir / ".config")
      let xdgCacheHome = getEnv("XDG_CACHE_HOME", homeDir / ".cache")
      (xdgDataHome / "nip" / "Programs",
       xdgDataHome / "nip" / "Links",
       xdgCacheHome / "nip",
       xdgDataHome / "nip" / "db" / "packages.json",
       xdgConfigHome / "nip" / "profiles")

  let userType = if isRoot: "root (system-wide)" else: "user (XDG-compliant local)"

  result = fmt"""# NIP Configuration File
# Simple key-value format
# Auto-detected: Running as {userType}

# Directory Configuration (XDG Base Directory compliant)
programs-dir = "{programsDir}"
links-dir = "{linksDir}"
cache-dir = "{cacheDir}"
db-file = "{dbFile}"

# Options
auto-symlink = true
check-conflicts = true
verbose = false

# Adapter Configuration
# Lower priority number = tried first

# Nix Adapter
nix-enabled = true
nix-priority = 10

# PKGSRC Adapter
pkgsrc-enabled = true
pkgsrc-priority = 20

# Pacman Adapter
pacman-enabled = true
pacman-priority = 30

# Variant System Configuration (Task 13.2)
# Default toolchain for building packages
default-toolchain = "gcc-13.2.0"

# Default target architecture and OS
default-target = "x86_64-linux"

# Paths to search for variant profile files (comma or colon separated)
profile-search-paths = "{profilePaths}"
"""

proc saveExampleConfig*(path: string): bool =
  ## Save example configuration to file
  try:
    let dir = parentDir(path)
    if not dirExists(dir):
      createDir(dir)

    writeFile(path, generateExampleConfig())
    echo fmt"✅ Created example config at: {path}"
    return true
  except:
    echo fmt"❌ Failed to create config at: {path}"
    return false
