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

// Package tasks provides MCP resources for filtered task lists.
package tasks

import (
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"strings"
	"time"

	"git.secluded.site/go-lunatask"
	"git.secluded.site/lune/internal/mcp/shared"
	"github.com/modelcontextprotocol/go-sdk/mcp"
)

// ErrUnknownArea indicates the area reference could not be resolved.
var ErrUnknownArea = errors.New("unknown area")

// Resource URIs for task list filters.
const (
	ResourceURIAll               = "lunatask://tasks/all"
	ResourceURIToday             = "lunatask://tasks/today"
	ResourceURIOverdue           = "lunatask://tasks/overdue"
	ResourceURINext7Days         = "lunatask://tasks/next-7-days"
	ResourceURIHighPriority      = "lunatask://tasks/high-priority"
	ResourceURINow               = "lunatask://tasks/now"
	ResourceURIRecentCompletions = "lunatask://tasks/recent-completions"
)

// Area-scoped resource templates.
const (
	AreaTasksTemplate          = "lunatask://area/{area}/tasks"
	AreaTodayTemplate          = "lunatask://area/{area}/today"
	AreaOverdueTemplate        = "lunatask://area/{area}/overdue"
	AreaNext7DaysTemplate      = "lunatask://area/{area}/next-7-days"
	AreaHighPriorityTemplate   = "lunatask://area/{area}/high-priority"
	AreaNowTemplate            = "lunatask://area/{area}/now"
	AreaRecentCompletionsTempl = "lunatask://area/{area}/recent-completions"
)

// Resource descriptions.
const (
	AllDescription = `All incomplete tasks. EXPENSIVE - prefer filtered resources.`

	TodayDescription = `Tasks scheduled for today.`

	OverdueDescription = `Overdue tasks (scheduled before today, incomplete).`

	Next7DaysDescription = `Tasks scheduled within the next 7 days.`

	HighPriorityDescription = `Tasks with highest priority.`

	NowDescription = `Tasks needing immediate attention: in-progress, highest priority, must-do, or urgent+important.`

	RecentCompletionsDescription = `Tasks completed in the last 72 hours.`

	AreaTasksDescription = `Incomplete tasks in area. {area} accepts config key or UUID.`

	AreaFilteredDescription = `Filtered tasks in area. {area} accepts config key or UUID.`
)

// Handler handles task list resource requests.
type Handler struct {
	client *lunatask.Client
	areas  []shared.AreaProvider
}

// NewHandler creates a new tasks resource handler.
func NewHandler(accessToken string, areas []shared.AreaProvider) *Handler {
	return &Handler{
		client: lunatask.NewClient(accessToken, lunatask.UserAgent("lune-mcp/1.0")),
		areas:  areas,
	}
}

// taskSummary represents a task in list output.
type taskSummary struct {
	DeepLink    string  `json:"deep_link"`
	Status      *string `json:"status,omitempty"`
	Priority    *int    `json:"priority,omitempty"`
	ScheduledOn *string `json:"scheduled_on,omitempty"`
	CompletedAt *string `json:"completed_at,omitempty"`
	CreatedAt   string  `json:"created_at"`
	AreaID      *string `json:"area_id,omitempty"`
	GoalID      *string `json:"goal_id,omitempty"`
	Important   *bool   `json:"important,omitempty"`
	Urgent      *bool   `json:"urgent,omitempty"`
}

// FilterType identifies which filter to apply.
type FilterType int

// Filter type constants.
const (
	TypeAll FilterType = iota
	TypeToday
	TypeOverdue
	TypeNext7Days
	TypeHighPriority
	TypeNow
	TypeRecentCompletions
)

// HandleReadAll handles the all tasks resource.
func (h *Handler) HandleReadAll(
	ctx context.Context,
	req *mcp.ReadResourceRequest,
) (*mcp.ReadResourceResult, error) {
	return h.handleFiltered(ctx, req, TypeAll, "")
}

// HandleReadToday handles the today tasks resource.
func (h *Handler) HandleReadToday(
	ctx context.Context,
	req *mcp.ReadResourceRequest,
) (*mcp.ReadResourceResult, error) {
	return h.handleFiltered(ctx, req, TypeToday, "")
}

// HandleReadOverdue handles the overdue tasks resource.
func (h *Handler) HandleReadOverdue(
	ctx context.Context,
	req *mcp.ReadResourceRequest,
) (*mcp.ReadResourceResult, error) {
	return h.handleFiltered(ctx, req, TypeOverdue, "")
}

// HandleReadNext7Days handles the next 7 days tasks resource.
func (h *Handler) HandleReadNext7Days(
	ctx context.Context,
	req *mcp.ReadResourceRequest,
) (*mcp.ReadResourceResult, error) {
	return h.handleFiltered(ctx, req, TypeNext7Days, "")
}

// HandleReadHighPriority handles the high priority tasks resource.
func (h *Handler) HandleReadHighPriority(
	ctx context.Context,
	req *mcp.ReadResourceRequest,
) (*mcp.ReadResourceResult, error) {
	return h.handleFiltered(ctx, req, TypeHighPriority, "")
}

// HandleReadNow handles the now tasks resource.
func (h *Handler) HandleReadNow(
	ctx context.Context,
	req *mcp.ReadResourceRequest,
) (*mcp.ReadResourceResult, error) {
	return h.handleFiltered(ctx, req, TypeNow, "")
}

// HandleReadRecentCompletions handles the recent completions resource.
func (h *Handler) HandleReadRecentCompletions(
	ctx context.Context,
	req *mcp.ReadResourceRequest,
) (*mcp.ReadResourceResult, error) {
	return h.handleFiltered(ctx, req, TypeRecentCompletions, "")
}

