use std

let nurcmd = "./../target/release/nur"

# Tests

def "nur test-env" [] {
    std assert (($env.PWD | path join ".nur" "scripts") in $env.NU_LIB_DIRS)
    std assert ($env.NUR_VERSION | is-not-empty)
    std assert ($env.NUR_TASK_NAME | is-not-empty)
    std assert ($env.NUR_TASK_CALL | is-not-empty)
}

def "nur test-config" [] {
    try {
        $env.config
    } catch {
        error make {"msg": "Config does not exist"}
    }
}

def "nur test-nu" [] {
    std assert ($nu.config-path == ($env.PWD | path join ".nur" "config.nu"))
    std assert ($nu.env-path == ($env.PWD | path join ".nur" "env.nu"))
    if (is-windows) {
        std assert (($nu.current-exe | path basename) == "nur.exe")
    } else {
        std assert (($nu.current-exe | path basename) == "nur")
    }
}

def "nur test-nur" [] {
    std assert ($nur.task-name == "test-nur")
    std assert ($nur.run-path == $env.PWD)
    std assert ($nur.project-path == $env.PWD)
    std assert ($nur.default-lib-dir == ($env.PWD | path join ".nur" "scripts"))
}

def "nur exec-stdin" [] {
    lines | each { |it| print $"BEFORE ($it) AFTER" }
}
def "nur test-stdin" [] {
    std assert ("inner" | run-nur --stdin exec-stdin | str contains "BEFORE inner AFTER")
}

def "nur do-failed-execution" [] {
    if (is-windows) {
        cmd /c "exit 1"
    } else {
        ^false
    }
}
def "nur test-failed-execution" [] {
    try {
        run-nur do-failed-execution
    } catch {
        return  # all ok
    }
    error make {"msg": "Did not fail, this is an error"}
}

def "nur do-invalid-executable" [] {
    ^does-not-exist-at-all-will-not-exist-ever
}
def "nur test-invalid-executable" [] {
    try {
        run-nur do-invalid-executable o+e>| ignore
    } catch {
        return  # all ok
    }
    error make {"msg": "Did not fail, this is an error"}
}

def "nur do-sub-task" [] { print "ok" }
def "nur do-sub-task sub" [] { print "sub-ok" }
def "nur test-sub-task" [] {
    std assert ((run-nur do-sub-task) == "ok")
    std assert ((run-nur do-sub-task sub) == "sub-ok")
}

def "nur do-sub-task-without-parent sub" [] { print "sub-ok" }
def "nur test-sub-task-without-parent" [] {
    std assert ((run-nur do-sub-task-without-parent sub) == "sub-ok")
}

def --wrapped "nur do-sub-task-with-any-args sub" [...args] { print "sub-ok" }
def "nur test-sub-task-with-any-args" [] {
    std assert ((run-nur do-sub-task-with-any-args sub) == "sub-ok")
    std assert ((run-nur do-sub-task-with-any-args sub --foo bar bla) == "sub-ok")
    std assert ((run-nur do-sub-task-with-any-args sub some random args) == "sub-ok")
}

def "nur test-running-commands" [] {
    std assert ((run-nur --commands "print 'ok'") == "ok")
    std assert ((run-nur --commands "print $nurcmd") == $nurcmd)
}

def "nur test-invalid-calls" [] {
    assert exit-code { run-nur non-existing-task o+e>| ignore } 1
    assert exit-code { run-nur --commands some-command some-task-name o+e>| ignore } 1
    assert exit-code { run-nur --enter-shell some-task-name o+e>| ignore } 1
    assert exit-code { run-nur --commands some-command --enter-shell o+e>| ignore } 1
}

# Utils and other commands

def is-windows [] {
    $nu.os-info.name == 'windows'
}

def "assert exit-code" [
    code: closure,
    exit_code: int,
] {
    try { do $code } catch { null }
    std assert ($env.LAST_EXIT_CODE == $exit_code) $"Expected exit code ($exit_code), but got ($env.LAST_EXIT_CODE)" --error-label {
        span: {
            start: (metadata $code).span.start
            end: (metadata $code).span.end
        }
        text: $"Did not return expected exit code ($exit_code)"
    }
}

def --wrapped run-nur [
    ...args
] {
    ^$nurcmd --quiet ...$args
}

def "nur prepare" [] {
    cargo build --release
}

def "nur run-all" [] {
    let tests = (scope commands | filter { |it| $it.name starts-with 'nur test-' } | each { |it| $it.name | split row ' ' })

    let failed_tests = (
        $tests | each {
            |it|
            $it | str join " " | print -n
            let was_ok: bool = try {
                run-nur $it.1 o+e>| ignore
                print $'...(ansi green)ok(ansi reset)'
                true  # mark as ok
            } catch {
                print $'...(ansi red)failed(ansi reset)'
                false  # mark as failed
            }

            if $was_ok { null } else { $it.1 }
        }
    )

    if ($failed_tests | is-not-empty) {
        error make {"msg": $'Some tests did fail, please fix those: ($failed_tests | str join ", ")'}
    }
}
