package mcp

import (
	"encoding/json"
	"reflect"
	"strings"
	"time"

	"github.com/mayswind/ezbookkeeping/pkg/core"
	"github.com/mayswind/ezbookkeeping/pkg/errs"
	"github.com/mayswind/ezbookkeeping/pkg/exchangerates"
	"github.com/mayswind/ezbookkeeping/pkg/models"
	"github.com/mayswind/ezbookkeeping/pkg/settings"
	"github.com/mayswind/ezbookkeeping/pkg/utils"
)

// MCPQueryExchangeRatesRequest represents all parameters of the query exchange rates request
type MCPQueryExchangeRatesRequest struct {
	Currencies string `json:"currencies" jsonschema_description:"Comma-separated list of currencies to query exchange rates for (e.g. USD,CNY,EUR)"`
}

// MCPQueryExchangeRatesResponse represents the response structure for querying exchange rates
type MCPQueryExchangeRatesResponse struct {
	BaseCurrency string                      `json:"base_currency" jsonschema_description:"Base currency code (e.g. USD)"`
	UpdateTime   string                      `json:"update_time" jsonschema_description:"Last update time of the exchange rates in RFC 3339 format (e.g. '2023-01-01T12:00:00Z')"`
	Rates        []*MCPQueryExchangeRateInfo `json:"rates" jsonschema_description:"Exchange rates for the specified currencies"`
}

// MCPQueryExchangeRateInfo defines the structure of exchange rate information for a specific currency
type MCPQueryExchangeRateInfo struct {
	Currency string `json:"currency" jsonschema_description:"Currency code (e.g. USD)"`
	Rate     string `json:"rate_to_base" jsonschema_description:"The amount of the base currency that can be obtained for 1 unit of this currency"`
}

type mcpQueryLatestExchangeRatesToolHandler struct{}

var MCPQueryLatestExchangeRatesToolHandler = &mcpQueryLatestExchangeRatesToolHandler{}

// Name returns the name of the MCP tool
func (h *mcpQueryLatestExchangeRatesToolHandler) Name() string {
	return "query_latest_exchange_rates"
}

// Description returns the description of the MCP tool
func (h *mcpQueryLatestExchangeRatesToolHandler) Description() string {
	return "Query latest exchange rates with specified currencies."
}

// InputType returns the input type for the MCP tool request
func (h *mcpQueryLatestExchangeRatesToolHandler) InputType() reflect.Type {
	return reflect.TypeOf(&MCPQueryExchangeRatesRequest{})
}

// OutputType returns the output type for the MCP tool response
func (h *mcpQueryLatestExchangeRatesToolHandler) OutputType() reflect.Type {
	return reflect.TypeOf(&MCPQueryExchangeRatesResponse{})
}

// Handle processes the MCP call tool request and returns the response
func (h *mcpQueryLatestExchangeRatesToolHandler) Handle(c *core.WebContext, callToolReq *MCPCallToolRequest, user *models.User, currentConfig *settings.Config, services MCPAvailableServices) (any, []*MCPTextContent, error) {
	var exchangeRatesRequest MCPQueryExchangeRatesRequest

	if callToolReq.Arguments != nil {
		if err := json.Unmarshal(callToolReq.Arguments, &exchangeRatesRequest); err != nil {
			return nil, nil, errs.NewIncompleteOrIncorrectSubmissionError(err)
		}
	} else {
		return nil, nil, errs.ErrIncompleteOrIncorrectSubmission
	}

	exchangeRateResponse, err := exchangerates.Container.GetLatestExchangeRates(c, user.Uid, currentConfig)

	if err != nil {
		return nil, nil, err
	}

	structuredResponse, response, err := h.createNewMCPQueryExchangeRatesResponse(exchangeRatesRequest.Currencies, exchangeRateResponse)

	if err != nil {
		return nil, nil, err
	}

	return structuredResponse, response, nil
}

func (h *mcpQueryLatestExchangeRatesToolHandler) createNewMCPQueryExchangeRatesResponse(currencies string, exchangeRatesResp *models.LatestExchangeRateResponse) (any, []*MCPTextContent, error) {
	queryCurrencies := make(map[string]bool)

	for _, currency := range strings.Split(currencies, ",") {
		currency = strings.TrimSpace(currency)

		if currency != "" {
			queryCurrencies[currency] = true
		}
	}

	response := &MCPQueryExchangeRatesResponse{
		BaseCurrency: exchangeRatesResp.BaseCurrency,
		UpdateTime:   utils.FormatUnixTimeToLongDateTimeWithTimezoneRFC3339Format(exchangeRatesResp.UpdateTime, time.UTC),
		Rates:        make([]*MCPQueryExchangeRateInfo, 0, len(exchangeRatesResp.ExchangeRates)),
	}

	for _, rate := range exchangeRatesResp.ExchangeRates {
		if _, exists := queryCurrencies[rate.Currency]; rate.Currency != exchangeRatesResp.BaseCurrency && !exists {
			continue
		}

		response.Rates = append(response.Rates, &MCPQueryExchangeRateInfo{
			Currency: rate.Currency,
			Rate:     rate.Rate,
		})
	}

	content, err := json.Marshal(response)

	if err != nil {
		return nil, nil, err
	}

	return response, []*MCPTextContent{
		NewMCPTextContent(string(content)),
	}, nil
}
