package mcp

import (
	"context"
	"encoding/json"
	"fmt"
	"os"
	"path/filepath"
	"strings"

	"github.com/tidbcloud/AgentTeam/desktop/internal/profile"
	"github.com/tidbcloud/AgentTeam/desktop/internal/runtimeattachments"
	"github.com/tidbcloud/AgentTeam/desktop/internal/types"
	"github.com/tidbcloud/AgentTeam/desktop/internal/wire/apiclient"
	"github.com/tidbcloud/AgentTeam/desktop/internal/wire/workspacefs"
)

const attachmentViewInstruction = "Use a file-reading tool to inspect this attachment file."

func heldRefreshActionLine(partial bool, refresh heldSendRefresh) string {
	switch heldRefreshAction(refresh) {
	case heldRefreshActionReadNewMessages:
		if partial {
			return "Target context is outdated: new message surfaced below, retry sending your message to continue if needed."
		}
		return "Target context is outdated: new message surfaced below, retry sending your message if needed."
	case heldRefreshActionPendingInput:
		return "Target context is outdated: new message will be surfaced at the next input boundary, retry sending your message after it if needed."
	default:
		if partial {
			return "Target context is outdated: read the exact target's recent history before retrying your message."
		}
		return "Target context is outdated: read the exact target's recent history before retrying your message."
	}
}

// uploadFileViaAPI uploads a file to the AgentTeam API and returns the attachment ID.
func (h *mcpHandler) uploadFileViaAPI(ctx context.Context, api *apiclient.Client, channelTarget, filePath string) (string, error) {
	normalizedTarget, err := normalizeMessageTarget(channelTarget)
	if err != nil {
		return "", err
	}
	channelTarget = normalizedTarget
	stat, err := os.Stat(filePath)
	if err != nil {
		if os.IsNotExist(err) {
			return "", fmt.Errorf("file not found: %s", filePath)
		}
		return "", fmt.Errorf("stat file: %s", err.Error())
	}
	if stat.Size() > apiclient.MaxUploadBytes {
		return "", fmt.Errorf("file too large (%.1f MiB), max %s", float64(stat.Size())/1024/1024, apiclient.MaxUploadSizeLabel)
	}

	resolved, err := api.ResolveTarget(ctx, channelTarget, apiclient.ResolvePurposeWritable)
	if err != nil {
		return "", err
	}

	uploadPath := api.AgentRuntimePath("/upload")
	attachmentID, err := api.PostMultipart(ctx, uploadPath, filePath, "file", map[string]string{"scopeId": resolved.ScopeID}, nil)
	if err != nil {
		resolved.InvalidateOnError(err)
		return "", err
	}
	return attachmentID, nil
}

func (h *mcpHandler) toolViewFile(ctx context.Context, api *apiclient.Client, args json.RawMessage) *types.MCPToolResult {
	var p struct {
		AttachmentID string `json:"attachment_id"`
	}
	if err := json.Unmarshal(args, &p); err != nil {
		return toolError("invalid arguments: " + err.Error())
	}
	if strings.TrimSpace(p.AttachmentID) == "" {
		return toolError("attachment_id is required")
	}

	cacheDir := runtimeattachments.CacheDir(profile.ConfigDir())
	if cachedPath, ok := runtimeattachments.FindCachedPath(cacheDir, p.AttachmentID); ok {
		info, _ := os.Stat(cachedPath)
		size := int64(-1)
		if info != nil {
			size = info.Size()
		}
		contentType := runtimeattachments.ContentTypeForPath(cachedPath)
		return toolText(fmt.Sprintf("Attachment file already cached at: %s\nFile metadata: %s\n\n%s",
			cachedPath, runtimeattachments.MetadataSummary(cachedPath, contentType, size), attachmentViewInstruction))
	}

	download, err := api.GetAttachment(ctx, p.AttachmentID)
	if err != nil {
		return toolError(err.Error())
	}

	contentType := runtimeattachments.NormalizeMIME(download.ContentType, download.Filename, download.Data)
	if err := os.MkdirAll(cacheDir, runtimeattachments.CacheDirMode); err != nil {
		return toolError(fmt.Sprintf("failed to cache attachment %s (%s, %d bytes): %v", p.AttachmentID, contentType, len(download.Data), err))
	}

	localPath := filepath.Join(cacheDir, runtimeattachments.CacheFilename(p.AttachmentID, download.Filename, contentType))
	if err := os.WriteFile(localPath, download.Data, runtimeattachments.CacheFileMode); err != nil {
		return toolError(fmt.Sprintf("failed to cache attachment %s (%s, %d bytes): %v", p.AttachmentID, contentType, len(download.Data), err))
	}

	return toolText(fmt.Sprintf("Attachment file %s saved to:\n%s\nFile metadata: %s\n\n%s",
		p.AttachmentID, localPath, runtimeattachments.MetadataSummary(firstNonEmptyString(download.Filename, localPath), contentType, int64(len(download.Data))), attachmentViewInstruction))
}

