# Image state management — read/write role image state from ~/.config/provisioning/images/

use ../lib_provisioning/utils/nickel_processor.nu [ncl-eval-soft]

export def image-state-path [provider: string, role: string]: nothing -> string {
    let dir = ($env.HOME | path join ".config" | path join "provisioning" | path join "images")
    $dir | path join $"($provider)-($role).ncl"
}

export def image-state-dir []: nothing -> string {
    $env.HOME | path join ".config" | path join "provisioning" | path join "images"
}

# Read state file. Returns a record with ImageRoleState fields.
# If the file does not exist, returns a pending-state record.
export def image-state-read [provider: string, role: string]: nothing -> record {
    let path = (image-state-path $provider $role)
    if not ($path | path exists) {
        return {
            provider:    $provider,
            role:        $role,
            snapshot_id: "SNAPSHOT_PENDING",
            built_at:    null,
            last_used:   null,
            os_base:     "unknown",
            labels:      {},
        }
    }
    let result = (ncl-eval-soft $path [] (error make { msg: $"Failed to parse image state ($path)" }))
    $result
}

# Write state file as a Nickel record literal.
export def image-state-write [provider: string, role: string, state: record]: nothing -> nothing {
    let dir  = (image-state-dir)
    let path = (image-state-path $provider $role)
    if not ($dir | path exists) {
        ^mkdir -p $dir
    }
    let built_at_val  = if ($state.built_at? | is-empty) { "null" } else { $"\"($state.built_at)\"" }
    let last_used_val = if ($state.last_used? | is-empty) { "null" } else { $"\"($state.last_used)\"" }
    let labels_str = (
        $state.labels?
        | default {}
        | items {|k, v| $"  ($k) = \"($v)\"," }
        | str join "\n"
    )
    let content = $"
\{
  provider    = \"($state.provider)\",
  role        = \"($state.role)\",
  snapshot_id = \"($state.snapshot_id)\",
  built_at    = ($built_at_val),
  last_used   = ($last_used_val),
  os_base     = \"($state.os_base | default "unknown")\",
  labels      = \{
($labels_str)
  \},
\}
" | str trim
    $content | save --force $path
}

# List state files. Optionally filter by provider.
export def image-state-list [--provider: string = ""]: nothing -> list<record> {
    let dir = (image-state-dir)
    if not ($dir | path exists) {
        return []
    }
    let files = (ls $dir | where name =~ '\.ncl$' | get name)
    let states = ($files | each {|f|
        ncl-eval-soft $f [] null
    } | where { $in != null })
    if ($provider | is-empty) {
        $states
    } else {
        $states | where provider == $provider
    }
}

# Returns true if the snapshot exists and is within freshness_days of built_at.
export def image-state-is-fresh [provider: string, role: string]: nothing -> bool {
    let state = (image-state-read $provider $role)
    if $state.snapshot_id == "SNAPSHOT_PENDING" { return false }
    if ($state.built_at | is-empty) { return false }
    let freshness_days = 30
    let built = ($state.built_at | into datetime)
    let age_days = ((date now) - $built | into duration | $in / 1day)
    $age_days <= $freshness_days
}

# Update only the snapshot_id and built_at fields in an existing state file.
export def image-state-set-snapshot [provider: string, role: string, snapshot_id: string]: nothing -> nothing {
    let existing = (image-state-read $provider $role)
    let updated = ($existing | merge {
        snapshot_id: $snapshot_id,
        built_at:    ((date now) | format date "%Y-%m-%dT%H:%M:%SZ"),
    })
    image-state-write $provider $role $updated
}

# Touch last_used timestamp for the given role state.
export def image-state-touch-used [provider: string, role: string]: nothing -> nothing {
    let existing = (image-state-read $provider $role)
    let updated = ($existing | merge {
        last_used: ((date now) | format date "%Y-%m-%dT%H:%M:%SZ"),
    })
    image-state-write $provider $role $updated
}
