/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved.
 * This source file is part of the Cangjie project, licensed under Apache-2.0
 * with Runtime Library Exception.
 *
 * See https://cangjie-lang.cn/pages/LICENSE for license information.
 */

// The Cangjie API is in Beta. For details on its capabilities and limitations, please refer to the README file.

package std.unittest

import std.regex.Regex
import std.convert.Parsable

let OP_TIMEOUT_EACH_NAME = camelCaseToKebabCase(KeyTimeoutEach().name)
let OP_TIMEOUT_EACH = "--${OP_TIMEOUT_EACH_NAME}"
let TIMEOUT_EACH_KEY = kebabCaseToCamelCase(OP_TIMEOUT_EACH_NAME)
let VALUE_TIMEOUT_PATTERN: Regex = Regex("(\\d+(\\.\\d+)?)(millis|s|m|h)")

extend Configuration {
    prop timeout: ?Duration {
        get() {
            get(KeyTimeout.timeout)
        }
    }

    prop timeoutHandler: (TestCaseInfo) -> Unit {
        get() {
            get(KeyTimeoutHandler.timeoutHandler) ?? { _: TestCaseInfo => () }
        }
    }
}

func cancellationPoint() {
    if (Thread.currentThread.hasPendingCancellation) {
        throw UnittestTimeoutException()
    }
}

func fillTimeoutKey(configuration: Configuration): Unit {
    if (let Some(timeout) <- configuration.get(KeyTimeoutEach.timeoutEach)) {
        if (let Some(parsed) <- parseTimeout(timeout)) {
            configuration.set(KeyTimeout.timeout, parsed)
        } else {
            throw UnittestCliOptionsFormatException(
                OP_TIMEOUT_EACH,
                actual: timeout,
                expected: "NUMBER(millis|s|m|h) format is expected. Example: 3s."
            )
        }
    }
}

private func parseTimeout(raw: String): ?Duration {
    if (let Some(data) <- VALUE_TIMEOUT_PATTERN.matcher(raw).fullMatch()) {
        let value = Float64.parse(data.matchString(1))
        let duration = match (data.matchString(3)) {
            case "millis" => Duration.millisecond
            case "s" => Duration.second
            case "m" => Duration.minute
            case "h" => Duration.hour
            case _ => throw Exception("unreachable")
        }
        duration * value
    } else {
        None
    }
}