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

// Package validate provides input validation helpers.
package validate

import (
	"errors"
	"fmt"
	"slices"
	"strings"

	"git.secluded.site/go-lunatask"
	"git.secluded.site/lune/internal/config"
)

// ErrInvalidReference indicates the input is not a valid UUID or deep link.
var ErrInvalidReference = errors.New("invalid reference: expected UUID or lunatask:// deep link")

// Reference parses a UUID or Lunatask deep link and returns the normalised UUID.
// Accepts formats:
//   - UUID: "3bbf1923-64ae-4bcf-96a9-9bb86c799dab"
//   - Deep link: "lunatask://areas/3bbf1923-64ae-4bcf-96a9-9bb86c799dab"
func Reference(input string) (string, error) {
	_, id, err := lunatask.ParseReference(input)
	if err != nil {
		return "", fmt.Errorf("%w: %s", ErrInvalidReference, input)
	}

	return id, nil
}

// ErrInvalidStatus indicates the status value is not recognized.
var ErrInvalidStatus = errors.New("invalid status")

// ErrInvalidStatusForWorkflow indicates the status is not valid for the area's workflow.
var ErrInvalidStatusForWorkflow = errors.New("invalid status for workflow")

// TaskStatus validates and normalizes a task status string.
// Returns the corresponding lunatask.TaskStatus or an error if invalid.
func TaskStatus(input string) (lunatask.TaskStatus, error) {
	status, err := lunatask.ParseTaskStatus(input)
	if err != nil {
		return "", fmt.Errorf("%w: %s", ErrInvalidStatus, input)
	}

	return status, nil
}

// TaskStatusForWorkflow validates a status string against a specific workflow.
// Returns the status if valid for the workflow, or an error listing valid options.
func TaskStatusForWorkflow(input string, workflow lunatask.Workflow) (lunatask.TaskStatus, error) {
	status, err := lunatask.ParseTaskStatus(input)
	if err != nil {
		return "", fmt.Errorf(
			"%w: '%s' for %s; valid: %s",
			ErrInvalidStatusForWorkflow, input, workflow.Description(), formatValidStatuses(workflow),
		)
	}

	if !slices.Contains(workflow.ValidStatuses(), status) {
		return "", fmt.Errorf(
			"%w: '%s' not valid for %s; valid: %s",
			ErrInvalidStatusForWorkflow, input, workflow.Description(), formatValidStatuses(workflow),
		)
	}

	return status, nil
}

func formatValidStatuses(workflow lunatask.Workflow) string {
	statuses := workflow.ValidStatuses()
	strs := make([]string, len(statuses))

	for i, s := range statuses {
		strs[i] = string(s)
	}

	return strings.Join(strs, ", ")
}

// ErrInvalidMotivation indicates the motivation value is not recognized.
var ErrInvalidMotivation = errors.New("invalid motivation")

// Motivation validates and normalizes a motivation string.
// Returns the corresponding lunatask.Motivation or an error if invalid.
func Motivation(input string) (lunatask.Motivation, error) {
	motivation, err := lunatask.ParseMotivation(input)
	if err != nil {
		return "", fmt.Errorf("%w: %s", ErrInvalidMotivation, input)
	}

	return motivation, nil
}

// ErrInvalidRelationship indicates the relationship strength is not recognized.
var ErrInvalidRelationship = errors.New("invalid relationship strength")

// RelationshipStrength validates and normalizes a relationship strength string.
// Returns the corresponding lunatask.RelationshipStrength or an error if invalid.
func RelationshipStrength(input string) (lunatask.RelationshipStrength, error) {
	rel, err := lunatask.ParseRelationshipStrength(input)
	if err != nil {
		return "", fmt.Errorf("%w: %s", ErrInvalidRelationship, input)
	}

	return rel, nil
}

// ErrInvalidArea indicates the area reference is not a valid UUID, deep link, or config key.
var ErrInvalidArea = errors.New("invalid area: expected UUID, lunatask:// deep link, or config key")

// AreaRef resolves an area reference to a UUID.
// Accepts formats:
//   - UUID: "527a2b42-99fd-490d-8b21-c55451368f4c"
//   - Deep link: "lunatask://areas/527a2b42-..."
//   - Config key: "projects"
func AreaRef(cfg *config.Config, 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
	if area := cfg.AreaByKey(input); area != nil {
		return area.ID, nil
	}

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

// ErrInvalidGoal indicates the goal reference is not a valid UUID, deep link, or config key.
var ErrInvalidGoal = errors.New("invalid goal: expected UUID, lunatask:// deep link, or config key")

// GoalRef resolves a goal reference to a UUID.
// Requires a valid area ID to look up goals by key (goals are scoped to areas).
// Accepts formats:
//   - UUID: "53ca909e-887d-4ed2-9943-d1212adf8ad8"
//   - Deep link: "lunatask://goals/53ca909e-..."
//   - Config key: "lunatask" (requires valid areaID for lookup)
func GoalRef(cfg *config.Config, areaID, 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 (requires area context)
	if area := cfg.AreaByID(areaID); area != nil {
		if goal := area.GoalByKey(input); goal != nil {
			return goal.ID, nil
		}
	}

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