#include <spdlog/spdlog.h>
#include "./references.hpp"
#include "../../protocol/transform/facade.hpp"
#include "../../service/document.hpp"
#include "../../service/symbol.hpp"

namespace lsp::provider::text_document
{
    std::string References::GetMethod() const
    {
        return "textDocument/references";
    }

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

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

        if (!request.params.has_value())
        {
            spdlog::warn("{}: Missing params in request", GetProviderName());
            return BuildErrorResponseMessage(request, protocol::ErrorCodes::InvalidParams, "Missing params");
        }

        protocol::ReferenceParams params =
            transform::As<protocol::ReferenceParams>(request.params.value());

        auto locations = BuildReferencesResponse(params, context);

        protocol::ResponseMessage response;
        response.id = request.id;

        if (!locations.empty())
            response.result = transform::LSPAny(locations);
        else
            response.result = transform::LSPAny(std::vector<protocol::Location>{});

        std::optional<std::string> json = transform::Serialize(response);
        if (!json.has_value())
            return BuildErrorResponseMessage(request, protocol::ErrorCodes::InternalError, "Failed to serialize response");
        return json.value();
    }

    std::vector<protocol::Location> References::BuildReferencesResponse(const protocol::ReferenceParams& params, ExecutionContext& context)
    {
        spdlog::trace("{}: Processing references request for URI='{}', Position=({}, {})",
                      GetProviderName(),
                      params.textDocument.uri,
                      params.position.line,
                      params.position.character);

        std::string identifier = GetIdentifierAtPosition(
            params.textDocument.uri, params.position, context);

        if (identifier.empty())
        {
            spdlog::info("{}: No identifier at position", GetProviderName());
            return {};
        }

        spdlog::debug("{}: Looking for references of '{}'", GetProviderName(), identifier);

        auto locations = FindReferences(params.textDocument.uri, identifier, params.context.includeDeclaration, context);
        spdlog::info("{}: Found {} references", GetProviderName(), locations.size());

        return locations;
    }

    std::vector<protocol::Location> References::FindReferences(const protocol::DocumentUri& uri, const std::string& identifier, bool include_declaration, ExecutionContext& context)
    {
        std::vector<protocol::Location> locations;

        // 从容器获取文档服务
        auto& document_service = context.GetService<service::Document>();

        auto content = document_service.GetContent(uri);
        auto tree = document_service.GetSyntaxTree(uri);

        if (!content.has_value() || !tree)
        {
            spdlog::warn("{}: Document not found or no syntax tree: {}", GetProviderName(), uri);
            return locations;
        }

        TSNode root = ts_tree_root_node(tree);
        FindReferencesInNode(root, identifier, *content, uri, locations, include_declaration);

        return locations;
    }

    std::string References::GetIdentifierAtPosition(const protocol::DocumentUri& uri, const protocol::Position& position, ExecutionContext& context)
    {
        auto& document_service = context.GetService<service::Document>();

        auto content = document_service.GetContent(uri);
        auto tree = document_service.GetSyntaxTree(uri);

        if (!content.has_value() || !tree)
            return "";

        size_t byte_offset = 0;
        size_t current_line = 0;
        size_t current_col = 0;

        for (size_t i = 0; i < content->length(); i++)
        {
            if (current_line == position.line && current_col == position.character)
            {
                byte_offset = i;
                break;
            }

            if ((*content)[i] == '\n')
            {
                current_line++;
                current_col = 0;
            }
            else
            {
                current_col++;
            }
        }

        TSNode root = ts_tree_root_node(tree);
        TSNode node = ts_node_descendant_for_byte_range(root, byte_offset, byte_offset);

        while (!ts_node_is_null(node))
        {
            const char* node_type = ts_node_type(node);
            if (strcmp(node_type, kIdentifier) == 0)
            {
                uint32_t start = ts_node_start_byte(node);
                uint32_t end = ts_node_end_byte(node);
                return content->substr(start, end - start);
            }
            node = ts_node_parent(node);
        }

        return "";
    }
}
