package tools

import (
	"context"
	"strings"

	"github.com/modelcontextprotocol/go-sdk/mcp"

	"git.warky.dev/wdevs/amcs/internal/session"
	"git.warky.dev/wdevs/amcs/internal/store"
	ext "git.warky.dev/wdevs/amcs/internal/types"
)

type ChatHistoryTool struct {
	store    *store.DB
	sessions *session.ActiveProjects
}

func NewChatHistoryTool(db *store.DB, sessions *session.ActiveProjects) *ChatHistoryTool {
	return &ChatHistoryTool{store: db, sessions: sessions}
}

// save_chat_history

type SaveChatHistoryInput struct {
	SessionID string             `json:"session_id" jsonschema:"unique identifier for the chat session (e.g. OpenClaw session id or thread id)"`
	Title     string             `json:"title,omitempty" jsonschema:"optional human-readable title for this conversation"`
	Channel   string             `json:"channel,omitempty" jsonschema:"optional channel name (e.g. telegram, discord, signal)"`
	AgentID   string             `json:"agent_id,omitempty" jsonschema:"optional agent identifier (e.g. claude, codex, main)"`
	Project   string             `json:"project,omitempty" jsonschema:"optional project name or id; falls back to active session project"`
	Messages  []ext.ChatMessage  `json:"messages" jsonschema:"ordered list of messages in the conversation"`
	Summary   string             `json:"summary,omitempty" jsonschema:"optional brief summary of the conversation"`
	Metadata  map[string]any     `json:"metadata,omitempty" jsonschema:"optional arbitrary key-value metadata"`
}

type SaveChatHistoryOutput struct {
	ChatHistory ext.ChatHistory `json:"chat_history"`
}

func (t *ChatHistoryTool) SaveChatHistory(ctx context.Context, req *mcp.CallToolRequest, in SaveChatHistoryInput) (*mcp.CallToolResult, SaveChatHistoryOutput, error) {
	if strings.TrimSpace(in.SessionID) == "" {
		return nil, SaveChatHistoryOutput{}, errRequiredField("session_id")
	}
	if len(in.Messages) == 0 {
		return nil, SaveChatHistoryOutput{}, errRequiredField("messages")
	}

	project, err := resolveProject(ctx, t.store, t.sessions, req, in.Project, false)
	if err != nil {
		return nil, SaveChatHistoryOutput{}, err
	}

	h := ext.ChatHistory{
		SessionID: strings.TrimSpace(in.SessionID),
		Title:     strings.TrimSpace(in.Title),
		Channel:   strings.TrimSpace(in.Channel),
		AgentID:   strings.TrimSpace(in.AgentID),
		Messages:  in.Messages,
		Summary:   strings.TrimSpace(in.Summary),
		Metadata:  in.Metadata,
	}
	if h.Metadata == nil {
		h.Metadata = map[string]any{}
	}
	if project != nil {
		h.ProjectID = &project.NumericID
	}

	saved, err := t.store.SaveChatHistory(ctx, h)
	if err != nil {
		return nil, SaveChatHistoryOutput{}, err
	}
	return nil, SaveChatHistoryOutput{ChatHistory: saved}, nil
}

// get_chat_history

type GetChatHistoryInput struct {
	ID        string `json:"id,omitempty" jsonschema:"numeric id of the saved chat history"`
	SessionID string `json:"session_id,omitempty" jsonschema:"original session_id — returns the most recent history for that session"`
}

type GetChatHistoryOutput struct {
	ChatHistory *ext.ChatHistory `json:"chat_history"`
}

func (t *ChatHistoryTool) GetChatHistory(ctx context.Context, _ *mcp.CallToolRequest, in GetChatHistoryInput) (*mcp.CallToolResult, GetChatHistoryOutput, error) {
	if in.ID == "" && in.SessionID == "" {
		return nil, GetChatHistoryOutput{}, errRequiredField("id or session_id")
	}

	if in.ID != "" {
		id, err := parseID(in.ID)
		if err != nil {
			return nil, GetChatHistoryOutput{}, errInvalidField("id", "invalid id", "must be a valid numeric id")
		}
		h, found, err := t.store.GetChatHistory(ctx, id)
		if err != nil {
			return nil, GetChatHistoryOutput{}, err
		}
		if !found {
			return nil, GetChatHistoryOutput{}, nil
		}
		return nil, GetChatHistoryOutput{ChatHistory: &h}, nil
	}

	h, found, err := t.store.GetChatHistoryBySessionID(ctx, in.SessionID)
	if err != nil {
		return nil, GetChatHistoryOutput{}, err
	}
	if !found {
		return nil, GetChatHistoryOutput{}, nil
	}
	return nil, GetChatHistoryOutput{ChatHistory: &h}, nil
}

// list_chat_histories

type ListChatHistoriesInput struct {
	Project   string `json:"project,omitempty"    jsonschema:"filter by project name or id"`
	Channel   string `json:"channel,omitempty"    jsonschema:"filter by channel (e.g. telegram, discord)"`
	AgentID   string `json:"agent_id,omitempty"   jsonschema:"filter by agent id"`
	SessionID string `json:"session_id,omitempty" jsonschema:"filter by session_id"`
	Days      int    `json:"days,omitempty"       jsonschema:"only include histories from the last N days"`
	Limit     int    `json:"limit,omitempty"      jsonschema:"maximum number of results to return (default 20, max 200)"`
}

type ListChatHistoriesOutput struct {
	ChatHistories []ext.ChatHistory `json:"chat_histories"`
}

func (t *ChatHistoryTool) ListChatHistories(ctx context.Context, req *mcp.CallToolRequest, in ListChatHistoriesInput) (*mcp.CallToolResult, ListChatHistoriesOutput, error) {
	filter := store.ListChatHistoriesFilter{
		Channel:   strings.TrimSpace(in.Channel),
		AgentID:   strings.TrimSpace(in.AgentID),
		SessionID: strings.TrimSpace(in.SessionID),
		Days:      in.Days,
		Limit:     in.Limit,
	}

	if in.Project != "" {
		project, err := resolveProject(ctx, t.store, t.sessions, req, in.Project, false)
		if err != nil {
			return nil, ListChatHistoriesOutput{}, err
		}
		if project != nil {
			filter.ProjectID = &project.NumericID
		}
	}

	histories, err := t.store.ListChatHistories(ctx, filter)
	if err != nil {
		return nil, ListChatHistoriesOutput{}, err
	}
	if histories == nil {
		histories = []ext.ChatHistory{}
	}
	return nil, ListChatHistoriesOutput{ChatHistories: histories}, nil
}

// delete_chat_history

type DeleteChatHistoryInput struct {
	ID string `json:"id" jsonschema:"numeric id of the chat history to delete"`
}

type DeleteChatHistoryOutput struct {
	Deleted bool `json:"deleted"`
}

func (t *ChatHistoryTool) DeleteChatHistory(ctx context.Context, _ *mcp.CallToolRequest, in DeleteChatHistoryInput) (*mcp.CallToolResult, DeleteChatHistoryOutput, error) {
	id, err := parseID(in.ID)
	if err != nil {
		return nil, DeleteChatHistoryOutput{}, errInvalidField("id", "invalid id", "must be a valid numeric id")
	}
	deleted, err := t.store.DeleteChatHistory(ctx, id)
	if err != nil {
		return nil, DeleteChatHistoryOutput{}, err
	}
	return nil, DeleteChatHistoryOutput{Deleted: deleted}, nil
}
