#include <spdlog/spdlog.h>
#include <chrono>
#include <filesystem>
#include "../language/ast/deserializer.hpp"
#include "./detail/symbol/conversion.hpp"
#include "./parser.hpp"
#include "./symbol.hpp"

namespace lsp::service
{
    Symbol::Symbol(std::shared_ptr<EventBus> event_bus) :
        event_bus_(std::move(event_bus))
    {
        // 订阅文档事件
        event_bus_->Subscribe<events::DocumentParsed>(
            [this](const auto& e) { OnDocumentParsed(e); });

        event_bus_->Subscribe<events::DocumentReparsed>(
            [this](const auto& e) { OnDocumentReparsed(e); });

        event_bus_->Subscribe<events::DocumentClosed>(
            [this](const auto& e) { OnDocumentClosed(e); });
    }

    Symbol::~Symbol() = default;

    // ==================== 符号加载 ====================

    void Symbol::LoadSystemSymbols(const std::string& folder)
    {
        spdlog::info("Loading system symbols from: {}", folder);
        auto start = std::chrono::steady_clock::now();

        if (!std::filesystem::exists(folder))
        {
            spdlog::warn("System folder does not exist: {}", folder);
            return;
        }

        size_t loaded = 0;
        size_t failed = 0;

        auto options = std::filesystem::directory_options::follow_directory_symlink |
                       std::filesystem::directory_options::skip_permission_denied;

        for (const auto& entry : std::filesystem::recursive_directory_iterator(folder, options))
        {
            try
            {
                // 使用 Parser 解析文件
                auto table_opt = Parser::ParseTsfFile(entry.path().string());
                if (!table_opt)
                {
                    failed++;
                    continue;
                }

                auto& table = *table_opt;

                // 确保符号表存在
                if (!table.symbol_table)
                {
                    spdlog::warn("Symbol table is null for: {}", entry.path().string());
                    failed++;
                    continue;
                }

                // 提取系统索引
                symbol::SystemSymbolIndex system_index = symbol::BuildFromEditingTable(table);

                system_repo_.Add(std::move(system_index));
                loaded++;

                if (loaded % 100 == 0)
                {
                    spdlog::debug("Loaded {} system symbols", loaded);
                }
            }
            catch (const std::exception& e)
            {
                spdlog::error("Exception loading system symbol {}: {}", entry.path().string(), e.what());
                failed++;
            }
        }

        auto duration = std::chrono::duration_cast<std::chrono::milliseconds>( std::chrono::steady_clock::now() - start) .count();
        spdlog::info("System symbols loaded: {} files, {} failed, {}ms", loaded, failed, duration);
    }

    void Symbol::LoadWorkspaceSymbols(const std::string& folder)
    {
        spdlog::info("Loading workspace symbols from: {}", folder);
        auto start = std::chrono::steady_clock::now();

        if (!std::filesystem::exists(folder))
        {
            spdlog::warn("Workspace folder does not exist: {}", folder);
            return;
        }

        size_t loaded = 0;
        size_t failed = 0;

        auto options = std::filesystem::directory_options::follow_directory_symlink |
                       std::filesystem::directory_options::skip_permission_denied;

        for (const auto& entry : std::filesystem::recursive_directory_iterator(folder, options))
        {
            if (!entry.is_regular_file() || entry.path().extension() != ".tsf")
                continue;

            try
            {
                // 使用 Parser 解析文件
                auto table_opt = Parser::ParseTsfFile(entry.path().string());
                if (!table_opt)
                {
                    failed++;
                    continue;
                }

                auto& table = *table_opt;

                // 确保符号表存在
                if (!table.symbol_table)
                {
                    spdlog::warn("Symbol table is null for: {}", entry.path().string());
                    failed++;
                    continue;
                }

                // 提取文件索引
                symbol::FileSymbolIndex file_index = symbol::BuildFromEditingTable(table);

                workspace_repo_.Add(std::move(file_index));
                loaded++;

                if (loaded % 100 == 0)
                    spdlog::debug("Loaded {} workspace symbols", loaded);
            }
            catch (const std::exception& e)
            {
                spdlog::error("Exception loading workspace symbol {}: {}", entry.path().string(), e.what());
                failed++;
            }
        }

        auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(
                            std::chrono::steady_clock::now() - start)
                            .count();

        spdlog::info("Workspace symbols loaded: {} files, {} failed, {}ms", loaded, failed, duration);
    }

    void Symbol::ReloadWorkspaceSymbols(const std::string& folder)
    {
        spdlog::info("Reloading workspace symbols");

        // 清空现有符号
        workspace_repo_.Clear();

        // 重新加载
        LoadWorkspaceSymbols(folder);
    }

    const symbol::repository::System& Symbol::SystemRepo() const
    {
        return system_repo_;
    }

    const symbol::repository::Workspace& Symbol::WorkspaceRepo() const
    {
        return workspace_repo_;
    }

    const symbol::repository::Editing& Symbol::EditingRepo() const
    {
        return editing_repo_;
    }

    // ==================== 事件处理 ====================

    void Symbol::OnDocumentParsed(const events::DocumentParsed& event)
    {
        if (!event.tree)
        {
            spdlog::warn("Received null tree for document: {}", event.item.uri);
            return;
        }

        // 创建 EditingSymbolTable
        symbol::EditingSymbolTable table;
        table.uri = event.item.uri;
        table.version = event.item.version;
        table.last_parsed = std::chrono::system_clock::now();
        table.is_dirty = false;
        table.deserializer = std::make_unique<language::ast::Deserializer>();

        // 使用 AST 反序列化器解析文档
        auto ast_result = table.deserializer->Parse(ts_tree_root_node(event.tree), event.item.text);
        if (!ast_result.IsSuccess())
        {
            spdlog::error("Failed to deserialize AST for: {}", event.item.uri);
            return;
        }

        table.ast_root = std::move(ast_result.root);

        // 确定文件类型 (根据 AST root 的 Program 内容判断)
        symbol::TsfFileType file_type = symbol::TsfFileType::kScript;
        if (table.ast_root)
        {
            auto& program = *table.ast_root;
            if (!program.statements.empty())
            {
                auto& first_stmt = program.statements[0];
                if (auto* unit_def = dynamic_cast<language::ast::UnitDefinition*>(first_stmt.get()))
                {
                    file_type = symbol::TsfFileType::kUnit;
                }
                else if (auto* class_def = dynamic_cast<language::ast::ClassDefinition*>(first_stmt.get()))
                {
                    file_type = symbol::TsfFileType::kClass;
                }
                else if (auto* func_def = dynamic_cast<language::ast::FunctionDefinition*>(first_stmt.get()))
                {
                    file_type = symbol::TsfFileType::kFunction;
                }
            }
        }
        table.file_type = file_type;

        // 创建符号表并构建
        table.symbol_table = std::make_unique<language::symbol::SymbolTable>();
        table.symbol_table->Build(*table.ast_root);
        // 添加到编辑仓库
        editing_repo_.AddOrUpdate(std::move(table));

        spdlog::debug("Document parsed and added to editing repo: {}", event.item.uri);
    }

    void Symbol::OnDocumentReparsed(const events::DocumentReparsed& event)
    {
        spdlog::debug("OnDocumentReparsed");
    }

    void Symbol::OnDocumentClosed(const events::DocumentClosed& event)
    {
        editing_repo_.Remove(event.textDocument.uri);
        spdlog::debug("Document closed and removed from editing repo: {}", event.textDocument.uri);
    }
}
