#include <spdlog/spdlog.h>
#include "./initialize.hpp"
#include "../../service/symbol.hpp"
#include "../../scheduler/async_executor.hpp"
#include "../../protocol/transform/facade.hpp"

namespace lsp::provider
{
    std::string Initialize::GetMethod() const
    {
        return "initialize";
    }

    std::string Initialize::GetProviderName() const
    {
        return "Initialize";
    }

    std::string Initialize::ProvideResponse(const protocol::RequestMessage& request, ExecutionContext& context)
    {
        spdlog::debug("InitializeProvider: Providing response for method {}", request.method);

        protocol::InitializeParams params = transform::As<protocol::InitializeParams>(request.params.value());
        if (params.workspaceFolders)
            ProcessWorkspaceFolder(params.workspaceFolders.value(), context);

        protocol::ResponseMessage response;
        response.id = request.id;
        response.result = transform::LSPAny(BuildInitializeResult());
        std::optional<std::string> json = transform::Serialize(response);
        if (!json.has_value())
        {
            context.TriggerLifecycleEvent(ServerLifecycleEvent::kInitializeFailed);
            return BuildErrorResponseMessage(request, protocol::ErrorCodes::InternalError, "Internal error");
        }
        context.TriggerLifecycleEvent(ServerLifecycleEvent::kInitialized);
        return json.value();
    }

    protocol::InitializeResult Initialize::BuildInitializeResult()
    {
        protocol::InitializeResult result;
        result.serverInfo.name = "TSL Language Server";
        result.serverInfo.version = __DATE__;
        result.capabilities.textDocumentSync = BuildTextDocumentSyncOptions();
        result.capabilities.completionProvider = BuildCompletionOptions();
        // result.capabilities.semanticTokensProvider = BuildSemanticTokenOptions();
        return result;
    }

    protocol::TextDocumentSyncOptions Initialize::BuildTextDocumentSyncOptions()
    {
        protocol::TextDocumentSyncOptions options;
        options.openClose = true;
        options.change = protocol::TextDocumentSyncKind::Incremental;
        return options;
    }

    protocol::CompletionOptions Initialize::BuildCompletionOptions()
    {
        protocol::CompletionOptions options;
        options.triggerCharacters = { std::vector<std::string>{ ".", "(" } };
        options.resolveProvider = true;
        options.completionItem = { .labelDetailsSupport = true };
        return options;
    }

    protocol::SemanticTokensOptions Initialize::BuildSemanticTokenOptions()
    {
        protocol::SemanticTokensOptions options;
        options.legend.tokenTypes.emplace_back(protocol::SemanticTokenTypesLiterals::Namespace);
        options.legend.tokenTypes.emplace_back(protocol::SemanticTokenTypesLiterals::Type);
        options.legend.tokenTypes.emplace_back(protocol::SemanticTokenTypesLiterals::Class);
        options.legend.tokenTypes.emplace_back(protocol::SemanticTokenTypesLiterals::Enum);
        options.legend.tokenTypes.emplace_back(protocol::SemanticTokenTypesLiterals::Interface);
        options.legend.tokenTypes.emplace_back(protocol::SemanticTokenTypesLiterals::Struct);
        options.legend.tokenTypes.emplace_back(protocol::SemanticTokenTypesLiterals::TypeParameter);
        options.legend.tokenTypes.emplace_back(protocol::SemanticTokenTypesLiterals::Parameter);
        options.legend.tokenTypes.emplace_back(protocol::SemanticTokenTypesLiterals::Variable);
        options.legend.tokenTypes.emplace_back(protocol::SemanticTokenTypesLiterals::Property);
        options.legend.tokenTypes.emplace_back(protocol::SemanticTokenTypesLiterals::EnumMember);
        options.legend.tokenTypes.emplace_back(protocol::SemanticTokenTypesLiterals::Event);
        options.legend.tokenTypes.emplace_back(protocol::SemanticTokenTypesLiterals::Function);
        options.legend.tokenTypes.emplace_back(protocol::SemanticTokenTypesLiterals::Method);
        options.legend.tokenTypes.emplace_back(protocol::SemanticTokenTypesLiterals::Macro);
        options.legend.tokenTypes.emplace_back(protocol::SemanticTokenTypesLiterals::Keyword);
        options.legend.tokenTypes.emplace_back(protocol::SemanticTokenTypesLiterals::Modifier);
        options.legend.tokenTypes.emplace_back(protocol::SemanticTokenTypesLiterals::Comment);
        options.legend.tokenTypes.emplace_back(protocol::SemanticTokenTypesLiterals::String);
        options.legend.tokenTypes.emplace_back(protocol::SemanticTokenTypesLiterals::Number);
        options.legend.tokenTypes.emplace_back(protocol::SemanticTokenTypesLiterals::Regexp);
        options.legend.tokenTypes.emplace_back(protocol::SemanticTokenTypesLiterals::Operator);
        options.legend.tokenTypes.emplace_back(protocol::SemanticTokenTypesLiterals::Decorator);
        options.legend.tokenModifiers.emplace_back(protocol::SemanticTokenModifiersLiterals::Declaration);
        options.legend.tokenModifiers.emplace_back(protocol::SemanticTokenModifiersLiterals::Definition);
        options.legend.tokenModifiers.emplace_back(protocol::SemanticTokenModifiersLiterals::Readonly);
        options.legend.tokenModifiers.emplace_back(protocol::SemanticTokenModifiersLiterals::Static);
        options.legend.tokenModifiers.emplace_back(protocol::SemanticTokenModifiersLiterals::Deprecated);
        options.legend.tokenModifiers.emplace_back(protocol::SemanticTokenModifiersLiterals::Abstract);
        options.legend.tokenModifiers.emplace_back(protocol::SemanticTokenModifiersLiterals::Async);
        options.legend.tokenModifiers.emplace_back(protocol::SemanticTokenModifiersLiterals::Modification);
        options.legend.tokenModifiers.emplace_back(protocol::SemanticTokenModifiersLiterals::Documentation);
        options.legend.tokenModifiers.emplace_back(protocol::SemanticTokenModifiersLiterals::DefaultLibrary);
        options.range = true;
        options.full = protocol::SemanticTokensOptions::Full{ .delta = true };
        return options;
    }

    void Initialize::ProcessWorkspaceFolder(const std::vector<protocol::WorkspaceFolder>& workspace_folders, ExecutionContext& context)
    {
        auto symbol = context.GetService<service::Symbol>();
        auto& scheduler = context.GetScheduler();
        for (const auto& workspace_folder : workspace_folders)
        {
            std::string workspace_path = UriToPath(workspace_folder.uri);
            scheduler.Submit("Load workspace symbols", [symbol, workspace_path, folder_name = workspace_folder.name]() -> std::optional<std::string> {
                try
                {
                    symbol->LoadWorkspaceSymbols(workspace_path);

                    return fmt::format("Loaded workspace {} symbols", workspace_path);
                }
                catch (const std::exception& e)
                {
                    spdlog::error("Failed to load workspace {} symbols: {}", folder_name, e.what());
                    throw; // Request会处理异常
                } }, [](const std::string& result) { spdlog::info("Worksapce loading result:", result); });
        }

        spdlog::info("Initiated loading for {} workspace folder(s)", workspace_folders.size());
    }

    std::string Initialize::UriToPath(const protocol::DocumentUri& uri)
    {
        std::string path = uri;
        if (path.find("file://") == 0)
            path = path.substr(7);
#ifdef _WIN32
        if (path.length() > 0 && path[0] == '/')
            path = path.substr(1);
#endif
        return path;
    }
}
