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

package lunatask

import (
	"encoding/json"
	"errors"
	"fmt"
	"strings"
)

// Errors returned by Priority operations.
var (
	// ErrInvalidPriority is returned when parsing an unknown priority string.
	ErrInvalidPriority = errors.New("invalid priority")
	// ErrPriorityOutOfRange is returned when a priority value is outside [-2, 2].
	ErrPriorityOutOfRange = errors.New("priority out of range")
)

// Priority represents task priority level from lowest (-2) to highest (2).
// The default priority is [PriorityNormal] (0).
type Priority int

// Task priority levels. See [Priority] for the full range.
const (
	PriorityLowest  Priority = -2
	PriorityLow     Priority = -1
	PriorityNormal  Priority = 0
	PriorityHigh    Priority = 1
	PriorityHighest Priority = 2
)

// String returns the lowercase name of the priority level.
func (p *Priority) String() string {
	switch *p {
	case PriorityLowest:
		return "lowest"
	case PriorityLow:
		return "low"
	case PriorityNormal:
		return "normal"
	case PriorityHigh:
		return "high"
	case PriorityHighest:
		return "highest"
	default:
		return fmt.Sprintf("Priority(%d)", *p)
	}
}

// Valid reports whether the priority is within the valid range [-2, 2].
func (p *Priority) Valid() bool {
	return *p >= PriorityLowest && *p <= PriorityHighest
}

// AllPriorities returns all valid priority values from lowest to highest.
func AllPriorities() []Priority {
	return []Priority{
		PriorityLowest,
		PriorityLow,
		PriorityNormal,
		PriorityHigh,
		PriorityHighest,
	}
}

// ParsePriority parses a string to a Priority value (case-insensitive).
// Valid values: "lowest", "low", "normal", "high", "highest".
func ParsePriority(str string) (Priority, error) {
	switch strings.ToLower(str) {
	case "lowest":
		return PriorityLowest, nil
	case "low":
		return PriorityLow, nil
	case "normal":
		return PriorityNormal, nil
	case "high":
		return PriorityHigh, nil
	case "highest":
		return PriorityHighest, nil
	default:
		return 0, fmt.Errorf("%w: %q", ErrInvalidPriority, str)
	}
}

// MarshalJSON implements [json.Marshaler].
// Priority marshals as its integer value.
func (p *Priority) MarshalJSON() ([]byte, error) {
	data, err := json.Marshal(int(*p))
	if err != nil {
		return nil, fmt.Errorf("marshaling priority: %w", err)
	}

	return data, nil
}

// UnmarshalJSON implements [json.Unmarshaler].
// Priority unmarshals from an integer value in the range [-2, 2].
func (p *Priority) UnmarshalJSON(data []byte) error {
	if string(data) == "null" {
		*p = PriorityNormal

		return nil
	}

	var val int
	if err := json.Unmarshal(data, &val); err != nil {
		return fmt.Errorf("priority must be an integer: %w", err)
	}

	if val < -2 || val > 2 {
		return fmt.Errorf("%w: %d", ErrPriorityOutOfRange, val)
	}

	*p = Priority(val)

	return nil
}
