// SPDX-FileCopyrightText: Amolith <amolith@secluded.site>
//
// SPDX-License-Identifier: AGPL-3.0-or-later

package tools

import (
	"context"
	"errors"
	"fmt"
	"strings"
	"time"

	"github.com/ijt/go-anytime"
	"github.com/mark3labs/mcp-go/mcp"
)

// AreaProvider defines the interface for accessing area data.
type AreaProvider interface {
	GetName() string
	GetID() string
	GetGoals() []GoalProvider
}

// GoalProvider defines the interface for accessing goal data.
type GoalProvider interface {
	GetName() string
	GetID() string
}

// HabitProvider defines the interface for accessing habit data.
type HabitProvider interface {
	GetName() string
	GetID() string
}

// HandlerConfig holds the necessary configuration for tool handlers.
type HandlerConfig struct {
	AccessToken string
	Timezone    string
	Areas       []AreaProvider
	Habits      []HabitProvider
}

// Handlers provides methods for handling MCP tool calls.
type Handlers struct {
	config HandlerConfig
}

// NewHandlers creates a new Handlers instance.
func NewHandlers(config HandlerConfig) *Handlers {
	return &Handlers{config: config}
}

// reportMCPError creates an MCP error result.
func reportMCPError(msg string) (*mcp.CallToolResult, error) {
	return &mcp.CallToolResult{
		IsError: true,
		Content: []mcp.Content{mcp.TextContent{Type: "text", Text: msg}},
	}, nil
}

// LoadLocation loads a timezone location string, returning a *time.Location or error.
func LoadLocation(timezone string) (*time.Location, error) {
	if timezone == "" {
		return nil, errors.New("timezone is not configured; please set the 'timezone' value in your config file (e.g. 'UTC' or 'America/New_York')")
	}

	loc, err := time.LoadLocation(timezone)
	if err != nil {
		return nil, fmt.Errorf("could not load timezone '%s': %w", timezone, err)
	}

	return loc, nil
}

// HandleGetTimestamp handles the get_timestamp tool call.
func (h *Handlers) HandleGetTimestamp(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
	natLangDate, ok := request.Params.Arguments["natural_language_date"].(string)
	if !ok || natLangDate == "" {
		return reportMCPError("Missing or invalid required argument: natural_language_date")
	}

	loc, err := LoadLocation(h.config.Timezone)
	if err != nil {
		return reportMCPError(err.Error())
	}

	parsedTime, err := anytime.Parse(natLangDate, time.Now().In(loc))
	if err != nil {
		return reportMCPError(fmt.Sprintf("Could not parse natural language date: %v", err))
	}

	return &mcp.CallToolResult{
		Content: []mcp.Content{
			mcp.TextContent{
				Type: "text",
				Text: parsedTime.Format(time.RFC3339),
			},
		},
	}, nil
}

// HandleListAreasAndGoals handles the list_areas_and_goals tool call.
func (h *Handlers) HandleListAreasAndGoals(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
	var b strings.Builder
	for _, area := range h.config.Areas {
		fmt.Fprintf(&b, "- %s: %s\n", area.GetName(), area.GetID())

		for _, goal := range area.GetGoals() {
			fmt.Fprintf(&b, "  - %s: %s\n", goal.GetName(), goal.GetID())
		}
	}

	return &mcp.CallToolResult{
		Content: []mcp.Content{
			mcp.TextContent{
				Type: "text",
				Text: b.String(),
			},
		},
	}, nil
}
