package tools

import (
	"context"
	"fmt"
	"strings"

	"github.com/google/uuid"
	"github.com/jackc/pgx/v5"
	"github.com/modelcontextprotocol/go-sdk/mcp"

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

func parseUUID(id string) (uuid.UUID, error) {
	trimmed := strings.TrimSpace(id)
	parsed, err := uuid.Parse(trimmed)
	if err != nil {
		return uuid.Nil, newMCPError(
			codeInvalidID,
			fmt.Sprintf("invalid id %q", id),
			mcpErrorData{
				Type:   mcperrors.TypeInvalidID,
				Field:  "id",
				Value:  trimmed,
				Detail: err.Error(),
				Hint:   "pass a valid UUID",
			},
		)
	}
	return parsed, nil
}

func sessionID(req *mcp.CallToolRequest) (string, error) {
	if req == nil || req.Session == nil || req.Session.ID() == "" {
		return "", newMCPError(
			codeSessionRequired,
			"tool requires an MCP session; use a stateful MCP client for session-scoped operations",
			mcpErrorData{
				Type: mcperrors.TypeSessionRequired,
				Hint: "use a stateful MCP client for session-scoped operations",
			},
		)
	}
	return req.Session.ID(), nil
}

func resolveProject(ctx context.Context, db *store.DB, sessions *session.ActiveProjects, req *mcp.CallToolRequest, raw string, required bool) (*thoughttypes.Project, error) {
	projectRef := strings.TrimSpace(raw)
	if projectRef == "" && sessions != nil && req != nil && req.Session != nil {
		if activeID, ok := sessions.Get(req.Session.ID()); ok {
			project, err := db.GetProject(ctx, activeID.String())
			if err == nil {
				return &project, nil
			}
			if err != pgx.ErrNoRows {
				return nil, err
			}
		}
	}

	if projectRef == "" {
		if required {
			return nil, newMCPError(
				codeProjectRequired,
				"project is required; pass project explicitly or call set_active_project in this MCP session first",
				mcpErrorData{
					Type:  mcperrors.TypeProjectRequired,
					Field: "project",
					Hint:  "pass project explicitly or call set_active_project in this MCP session first",
				},
			)
		}
		return nil, nil
	}

	project, err := db.GetProject(ctx, projectRef)
	if err != nil {
		if err == pgx.ErrNoRows {
			return nil, newMCPError(
				codeProjectNotFound,
				fmt.Sprintf("project %q not found", projectRef),
				mcpErrorData{
					Type:    mcperrors.TypeProjectNotFound,
					Field:   "project",
					Project: projectRef,
					Hint:    fmt.Sprintf("project %q does not exist yet; call create_project with name=%q first, then retry", projectRef, projectRef),
				},
			)
		}
		return nil, err
	}

	return &project, nil
}

func formatContextBlock(header string, lines []string) string {
	if len(lines) == 0 {
		return header + "\n\nNo matching thoughts."
	}
	return header + "\n\n" + strings.Join(lines, "\n\n")
}

func thoughtContextLine(index int, content string, metadata thoughttypes.ThoughtMetadata, similarity float64) string {
	label := fmt.Sprintf("%d. %s", index+1, strings.TrimSpace(content))
	parts := make([]string, 0, 3)
	if len(metadata.Topics) > 0 {
		parts = append(parts, "topics="+strings.Join(metadata.Topics, ", "))
	}
	if metadata.Type != "" {
		parts = append(parts, "type="+metadata.Type)
	}
	if similarity > 0 {
		parts = append(parts, fmt.Sprintf("similarity=%.3f", similarity))
	}
	if len(parts) == 0 {
		return label
	}
	return label + "\n[" + strings.Join(parts, " | ") + "]"
}