// HandleReadAreaTasks handles area-scoped task resources.
func (h *Handler) HandleReadAreaTasks(
	ctx context.Context,
	req *mcp.ReadResourceRequest,
) (*mcp.ReadResourceResult, error) {
	areaRef, filterType := parseAreaURI(req.Params.URI)
	if areaRef == "" {
		return nil, fmt.Errorf("invalid URI %q: %w", req.Params.URI, mcp.ResourceNotFoundError(req.Params.URI))
	}

	areaID, err := h.resolveAreaRef(areaRef)
	if err != nil {
		return nil, fmt.Errorf("invalid area %q: %w", areaRef, mcp.ResourceNotFoundError(req.Params.URI))
	}

	return h.handleFiltered(ctx, req, filterType, areaID)
}

// resolveAreaRef resolves an area reference to a UUID.
// Accepts config key or UUID.
func (h *Handler) resolveAreaRef(input string) (string, error) {
	// Try UUID or deep link first
	if _, id, err := lunatask.ParseReference(input); err == nil {
		return id, nil
	}

	// Try config key lookup
	for _, area := range h.areas {
		if area.Key == input {
			return area.ID, nil
		}
	}

	return "", fmt.Errorf("%w: %s", ErrUnknownArea, input)
}

func (h *Handler) handleFiltered(
	ctx context.Context,
	req *mcp.ReadResourceRequest,
	filterType FilterType,
	areaID string,
) (*mcp.ReadResourceResult, error) {
	tasks, err := h.client.ListTasks(ctx, nil)
	if err != nil {
		return nil, fmt.Errorf("fetching tasks: %w", err)
	}

	// Apply area filter first if specified
	if areaID != "" {
		tasks = FilterByArea(tasks, areaID)
	}

	// Apply semantic filter
	tasks = applyFilter(tasks, filterType)

	summaries := buildSummaries(tasks)

	data, err := json.MarshalIndent(summaries, "", "  ")
	if err != nil {
		return nil, fmt.Errorf("marshaling tasks: %w", err)
	}

	return &mcp.ReadResourceResult{
		Contents: []*mcp.ResourceContents{{
			URI:      req.Params.URI,
			MIMEType: "application/json",
			Text:     string(data),
		}},
	}, nil
}

func applyFilter(tasks []lunatask.Task, filterType FilterType) []lunatask.Task {
	switch filterType {
	case TypeAll:
		return filterIncomplete(tasks)
	case TypeToday:
		return FilterToday(tasks)
	case TypeOverdue:
		return FilterOverdue(tasks)
	case TypeNext7Days:
		return FilterNext7Days(tasks)
	case TypeHighPriority:
		return FilterHighPriority(tasks)
	case TypeNow:
		return FilterNow(tasks)
	case TypeRecentCompletions:
		return FilterRecentCompletions(tasks)
	default:
		return filterIncomplete(tasks)
	}
}

func filterIncomplete(tasks []lunatask.Task) []lunatask.Task {
	result := make([]lunatask.Task, 0)

	for _, task := range tasks {
		if !isCompleted(&task) {
			result = append(result, task)
		}
	}

	return result
}

func buildSummaries(tasks []lunatask.Task) []taskSummary {
	summaries := make([]taskSummary, 0, len(tasks))

	for _, task := range tasks {
		summary := taskSummary{
			CreatedAt: task.CreatedAt.Format(time.RFC3339),
			AreaID:    task.AreaID,
			GoalID:    task.GoalID,
		}

		summary.DeepLink, _ = lunatask.BuildDeepLink(lunatask.ResourceTask, task.ID)

		if task.Status != nil {
			s := string(*task.Status)
			summary.Status = &s
		}

		if task.Priority != nil {
			p := int(*task.Priority)
			summary.Priority = &p
		}

		if task.ScheduledOn != nil {
			s := task.ScheduledOn.Format("2006-01-02")
			summary.ScheduledOn = &s
		}

		if task.CompletedAt != nil {
			s := task.CompletedAt.Format(time.RFC3339)
			summary.CompletedAt = &s
		}

		if task.Eisenhower != nil {
			important := task.Eisenhower.IsImportant()
			urgent := task.Eisenhower.IsUrgent()
			summary.Important = &important
			summary.Urgent = &urgent
		}

		summaries = append(summaries, summary)
	}

	return summaries
}

// parseAreaURI extracts area_id and filter type from area-scoped URIs.
// Examples:
//   - lunatask://area/uuid/tasks -> uuid, FilterAll
//   - lunatask://area/uuid/today -> uuid, FilterToday
func parseAreaURI(uri string) (string, FilterType) {
	const (
		prefix    = "lunatask://area/"
		maxParts  = 2
		minLength = 1
	)

	filterNameToType := map[string]FilterType{
		"tasks":              TypeAll,
		"today":              TypeToday,
		"overdue":            TypeOverdue,
		"next-7-days":        TypeNext7Days,
		"high-priority":      TypeHighPriority,
		"now":                TypeNow,
		"recent-completions": TypeRecentCompletions,
	}

	if !strings.HasPrefix(uri, prefix) {
		return "", TypeAll
	}

	rest := strings.TrimPrefix(uri, prefix)
	parts := strings.SplitN(rest, "/", maxParts)

	if len(parts) == 0 || parts[0] == "" {
		return "", TypeAll
	}

	areaID := parts[0]

	if len(parts) == minLength {
		return areaID, TypeAll
	}

	filterType, ok := filterNameToType[parts[1]]
	if !ok {
		return "", TypeAll
	}

	return areaID, filterType
}
