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-dotenv" [] {
    std assert ($env.TEST_LOADING_DOT_ENV == "1")
}

def "nur test-broken-dotenv" [] {
    assert exit-code { run-nur --dotenv=.broken-env } 1
}

def "nur test-missing-dotenv" [] {
    assert exit-code { run-nur --dotenv=.missing-env } 1
}

def "nur test-dotenv-is-a-directory" [] {
    assert exit-code { run-nur --dotenv=dotenv-dir } 1
}

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
    } 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 } 1
    assert exit-code { run-nur --commands some-command some-task-name } 1
    assert exit-code { run-nur --enter-shell some-task-nam } 1
    assert exit-code { run-nur --commands some-command --enter-shell } 1
}

def "nur do-test-preserve-exit-code" [] { exit 123 }
def "nur test-preserve-exit-code" [] {
    assert exit-code { run-nur do-test-preserve-exit-code } 123
}

def "nur test-nur-list" [] {
    let nur_list = (run-nur --list | lines)
    std assert ($nur_list | is-not-empty)
}

# 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 | into int) == $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 | where { |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 nur_call = (run-nur $it.1 | complete)
            let was_ok: bool = ($nur_call.exit_code == 0)
            if $was_ok {
                print $'...(ansi green)ok(ansi reset)'
            } else {
                print $'...(ansi red)failed(ansi reset)'
                print $nur_call
            }

            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 ", ")'}
    }
}
