/**
 * Copyright (c) 2021-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 <memory>
#include "public/es2panda_lib.h"
#include "public/public.h"
#include "declgenEts2Ts.h"
#include "ir/astNodeHistory.h"
#include "isolatedDeclgenChecker.h"

namespace ark::es2panda::declgen_ets2ts {

static void PrintUsage()
{
    std::cerr << "Usage: declgen_ets2ts [OPTIONS] [input]\n";
    std::cerr << "    --export-all         Treat all top level statements as exported\n";
    std::cerr << "    --output-dets=[FILE] Path to output .d.ets file\n";
    std::cerr << "    --output-ets=[FILE]  Path to output .ets file\n";
    std::cerr << "    --help               Print this message and exit. Default: false\n";
    std::cerr << "Tail arguments:\n";
    std::cerr << "input: input file\n";
}

void FilterArgs(Span<const char *const> args, int &newArgc, const char **&newArgv)
{
    ES2PANDA_ASSERT(args.size() > 1);
    std::vector<const char *> filteredArgs;
    filteredArgs.push_back(args[0]);
    for (size_t i = 1; i < args.size(); ++i) {
        if (std::strcmp(args[i], "--export-all") == 0 ||
            std::strncmp(args[i], "--output-dets", std::strlen("--output-dets")) == 0 ||
            std::strncmp(args[i], "--output-ets", std::strlen("--output-ets")) == 0) {
            continue;
        }
        filteredArgs.push_back(args[i]);
    }

    newArgc = static_cast<int>(filteredArgs.size());
    if (newArgc <= 0 || static_cast<size_t>(newArgc) > args.size()) {
        return;
    }
    newArgv = new const char *[newArgc];
    std::copy(filteredArgs.begin(), filteredArgs.end(), newArgv);
}

static DeclgenOptions ParseOptions(Span<const char *const> args)
{
    DeclgenOptions options;
    for (size_t i = 1; i < args.size(); ++i) {
        if (std::strcmp(args[i], "--export-all") == 0) {
            options.exportAll = true;
        } else if (std::strncmp(args[i], "--output-dets", std::strlen("--output-dets")) == 0 &&
                   std::strchr(args[i], '=') != nullptr) {
            std::string arg = args[i];
            options.outputDeclEts = arg.substr(std::strlen("--output-dets="));
        } else if (std::strncmp(args[i], "--output-ets", std::strlen("--output-ets")) == 0 &&
                   std::strchr(args[i], '=') != nullptr) {
            std::string arg = args[i];
            options.outputEts = arg.substr(std::strlen("--output-ets="));
        }
    }
    return options;
}

static int Run(int argc, const char **argv)
{
    Span<const char *const> args(argv, static_cast<size_t>(argc));
    if (args.size() == 1 || std::strcmp(args[1], "--help") == 0) {
        PrintUsage();
        return 1;
    }
    auto declgenOptions = ParseOptions(args);
    int newArgc = 0;
    const char **newArgv = nullptr;
    FilterArgs(args, newArgc, newArgv);
    const auto *impl = es2panda_GetImpl(ES2PANDA_LIB_VERSION);
    auto *cfg = impl->CreateConfig(newArgc, newArgv);
    if (cfg == nullptr) {
        return 1;
    }
    auto *cfgImpl = reinterpret_cast<ark::es2panda::public_lib::ConfigImpl *>(cfg);
    auto parserInputCStr = cfgImpl->options->CStrParserInputContents().first;
    es2panda_Context *ctx =
        impl->CreateContextFromStringWithHistory(cfg, parserInputCStr, cfgImpl->options->SourceFileName().c_str());
    auto *ctxImpl = reinterpret_cast<ark::es2panda::public_lib::Context *>(ctx);
    ctxImpl->lazyCheck = false;

    impl->ProceedToState(ctx, ES2PANDA_STATE_PARSED);

    TSDeclGenerator *declgen = new TSDeclGenerator(ctxImpl);

    auto destroyContextAndDeclgen = [declgen, impl, ctx, cfg, newArgv]() {
        delete declgen;
        impl->DestroyContext(ctx);
        impl->DestroyConfig(cfg);
        delete[] newArgv;
    };

    if (!declgen->SetDeclgenOptions(declgenOptions)) {
        destroyContextAndDeclgen();
        return 1;
    }

    if (!declgen->GenerateTsDeclarationsAfterParsedPhase()) {
        destroyContextAndDeclgen();
        return 1;
    }

    impl->ProceedToState(ctx, ES2PANDA_STATE_CHECKED);

    if (!declgen->GenerateTsDeclarationsAfterCheckPhase()) {
        destroyContextAndDeclgen();
        return 1;
    }

    if (!declgen->Write()) {
        destroyContextAndDeclgen();
        return 1;
    }

    destroyContextAndDeclgen();
    return 0;
}
}  // namespace ark::es2panda::declgen_ets2ts

int main(int argc, const char **argv)
{
    return ark::es2panda::declgen_ets2ts::Run(argc, argv);
}