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

package task

import (
	"context"

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

// UpdateToolName is the name of the update task tool.
const UpdateToolName = "update_task"

// UpdateToolDescription describes the update task tool for LLMs.
const UpdateToolDescription = `Updates an existing task in Lunatask.

Required:
- id: Task UUID or lunatask:// deep link

Optional (only specified fields are updated):
- name: New task title
- area_id: Move to area (UUID, lunatask:// deep link, or config key)
- goal_id: Move to goal (UUID, lunatask:// deep link, or config key; requires area_id)
- status: later, next, started, waiting, completed
- note: New markdown note (replaces existing)
- priority: lowest, low, normal, high, highest
- estimate: Time estimate in minutes (0-720)
- motivation: must, should, want
- important: true/false for Eisenhower matrix
- urgent: true/false for Eisenhower matrix
- scheduled_on: Date to schedule (YYYY-MM-DD)

Returns the updated task's ID and deep link.`

// UpdateInput is the input schema for updating a task.
type UpdateInput struct {
	ID          string  `json:"id"                     jsonschema:"required"`
	Name        *string `json:"name,omitempty"`
	AreaID      *string `json:"area_id,omitempty"`
	GoalID      *string `json:"goal_id,omitempty"`
	Status      *string `json:"status,omitempty"`
	Note        *string `json:"note,omitempty"`
	Priority    *string `json:"priority,omitempty"`
	Estimate    *int    `json:"estimate,omitempty"`
	Motivation  *string `json:"motivation,omitempty"`
	Important   *bool   `json:"important,omitempty"`
	Urgent      *bool   `json:"urgent,omitempty"`
	ScheduledOn *string `json:"scheduled_on,omitempty"`
}

// UpdateOutput is the output schema for updating a task.
type UpdateOutput struct {
	DeepLink string `json:"deep_link"`
}

// parsedUpdateInput holds validated and parsed update input fields.
type parsedUpdateInput struct {
	ID          string
	Name        *string
	AreaID      *string
	GoalID      *string
	Status      *lunatask.TaskStatus
	Note        *string
	Priority    *lunatask.Priority
	Estimate    *int
	Motivation  *lunatask.Motivation
	Important   *bool
	Urgent      *bool
	ScheduledOn *lunatask.Date
}

// HandleUpdate updates an existing task.
func (h *Handler) HandleUpdate(
	ctx context.Context,
	_ *mcp.CallToolRequest,
	input UpdateInput,
) (*mcp.CallToolResult, UpdateOutput, error) {
	parsed, errResult := parseUpdateInput(h.cfg, input)
	if errResult != nil {
		return errResult, UpdateOutput{}, nil
	}

	builder := h.client.NewTaskUpdate(parsed.ID)
	applyToTaskUpdateBuilder(builder, parsed)

	task, err := builder.Update(ctx)
	if err != nil {
		return shared.ErrorResult(err.Error()), UpdateOutput{}, nil
	}

	deepLink, _ := lunatask.BuildDeepLink(lunatask.ResourceTask, task.ID)

	return &mcp.CallToolResult{
		Content: []mcp.Content{&mcp.TextContent{
			Text: "Task updated: " + deepLink,
		}},
	}, UpdateOutput{DeepLink: deepLink}, nil
}

//nolint:cyclop,funlen
func parseUpdateInput(cfg *config.Config, input UpdateInput) (*parsedUpdateInput, *mcp.CallToolResult) {
	_, id, err := lunatask.ParseReference(input.ID)
	if err != nil {
		return nil, shared.ErrorResult("invalid ID: expected UUID or lunatask:// deep link")
	}

	parsed := &parsedUpdateInput{
		ID:        id,
		Name:      input.Name,
		Note:      input.Note,
		Estimate:  input.Estimate,
		Important: input.Important,
		Urgent:    input.Urgent,
	}

	if input.AreaID != nil {
		areaID, err := validate.AreaRef(cfg, *input.AreaID)
		if err != nil {
			return nil, shared.ErrorResult(err.Error())
		}

		parsed.AreaID = &areaID
	}

	if input.GoalID != nil {
		areaID := ""
		if parsed.AreaID != nil {
			areaID = *parsed.AreaID
		}

		goalID, err := validate.GoalRef(cfg, areaID, *input.GoalID)
		if err != nil {
			return nil, shared.ErrorResult(err.Error())
		}

		parsed.GoalID = &goalID
	}

	if input.Estimate != nil {
		if err := shared.ValidateEstimate(*input.Estimate); err != nil {
			return nil, shared.ErrorResult(err.Error())
		}
	}

	if input.Status != nil {
		status, err := lunatask.ParseTaskStatus(*input.Status)
		if err != nil {
			return nil, shared.ErrorResult(err.Error())
		}

		parsed.Status = &status
	}

	if input.Priority != nil {
		priority, err := lunatask.ParsePriority(*input.Priority)
		if err != nil {
			return nil, shared.ErrorResult(err.Error())
		}

		parsed.Priority = &priority
	}

	if input.Motivation != nil {
		motivation, err := lunatask.ParseMotivation(*input.Motivation)
		if err != nil {
			return nil, shared.ErrorResult(err.Error())
		}

		parsed.Motivation = &motivation
	}

	if input.ScheduledOn != nil {
		date, err := dateutil.Parse(*input.ScheduledOn)
		if err != nil {
			return nil, shared.ErrorResult(err.Error())
		}

		parsed.ScheduledOn = &date
	}

	return parsed, nil
}

//nolint:cyclop
func applyToTaskUpdateBuilder(builder *lunatask.TaskUpdateBuilder, parsed *parsedUpdateInput) {
	if parsed.Name != nil {
		builder.Name(*parsed.Name)
	}

	if parsed.AreaID != nil {
		builder.InArea(*parsed.AreaID)
	}

	if parsed.GoalID != nil {
		builder.InGoal(*parsed.GoalID)
	}

	if parsed.Status != nil {
		builder.WithStatus(*parsed.Status)
	}

	if parsed.Note != nil {
		builder.WithNote(*parsed.Note)
	}

	if parsed.Priority != nil {
		builder.Priority(*parsed.Priority)
	}

	if parsed.Estimate != nil {
		builder.WithEstimate(*parsed.Estimate)
	}

	if parsed.Motivation != nil {
		builder.WithMotivation(*parsed.Motivation)
	}

	if parsed.Important != nil {
		if *parsed.Important {
			builder.Important()
		} else {
			builder.NotImportant()
		}
	}

	if parsed.Urgent != nil {
		if *parsed.Urgent {
			builder.Urgent()
		} else {
			builder.NotUrgent()
		}
	}

	if parsed.ScheduledOn != nil {
		builder.ScheduledOn(*parsed.ScheduledOn)
	}
}
