#include "ClaudeProvider.hpp"
#include <spdlog/sinks/stdout_color_sinks.h>
#include <cstdlib>

namespace celuna {

ClaudeProvider::ClaudeProvider(const json& config) {
    m_logger = spdlog::get("ClaudeProvider");
    if (!m_logger) {
        m_logger = spdlog::stdout_color_mt("ClaudeProvider");
    }

    // Get API key from environment
    std::string apiKeyEnv = config.value("api_key_env", "ANTHROPIC_API_KEY");
    const char* apiKey = std::getenv(apiKeyEnv.c_str());
    if (!apiKey) {
        m_logger->error("API key not found in environment: {}", apiKeyEnv);
        throw std::runtime_error("Missing API key: " + apiKeyEnv);
    }
    m_apiKey = apiKey;

    m_model = config.value("model", "claude-sonnet-4-20250514");
    m_maxTokens = config.value("max_tokens", 4096);
    m_baseUrl = config.value("base_url", "https://api.anthropic.com");

    m_client = std::make_unique<HttpClient>(m_baseUrl, 60);
    m_client->setHeader("x-api-key", m_apiKey);
    m_client->setHeader("anthropic-version", "2023-06-01");
    m_client->setHeader("Content-Type", "application/json");

    m_logger->info("ClaudeProvider initialized: model={}", m_model);
}

LLMResponse ClaudeProvider::chat(const std::string& systemPrompt,
                                  const json& messages,
                                  const json& tools) {
    json request = {
        {"model", m_model},
        {"max_tokens", m_maxTokens},
        {"system", systemPrompt},
        {"messages", messages}
    };

    if (!tools.empty()) {
        request["tools"] = convertTools(tools);
    }

    m_logger->debug("Sending request to Claude: {} messages, {} tools", messages.size(), tools.size());
    m_logger->trace("Request body: {}", request.dump(2));

    auto response = m_client->post("/v1/messages", request);

    if (!response.success) {
        m_logger->error("Claude API error: {}", response.error);
        LLMResponse errorResp;
        errorResp.text = "Error: " + response.error;
        errorResp.is_end_turn = true;
        return errorResp;
    }

    try {
        json jsonResponse = json::parse(response.body);
        return parseResponse(jsonResponse);
    } catch (const json::exception& e) {
        m_logger->error("Failed to parse Claude response: {}", e.what());
        LLMResponse errorResp;
        errorResp.text = "Parse error: " + std::string(e.what());
        errorResp.is_end_turn = true;
        return errorResp;
    }
}

json ClaudeProvider::convertTools(const json& tools) {
    // Tools are already in a compatible format
    // Just ensure the structure matches Claude's expectations
    json claudeTools = json::array();

    for (const auto& tool : tools) {
        json claudeTool = {
            {"name", tool.value("name", "")},
            {"description", tool.value("description", "")},
            {"input_schema", tool.value("input_schema", json::object())}
        };
        claudeTools.push_back(claudeTool);
    }

    return claudeTools;
}

LLMResponse ClaudeProvider::parseResponse(const json& response) {
    LLMResponse result;

    result.stop_reason = response.value("stop_reason", "");
    result.is_end_turn = (result.stop_reason == "end_turn");
    result.model = response.value("model", m_model);

    // Parse usage
    if (response.contains("usage")) {
        result.input_tokens = response["usage"].value("input_tokens", 0);
        result.output_tokens = response["usage"].value("output_tokens", 0);
    }

    // Parse content blocks
    if (response.contains("content") && response["content"].is_array()) {
        for (const auto& block : response["content"]) {
            std::string type = block.value("type", "");

            if (type == "text") {
                result.text += block.value("text", "");
            }
            else if (type == "tool_use") {
                ToolCall call;
                call.id = block.value("id", "");
                call.name = block.value("name", "");
                call.input = block.value("input", json::object());
                result.tool_calls.push_back(call);
            }
        }
    }

    m_logger->debug("Claude response: text={} chars, tools={}, stop={}",
                   result.text.size(), result.tool_calls.size(), result.stop_reason);

    return result;
}

json ClaudeProvider::formatToolResults(const std::vector<ToolResult>& results) {
    // Claude format: single user message with array of tool_result blocks
    json content = json::array();

    for (const auto& result : results) {
        json block = {
            {"type", "tool_result"},
            {"tool_use_id", result.tool_call_id},
            {"content", result.content}
        };
        if (result.is_error) {
            block["is_error"] = true;
        }
        content.push_back(block);
    }

    return {
        {"role", "user"},
        {"content", content}
    };
}

void ClaudeProvider::appendAssistantMessage(json& messages, const LLMResponse& response) {
    json content = buildAssistantContent(response);
    messages.push_back({
        {"role", "assistant"},
        {"content", content}
    });
}

json ClaudeProvider::buildAssistantContent(const LLMResponse& response) {
    json content = json::array();

    // Add text block if present
    if (!response.text.empty()) {
        content.push_back({
            {"type", "text"},
            {"text", response.text}
        });
    }

    // Add tool_use blocks
    for (const auto& call : response.tool_calls) {
        content.push_back({
            {"type", "tool_use"},
            {"id", call.id},
            {"name", call.name},
            {"input", call.input}
        });
    }

    return content;
}

} // namespace celuna
