package domain

import (
	"fmt"
	"strconv"

	"source.toby3d.me/toby3d/auth/internal/common"
)

// Action represent action for token endpoint supported by IndieAuth.
//
// NOTE(toby3d): Encapsulate enums in structs for extra compile-time safety:
// https://threedots.tech/post/safer-enums-in-go/#struct-based-enums
type Action struct {
	action string
}

//nolint:gochecknoglobals // structs cannot be constants
var (
	ActionUnd = Action{action: ""} // "und"

	// ActionRevoke represent action for revoke token.
	ActionRevoke = Action{action: "revoke"} // "revoke"

	// ActionTicket represent action for TicketAuth extension.
	ActionTicket = Action{action: "ticket"} // "ticket"
)

var ErrActionSyntax error = NewError(ErrorCodeInvalidRequest, "unknown action method", "")

//nolint:gochecknoglobals
var uidsActions = map[string]Action{
	ActionRevoke.action: ActionRevoke,
	ActionTicket.action: ActionTicket,
}

// ParseAction parse string identifier of action into struct enum.
func ParseAction(uid string) (Action, error) {
	if action, ok := uidsActions[uid]; ok {
		return action, nil
	}

	return ActionUnd, fmt.Errorf("%w: %s", ErrActionSyntax, uid)
}

// UnmarshalForm implements custom unmarshler for form values.
func (a *Action) UnmarshalForm(v []byte) error {
	action, err := ParseAction(string(v))
	if err != nil {
		return fmt.Errorf("Action: UnmarshalForm: %w", err)
	}

	*a = action

	return nil
}

// UnmarshalJSON implements custom unmarshler for JSON.
func (a *Action) UnmarshalJSON(v []byte) error {
	src, err := strconv.Unquote(string(v))
	if err != nil {
		return fmt.Errorf("Action: UnmarshalJSON: %w", err)
	}

	action, err := ParseAction(src)
	if err != nil {
		return fmt.Errorf("Action: UnmarshalJSON: %w", err)
	}

	*a = action

	return nil
}

// String returns string representation of action.
func (a Action) String() string {
	if a.action != "" {
		return a.action
	}

	return common.Und
}

func (a Action) GoString() string {
	return "domain.Action(" + a.String() + ")"
}
