/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2024-2025. All rights reserved.
 */
macro package magic.dsl

import std.ast.*

class UserAttr <: Attr {
    private static let ATTR_NAMES = ["include"]

    UserAttr(map: AttrMap) { super(map) }

    init() { super(AttrMap()) }

    prop include: Option<String> {
        get() {
            getLiteral("include")
        }
    }

    static func parse(attrTokens: Tokens): UserAttr {
        let map = AttrParser(attrTokens).parseAttr(
            macroName: "user",
            validKeys: ["include"]
        )
        return UserAttr(map)
    }
}

public macro user(input: Tokens): Tokens {
    assertParentContext("agent")
    let userAttr = UserAttr()
    return transformUser(input, userAttr)
}

public macro user(attr: Tokens, input: Tokens): Tokens {
    assertParentContext("agent")
    let userAttr = UserAttr.parse(attr)
    let result = transformUser(input, userAttr)
    return result
}

/**
 * Transform
 * ```
 * @user( ... )
 * ```
 * as
 * override protected func USER_PROMPT_FUNC_NAME(): String {
 *   let PROMPT_BUILDER = StingBuilder()
 *   ...
 *   return $PROMPT_BUILDER.toString()
 * }
 */
func transformUser(input: Tokens, userAttr: UserAttr): Tokens {
    let funcName = newIdentifierToken("${USER_PROMPT_FUNC}")
    if (let Some(path) <- userAttr.include) {
        if (!exists(path)) {
            throw DslException("`include` refers to non-existing file: ${path}")
        }
        return quote(override protected func $funcName(): String {
            return PromptUtils.readPromptFile($(newLiteralToken(path)))
        })
    } else {
        let tokens = quote(override protected func $funcName(): String {
            let $PROMPT_BUILDER = StringBuilder()
            $input
            return $PROMPT_BUILDER.toString()
        })
        return transformStringLiteral(tokens, isDecl: true)
    }
}