/**
 * Copyright (c) 2025-2026 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "ETSparser.h"
#include "parser/parserStatusContext.h"
#include "lexer/lexer.h"
#include "ir/astNode.h"
#include "ir/ets/etsModule.h"
#include "util/errorRecovery.h"
#include "generated/diagnostic.h"

namespace ark::es2panda::parser {

using namespace std::literals::string_literals;

ir::ETSModule *ETSParser::ParseNamespaceStatement(ir::ModifierFlags memberModifiers)
{
    auto savedCtx = SavedStatusContext<ParserStatus::IN_NAMESPACE>(&GetContext());

    auto modifiers = ir::ModifierFlags::NONE;
    if (((memberModifiers & ir::ModifierFlags::EXPORT) != 0)) {
        modifiers |= ir::ModifierFlags::EXPORT;
    }
    if (((memberModifiers & ir::ModifierFlags::DEFAULT_EXPORT) != 0)) {
        modifiers |= ir::ModifierFlags::DEFAULT_EXPORT;
    }
    if ((memberModifiers & ir::ModifierFlags::DECLARE) != 0 || InAmbientContext()) {
        modifiers |= ir::ModifierFlags::DECLARE;
        GetContext().Status() |= ParserStatus::IN_AMBIENT_CONTEXT;
    }

    return ParseNamespace(modifiers)->AsETSModule();
}

ir::Statement *ETSParser::ParseNamespace(ir::ModifierFlags flags)
{
    if ((GetContext().Status() & ParserStatus::IN_NAMESPACE) == 0) {
        LogError(diagnostic::NAMESPACE_ONLY_TOP_OR_IN_NAMESPACE);
    }
    ir::ETSModule *ns = ParseNamespaceImp(flags);
    ES2PANDA_ASSERT(ns != nullptr);
    return ns;
}

ir::ETSModule *ETSParser::ParseNamespaceImp(ir::ModifierFlags flags)
{
    auto nsStart = Lexer()->GetToken().Start();
    Lexer()->NextToken();
    auto *result = AllocNode<ir::ETSModule>(Allocator(), ArenaVector<ir::Statement *>(Allocator()->Adapter()),
                                            ExpectIdentifier(), ir::ModuleFlag::NAMESPACE, GetContext().GetLanguage(),
                                            GetGlobalProgram());  // todo: why global?
    ir::ETSModule *parent = result;
    ir::ETSModule *child = nullptr;
    while (Lexer()->GetToken().Type() == lexer::TokenType::PUNCTUATOR_PERIOD) {
        Lexer()->NextToken();
        auto start = Lexer()->GetToken().Start();
        child = AllocNode<ir::ETSModule>(Allocator(), ArenaVector<ir::Statement *>(Allocator()->Adapter()),
                                         ExpectIdentifier(), ir::ModuleFlag::NAMESPACE, GetContext().GetLanguage(),
                                         GetGlobalProgram());  // todo: why global?
        child->SetParent(parent);
        child->SetRange({start, Lexer()->GetToken().Start()});
        child->AddModifier(ir::ModifierFlags::EXPORT);
        if ((flags & ir::ModifierFlags::DECLARE) != 0) {
            child->AddModifier(ir::ModifierFlags::DECLARE);
        }
        parent->AddStatement(child);
        parent = child;
    }
    auto statements = this->ParseNamespaceBlockStatements();
    auto nsEnd = Lexer()->GetToken().End();
    Lexer()->NextToken();
    if (child != nullptr) {
        child->SetNamespaceChainLastNode();
        child->SetStatements(std::move(statements));
    } else {
        result->SetNamespaceChainLastNode();
        result->SetStatements(std::move(statements));
    }
    result->AddModifier(flags);
    result->SetRange({nsStart, nsEnd});
    return result;
}

ArenaVector<ir::Statement *> ETSParser::ParseNamespaceBlockStatements()
{
    ExpectToken(lexer::TokenType::PUNCTUATOR_LEFT_BRACE);
    ArenaVector<ir::Statement *> statements(Allocator()->Adapter());
    while (Lexer()->GetToken().Type() != lexer::TokenType::PUNCTUATOR_RIGHT_BRACE) {
        util::ErrorRecursionGuard infiniteLoopBlocker(Lexer());
        if (Lexer()->GetToken().Type() == lexer::TokenType::EOS) {
            LogError(diagnostic::UNEXPECTED_TOKEN);
            break;
        }
        if (Lexer()->TryEatTokenType(lexer::TokenType::PUNCTUATOR_SEMI_COLON)) {
            continue;
        }
        auto status = GetContext().Status();
        auto st = ParseTopLevelStatement();
        GetContext().Status() = status;
        statements.emplace_back(st);
    }
    return statements;
}

}  // namespace ark::es2panda::parser