#include "config.h"

namespace humanus {

MCPServerConfig MCPServerConfig::load_from_toml(const toml::table &tool_table) {
    MCPServerConfig config;
    
    try {
        // Read type
        if (!tool_table.contains("type") || !tool_table["type"].is_string()) {
            throw std::runtime_error("Tool configuration missing type field, expected sse or stdio.");
        }

        config.type = tool_table["type"].as_string()->get();

        if (config.type == "stdio") {
            // Read command
            if (!tool_table.contains("command") || !tool_table["command"].is_string()) {
                throw std::runtime_error("stdio type tool configuration missing command field.");
            }
            config.command = tool_table["command"].as_string()->get();
            
            // Read arguments (if any)
            if (tool_table.contains("args") && tool_table["args"].is_array()) {
                const auto& args_array = *tool_table["args"].as_array();
                for (const auto& arg : args_array) {
                    if (arg.is_string()) {
                        config.args.push_back(arg.as_string()->get());
                    }
                }
            }
            
            // Read environment variables
            if (tool_table.contains("env") && tool_table["env"].is_table()) {
                const auto& env_table = *tool_table["env"].as_table();
                for (const auto& [key, value] : env_table) {
                    if (value.is_string()) {
                        config.env_vars[key] = value.as_string()->get();
                    } else if (value.is_integer()) {
                        config.env_vars[key] = value.as_integer()->get();
                    } else if (value.is_floating_point()) {
                        config.env_vars[key] = value.as_floating_point()->get();
                    } else if (value.is_boolean()) {
                        config.env_vars[key] = value.as_boolean()->get();
                    }
                }
            }
        } else if (config.type == "sse") {
            // Read host and port or url
            if (tool_table.contains("url") && tool_table["url"].is_string()) {
                config.url = tool_table["url"].as_string()->get();
            } else {
                if (!tool_table.contains("host") || !tool_table["host"].is_string()) {
                    throw std::runtime_error("sse type tool configuration missing host field");
                }
                config.host = tool_table["host"].as_string()->get();

                if (!tool_table.contains("port") || !tool_table["port"].is_integer()) {
                    throw std::runtime_error("sse type tool configuration missing port field");
                }
                config.port = tool_table["port"].as_integer()->get();
            }
        } else {
            throw std::runtime_error("Unsupported tool type: " + config.type);
        }
    } catch (const std::exception& e) {
        logger->error("Failed to load MCP tool configuration: " + std::string(e.what()));
        throw;
    }
    
    return config;
}

// Initialize static members
Config* Config::_instance = nullptr;

void Config::_load_initial_llm_config() {
    try {
        auto config_path = _get_llm_config_path();
        logger->info("Loading LLM config file from: " + config_path.string());
        
        const auto& data = toml::parse_file(config_path.string());

        // Load LLM configuration
        for (const auto& [key, value] : data) {
            const auto& llm_table = *value.as_table();

            LLMConfig llm_config;

            if (llm_table.contains("model") && llm_table["model"].is_string()) {
                llm_config.model = llm_table["model"].as_string()->get();
            }

            if (llm_table.contains("api_key") && llm_table["api_key"].is_string()) {
                llm_config.api_key = llm_table["api_key"].as_string()->get();
            }

            if (llm_table.contains("base_url") && llm_table["base_url"].is_string()) {
                llm_config.base_url = llm_table["base_url"].as_string()->get();
            }

            if (llm_table.contains("endpoint") && llm_table["endpoint"].is_string()) {
                llm_config.endpoint = llm_table["endpoint"].as_string()->get();
            }

            if (llm_table.contains("vision_details") && llm_table["vision_details"].is_string()) {
                llm_config.vision_details = llm_table["vision_details"].as_string()->get();
            }
            
            if (llm_table.contains("max_tokens") && llm_table["max_tokens"].is_integer()) {
                llm_config.max_tokens = llm_table["max_tokens"].as_integer()->get();
            }

            if (llm_table.contains("timeout") && llm_table["timeout"].is_integer()) {
                llm_config.timeout = llm_table["timeout"].as_integer()->get();
            }

            if (llm_table.contains("temperature") && llm_table["temperature"].is_floating_point()) {
                llm_config.temperature = llm_table["temperature"].as_floating_point()->get();
            }

            if (llm_table.contains("enable_vision") && llm_table["enable_vision"].is_boolean()) {
                llm_config.enable_vision = llm_table["enable_vision"].as_boolean()->get();
            }

            if (llm_table.contains("enable_tool") && llm_table["enable_tool"].is_boolean()) {
                llm_config.enable_tool = llm_table["enable_tool"].as_boolean()->get();
            }

            _config.llm[std::string(key.str())] = llm_config;

            if (!llm_config.enable_tool) {
                // Load tool helper configuration
                ToolParser tool_parser;
                if (llm_table.contains("tool_parser") && llm_table["tool_parser"].is_table()) {
                    const auto& tool_parser_table = *llm_table["tool_parser"].as_table();
                    if (tool_parser_table.contains("tool_start")) {
                        tool_parser.tool_start = tool_parser_table["tool_start"].as_string()->get();
                    }

                    if (tool_parser_table.contains("tool_end")) {
                        tool_parser.tool_end = tool_parser_table["tool_end"].as_string()->get();
                    }

                    if (tool_parser_table.contains("tool_hint_template")) {
                        tool_parser.tool_hint_template = tool_parser_table["tool_hint_template"].as_string()->get();
                    }
                }
                _config.tool_parser[std::string(key.str())] = tool_parser;
            }
        }

        if (_config.llm.empty()) {
            throw std::runtime_error("No LLM configuration found");
        } else if (_config.llm.find("default") == _config.llm.end()) {
            _config.llm["default"] = _config.llm.begin()->second;
        }

        if (_config.tool_parser.find("default") == _config.tool_parser.end()) {
            _config.tool_parser["default"] = ToolParser();
        }
    } catch (const std::exception& e) {
        logger->warn("Failed to load LLM configuration: " + std::string(e.what()));
        // Set default configuration
        _config.llm["default"] = LLMConfig();
        _config.tool_parser["default"] = ToolParser();
    }
}

void Config::_load_initial_mcp_server_config() {
    try {
        auto config_path = _get_mcp_server_config_path();
        logger->info("Loading MCP tool config file from: " + config_path.string());

        const auto& data = toml::parse_file(config_path.string());

        // Load MCP tool configuration
        for (const auto& [key, value] : data) {
            const auto& tool_table = *value.as_table();

            _config.mcp_server[std::string(key.str())] = MCPServerConfig::load_from_toml(tool_table);
        }
    } catch (const std::exception& e) {
        logger->warn("Failed to load MCP tool configuration: " + std::string(e.what()));
    }
}

void Config::_load_initial_embedding_model_config() {
    try {
        auto config_path = _get_embedding_model_config_path();
        logger->info("Loading embedding model config file from: " + config_path.string());
        
        const auto& data = toml::parse_file(config_path.string());

        // Load embedding model configuration
        for (const auto& [key, value] : data) {
            const auto& embd_table = *value.as_table();

            EmbeddingModelConfig embd_config;

            if (embd_table.contains("provider") && embd_table["provider"].is_string()) {
                embd_config.provider = embd_table["provider"].as_string()->get();
            }

            if (embd_table.contains("base_url") && embd_table["base_url"].is_string()) {
                embd_config.base_url = embd_table["base_url"].as_string()->get();
            }

            if (embd_table.contains("endpoint") && embd_table["endpoint"].is_string()) {
                embd_config.endpoint = embd_table["endpoint"].as_string()->get();
            }

            if (embd_table.contains("model") && embd_table["model"].is_string()) {
                embd_config.model = embd_table["model"].as_string()->get();
            }

            if (embd_table.contains("api_key") && embd_table["api_key"].is_string()) {
                embd_config.api_key = embd_table["api_key"].as_string()->get();
            }

            if (embd_table.contains("embedding_dims") && embd_table["embedding_dims"].is_integer()) {
                embd_config.embedding_dims = embd_table["embedding_dims"].as_integer()->get();
            }

            if (embd_table.contains("max_retries") && embd_table["max_retries"].is_integer()) {
                embd_config.max_retries = embd_table["max_retries"].as_integer()->get();
            }

            _config.embedding_model[std::string(key.str())] = embd_config;
        }

        if (_config.embedding_model.empty()) {
            throw std::runtime_error("No embedding model configuration found");
        } else if (_config.embedding_model.find("default") == _config.embedding_model.end()) {
            _config.embedding_model["default"] = _config.embedding_model.begin()->second;
        }
    } catch (const std::exception& e) {
        logger->warn("Failed to load embedding model configuration: " + std::string(e.what()));
        // Set default configuration
        _config.embedding_model["default"] = EmbeddingModelConfig();
    }
}

void Config::_load_initial_vector_store_config() {
    try {
        auto config_path = _get_vector_store_config_path();
        logger->info("Loading vector store config file from: " + config_path.string());
        
        const auto& data = toml::parse_file(config_path.string());

        // Load vector store configuration
        for (const auto& [key, value] : data) {
            const auto& vs_table = *value.as_table();

            VectorStoreConfig vs_config;

            if (vs_table.contains("provider") && vs_table["provider"].is_string()) {
                vs_config.provider = vs_table["provider"].as_string()->get();
            }

            if (vs_table.contains("dim") && vs_table["dim"].is_integer()) {
                vs_config.dim = vs_table["dim"].as_integer()->get();
            }

            if (vs_table.contains("max_elements") && vs_table["max_elements"].is_integer()) {
                vs_config.max_elements = vs_table["max_elements"].as_integer()->get();
            }

            if (vs_table.contains("M") && vs_table["M"].is_integer()) {
                vs_config.M = vs_table["M"].as_integer()->get();
            }

            if (vs_table.contains("ef_construction") && vs_table["ef_construction"].is_integer()) {
                vs_config.ef_construction = vs_table["ef_construction"].as_integer()->get();
            }

            if (vs_table.contains("metric") && vs_table["metric"].is_string()) {
                const auto& metric_str = vs_table["metric"].as_string()->get();
                if (metric_str == "L2") {
                    vs_config.metric = VectorStoreConfig::Metric::L2;
                } else if (metric_str == "IP") {
                    vs_config.metric = VectorStoreConfig::Metric::IP;
                } else {
                    throw std::runtime_error("Invalid metric: " + metric_str);
                }
            }

            _config.vector_store[std::string(key.str())] = vs_config;
        }

        if (_config.vector_store.empty()) {
            throw std::runtime_error("No vector store configuration found");
        } else if (_config.vector_store.find("default") == _config.vector_store.end()) {
            _config.vector_store["default"] = _config.vector_store.begin()->second;
        }
    } catch (const std::exception& e) {
        logger->warn("Failed to load vector store configuration: " + std::string(e.what()));
        // Set default configuration
        _config.vector_store["default"] = VectorStoreConfig();
    }
}

} // namespace humanus