func (h *mcpHandler) localToolWorkDir(api *apiclient.Client) string {
	if api != nil {
		agentID := strings.TrimSpace(api.LocalAgentID)
		if agentID != "" && h.c != nil && h.runtime != nil {
			if sess, ok := h.runtime.Session(agentID); ok {
				if workDir := strings.TrimSpace(sess.WorkDir); workDir != "" {
					return workDir
				}
			}
		}
	}
	return strings.TrimSpace(os.Getenv(envWorkDir))
}

func (h *mcpHandler) resolveLocalToolFilePath(api *apiclient.Client, raw string) (string, error) {
	workDir := h.localToolWorkDir(api)
	if workDir == "" {
		return "", fmt.Errorf("local file access requires %s or a session workdir", envWorkDir)
	}
	absWorkDir, err := filepath.Abs(workDir)
	if err != nil {
		return "", fmt.Errorf("resolve workdir: %w", err)
	}
	return workspacefs.ResolvePathWithinBase(absWorkDir, raw)
}

// ── message_attachment_upload ─────────────────────────────────────────────────────────────

func (h *mcpHandler) toolUploadFile(ctx context.Context, api *apiclient.Client, args json.RawMessage) *types.MCPToolResult {
	var p struct {
		FilePath string `json:"file_path"`
		Channel  string `json:"channel"`
	}
	if err := json.Unmarshal(args, &p); err != nil {
		return toolError("invalid arguments: " + err.Error())
	}
	filePath := strings.TrimSpace(p.FilePath)
	channel, err := normalizeMessageTarget(p.Channel)
	if err != nil {
		return toolError(err.Error())
	}
	if filePath == "" {
		return toolError("file_path is required")
	}
	if channel == "" {
		return toolError("channel is required")
	}
	resolvedPath, err := h.resolveLocalToolFilePath(api, filePath)
	if err != nil {
		return toolError(err.Error())
	}

	stat, err := os.Stat(resolvedPath)
	if err != nil {
		if os.IsNotExist(err) {
			return toolError("File not found: " + resolvedPath)
		}
		return toolError(err.Error())
	}
	if stat.Size() > apiclient.MaxUploadBytes {
		return toolError(fmt.Sprintf("File too large (%.1f MiB). Max %s.", float64(stat.Size())/1024/1024, apiclient.MaxUploadSizeLabel))
	}

	attachmentID, err := h.uploadFileViaAPI(ctx, api, channel, resolvedPath)
	if err != nil {
		return toolError(err.Error())
	}

	return toolText(fmt.Sprintf("File uploaded: %s (%.1fKB)\nAttachment ID: %s\n\nUse this ID in the message tool's attachment_ids parameter to include it in a message.",
		filepath.Base(resolvedPath), float64(stat.Size())/1024, attachmentID))
}

// ── message_search ─────────────────────────────────────────────────────────
