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

package lunatask

import (
	"errors"
	"fmt"
	"strings"

	"github.com/google/uuid"
)

// Errors returned by deep link operations.
var (
	// ErrInvalidDeepLink is returned when parsing a malformed deep link.
	ErrInvalidDeepLink = errors.New("invalid deep link")
	// ErrInvalidReference is returned when input is neither a valid deep link nor UUID.
	ErrInvalidReference = errors.New("invalid reference")
	// ErrInvalidResource is returned when a resource type is unknown.
	ErrInvalidResource = errors.New("invalid resource")
	// ErrInvalidUUID is returned when the UUID portion is empty or malformed.
	ErrInvalidUUID = errors.New("invalid UUID")
)

// Resource represents a Lunatask resource type for deep links.
type Resource string

// Valid resource types for deep links.
const (
	ResourceArea     Resource = "areas"
	ResourceGoal     Resource = "goals"
	ResourceTask     Resource = "tasks"
	ResourceNote     Resource = "notes"
	ResourcePerson   Resource = "people"
	ResourceNotebook Resource = "notebooks"
)

// ResourceUnknown is returned when parsing a raw UUID without resource context.
const ResourceUnknown Resource = ""

// Valid reports whether r is a known resource type.
func (r Resource) Valid() bool {
	switch r {
	case ResourceArea, ResourceGoal, ResourceTask, ResourceNote, ResourcePerson, ResourceNotebook:
		return true
	case ResourceUnknown:
		return false
	}

	return false
}

// String returns the resource type as a string.
func (r Resource) String() string {
	return string(r)
}

// ValidateUUID checks whether id is a valid UUID string.
func ValidateUUID(id string) error {
	if id == "" {
		return fmt.Errorf("%w: empty", ErrInvalidUUID)
	}

	if _, err := uuid.Parse(id); err != nil {
		return fmt.Errorf("%w: %q", ErrInvalidUUID, id)
	}

	return nil
}

// ParseReference extracts resource type and UUID from a Lunatask deep link
// or plain UUID. Accepts "lunatask://tasks/uuid" or plain UUID strings.
// When a plain UUID is provided, the resource type will be ResourceUnknown.
// The UUID is validated to be a well-formed UUID string.
func ParseReference(input string) (Resource, string, error) {
	if input == "" {
		return "", "", fmt.Errorf("%w: empty input", ErrInvalidReference)
	}

	// Check for lunatask:// prefix
	const prefix = "lunatask://"
	if !strings.HasPrefix(input, prefix) {
		// Treat as plain UUID, validate format
		if err := ValidateUUID(input); err != nil {
			return "", "", fmt.Errorf("%w %q: expected UUID or lunatask:// deep link", ErrInvalidReference, input)
		}

		return ResourceUnknown, input, nil
	}

	// Remove prefix and split
	remainder := strings.TrimPrefix(input, prefix)
	parts := strings.SplitN(remainder, "/", 2) //nolint:mnd // split resource/uuid

	if len(parts) != 2 || parts[0] == "" || parts[1] == "" {
		return "", "", fmt.Errorf("%w: %q", ErrInvalidDeepLink, input)
	}

	resource := Resource(parts[0])
	id := parts[1]

	if !resource.Valid() {
		return "", "", fmt.Errorf("%w: %q", ErrInvalidResource, resource)
	}

	if err := ValidateUUID(id); err != nil {
		return "", "", err
	}

	return resource, id, nil
}

// ParseDeepLink extracts resource type and UUID from input.
//
// Deprecated: Use ParseReference instead.
func ParseDeepLink(input string) (Resource, string, error) {
	return ParseReference(input)
}

// BuildDeepLink constructs a Lunatask deep link from resource type and ID.
// Returns "lunatask://resource/uuid". The ID is validated to be a well-formed UUID.
func BuildDeepLink(resource Resource, id string) (string, error) {
	if err := ValidateUUID(id); err != nil {
		return "", err
	}

	if !resource.Valid() {
		return "", fmt.Errorf("%w: %q", ErrInvalidResource, resource)
	}

	return fmt.Sprintf("lunatask://%s/%s", resource, id), nil
}
