// Package kind includes a type for convenient handling of event kinds, and a
// kind database with reverse lookup for human-readable information about event
// kinds.
package kind

import (
	"sync"

	"golang.org/x/exp/constraints"
	"lol.mleku.dev/chk"
	"next.orly.dev/pkg/encoders/ints"
)

// K - which will be externally referenced as kind.K is the event type in the
// nostr protocol, the use of the capital K signifying type, consistent with Go
// idiom, the Go standard library, and much, conformant, existing code.
type K struct {
	K uint16
}

// New creates a new kind.K with a provided integer value. Note that anything
// larger than 2^16 will be truncated.
func New[V constraints.Integer](k V) (ki *K) { return &K{uint16(k)} }

// ToInt returns the value of the kind.K as an int.
func (k *K) ToInt() int {
	if k == nil {
		return 0
	}
	return int(k.K)
}

// ToU16 returns the value of the kind.K as an uint16 (the native form).
func (k *K) ToU16() uint16 {
	if k == nil {
		return 0
	}
	return k.K
}

// ToI32 returns the value of the kind.K as an int32.
func (k *K) ToI32() int32 {
	if k == nil {
		return 0
	}
	return int32(k.K)
}

// ToU64 returns the value of the kind.K as an uint64.
func (k *K) ToU64() uint64 {
	if k == nil {
		return 0
	}
	return uint64(k.K)
}

// Name returns the human readable string describing the semantics of the
// kind.K.
func (k *K) Name() string { return GetString(k.K) }

// Equal checks if
func (k *K) Equal(k2 uint16) bool {
	if k == nil {
		return false
	}
	return k.K == k2
}

var Privileged = []*K{
	EncryptedDirectMessage,
	GiftWrap,
	GiftWrapWithKind4,
	JWTBinding,
	ApplicationSpecificData,
	Seal,
	DirectMessage,
	FileMessage,
}

// IsPrivileged returns true if the type is the kind of message nobody else than
// the pubkeys in the event and p tags of the event are party to.
func IsPrivileged(k uint16) (is bool) {
	for i := range Privileged {
		if k == Privileged[i].K {
			return true
		}
	}
	return
}

// Marshal renders the kind.K into bytes containing the ASCII string form of the
// kind number.
func (k *K) Marshal(dst []byte) (b []byte) {
	return ints.New(k.ToU64()).Marshal(dst)
}

// Unmarshal decodes a byte string into a kind.K.
func (k *K) Unmarshal(b []byte) (r []byte, err error) {
	n := ints.New(0)
	if r, err = n.Unmarshal(b); chk.T(err) {
		return
	}
	k.K = n.Uint16()
	return
}

// GetString returns a human-readable identifier for a kind.K.
func GetString(t uint16) string {
	MapMx.RLock()
	defer MapMx.RUnlock()
	return Map[t]
}

// IsEphemeral returns true if the event kind is an ephemeral event. (not to be
// stored)
func IsEphemeral(k uint16) bool {
	return k >= EphemeralStart.K && k < EphemeralEnd.K
}

// IsReplaceable returns true if the event kind is a replaceable kind - that is,
// if the newest version is the one that is in force (eg follow lists, relay
// lists, etc.
func IsReplaceable(k uint16) bool {
	return k == ProfileMetadata.K || k == FollowList.K ||
		(k >= ReplaceableStart.K && k < ReplaceableEnd.K)
}

// IsParameterizedReplaceable is a kind of event that is one of a group of
// events that replaces based on matching criteria.
func IsParameterizedReplaceable(k uint16) bool {
	return k >= ParameterizedReplaceableStart.K &&
		k < ParameterizedReplaceableEnd.K
}

// Directory events are events that necessarily need to be readable by anyone in
// order to interact with users who have access to the relay, in order to
// facilitate other users to find and interact with users on an auth-required
// relay.
var Directory = []*K{
	ProfileMetadata,
	FollowList,
	EventDeletion,
	Reporting,
	RelayListMetadata,
	MuteList,
	DMRelaysList,
}

// IsDirectoryEvent returns whether an event kind is a Directory event, which
// should grant permission to read such events without requiring authentication.
func IsDirectoryEvent(k uint16) bool {
	for i := range Directory {
		if k == Directory[i].K {
			return true
		}
	}
	return false
}

var (
	// ProfileMetadata is an event type that stores user profile data, pet
	// names, bio, lightning address, etc.
	ProfileMetadata = &K{0}
	// SetMetadata is a synonym for ProfileMetadata.
	SetMetadata = &K{0}
	// TextNote is a standard short text note of plain text a la twitter
	TextNote = &K{1}
	// RecommendServer is an event type that...
	RecommendRelay = &K{2}
	// FollowList an event containing a list of pubkeys of users that should be
	// shown as follows in a timeline.
	FollowList = &K{3}
	Follows    = &K{3}
	// EncryptedDirectMessage is an event type that...
	EncryptedDirectMessage = &K{4}
	// EventDeletion is an event type that...
	EventDeletion = &K{5}
	Deletion      = &K{5}
	// Repost is an event type that...
	Repost = &K{6}
	// Reaction is an event type that...
	Reaction = &K{7}
	// BadgeAward is an event type
	BadgeAward = &K{8}
	// ChatMessage is an event type for NIP-C7 chat messages
	ChatMessage = &K{9}
	// GroupChatThreadedReply is deprecated
	GroupChatThreadedReply = &K{10}
	// Thread is an event type for NIP-7D threads
	Thread = &K{11}
	// GroupThreadReply is deprecated
	GroupThreadReply = &K{12}
	// Seal is an event that wraps a PrivateDirectMessage and is placed inside a
	// GiftWrap or GiftWrapWithKind4
	Seal = &K{13}
	// DirectMessage is a nip-17 direct message with a different
	// construction. It doesn't actually appear as an event a relay might receive
	// but only as the stringified content of a GiftWrap or GiftWrapWithKind4 inside
	// a
	DirectMessage = &K{14}
	// FileMessage is a NIP-17 file message
	FileMessage = &K{15}
	// GenericRepost is an event type that...
	GenericRepost = &K{16}
	// ReactionToWebsite is a reaction to a website
	ReactionToWebsite = &K{17}
	// Picture is an event type for NIP-68 picture-first feeds
	Picture = &K{20}
	// VideoEventHorizontal is for horizontal video events
	VideoEventHorizontal = &K{34235}
	// VideoEventVertical is for vertical video events
	VideoEventVertical = &K{34236}
	// ChannelCreation is an event type that...
	ChannelCreation = &K{40}
	// ChannelMetadata is an event type that...
	ChannelMetadata = &K{41}
	// ChannelMessage is an event type that...
	ChannelMessage = &K{42}
	// ChannelHideMessage is an event type that...
	ChannelHideMessage = &K{43}
	// ChannelMuteUser is an event type that...
	ChannelMuteUser = &K{44}
	// Bid is an event type that...
	Bid = &K{1021}
	// BidConfirmation is an event type that...
	BidConfirmation = &K{1022}
	// OpenTimestamps is an event type that...
	OpenTimestamps    = &K{1040}
	GiftWrap          = &K{1059}
	GiftWrapWithKind4 = &K{1060}
	// FileMetadata is an event type that...
	FileMetadata = &K{1063}
	// LiveChatMessage is an event type that...
	LiveChatMessage = &K{1311}
	// BitcoinBlock is an event type created for the Nostrocket
	BitcoinBlock = &K{1517}
	// LiveStream from zap.stream
	LiveStream = &K{1808}
	// ProblemTracker is an event type used by Nostrocket
	ProblemTracker = &K{1971}
	// MemoryHole is an event type contains a report about an event (usually
	// text note or other human readable)
	MemoryHole = &K{1984}
	Reporting  = &K{1984}
	// Label is an event type has L and l tags, namespace and type - NIP-32
	Label = &K{1985}
	// CommunityPostApproval is an event type that...
	CommunityPostApproval = &K{4550}
	JobRequestStart       = &K{5000}
	JobRequestEnd         = &K{5999}
	JobResultStart        = &K{6000}
	JobResultEnd          = &K{6999}
	JobFeedback           = &K{7000}
	ZapGoal               = &K{9041}
	// ZapRequest is an event type that...
	ZapRequest = &K{9734}
	// Zap is an event type that...
	Zap        = &K{9735}
	Highlights = &K{9882}
	// ReplaceableStart is an event type that...
	ReplaceableStart = &K{10000}
	// MuteList is an event type that...
	MuteList  = &K{10000}
	BlockList = &K{10000}
	// PinList is an event type that...
	PinList = &K{10001}
	// RelayListMetadata is an event type that...
	RelayListMetadata     = &K{10002}
	BookmarkList          = &K{10003}
	CommunitiesList       = &K{10004}
	PublicChatsList       = &K{10005}
	BlockedRelaysList     = &K{10006}
	SearchRelaysList      = &K{10007}
	InterestsList         = &K{10015}
	UserEmojiList         = &K{10030}
	DMRelaysList          = &K{10050}
	FileStorageServerList = &K{10096}
	// JWTBinding is an event kind that creates a link between a JWT certificate and a pubkey
	JWTBinding = &K{13004}
	// NWCWalletServiceInfo is an event type that...
	NWCWalletServiceInfo = &K{13194}
	WalletServiceInfo    = &K{13194}
	// ReplaceableEnd is an event type that...
	ReplaceableEnd = &K{19999}
	// EphemeralStart is an event type that...
	EphemeralStart  = &K{20000}
	LightningPubRPC = &K{21000}
	// ClientAuthentication is an event type that...
	ClientAuthentication = &K{22242}
	// NWCWalletRequest is an event type that...
	NWCWalletRequest = &K{23194}
	WalletRequest    = &K{23194}
	// NWCWalletResponse is an event type that...
	NWCWalletResponse      = &K{23195}
	WalletResponse         = &K{23195}
	NWCNotification        = &K{23196}
	WalletNotificationNip4 = &K{23196}
	WalletNotification     = &K{23197}
	// NostrConnect is an event type that...
	NostrConnect = &K{24133}
	HTTPAuth     = &K{27235}
	// EphemeralEnd is an event type that...
	EphemeralEnd = &K{29999}
	// ParameterizedReplaceableStart is an event type that...
	ParameterizedReplaceableStart = &K{30000}
	// CategorizedPeopleList is an event type that...
	CategorizedPeopleList = &K{30000}
	FollowSets            = &K{30000}
	// CategorizedBookmarksList is an event type that...
	CategorizedBookmarksList = &K{30001}
	GenericLists             = &K{30001}
	RelaySets                = &K{30002}
	BookmarkSets             = &K{30003}
	CurationSets             = &K{30004}
	// ProfileBadges is an event type that...
	ProfileBadges = &K{30008}
	// BadgeDefinition is an event type that...
	BadgeDefinition = &K{30009}
	InterestSets    = &K{30015}
	// StallDefinition creates or updates a stall
	StallDefinition = &K{30017}
	// ProductDefinition creates or updates a product
	ProductDefinition    = &K{30018}
	MarketplaceUIUX      = &K{30019}
	ProductSoldAsAuction = &K{30020}
	// Article is an event type that...
	Article              = &K{30023}
	LongFormContent      = &K{30023}
	DraftLongFormContent = &K{30024}
	EmojiSets            = &K{30030}
	// ApplicationSpecificData is an event type stores data about application
	// configuration, this, like DMs and giftwraps must be protected by user
	// auth.
	ApplicationSpecificData = &K{30078}
	LiveEvent               = &K{30311}
	UserStatuses            = &K{30315}
	ClassifiedListing       = &K{30402}
	DraftClassifiedListing  = &K{30403}
	DateBasedCalendarEvent  = &K{31922}
	TimeBasedCalendarEvent  = &K{31923}
	Calendar                = &K{31924}
	CalendarEventRSVP       = &K{31925}
	HandlerRecommendation   = &K{31989}
	HandlerInformation      = &K{31990}
	// WaveLakeTrack which has no spec and uses malformed tags
	WaveLakeTrack       = &K{32123}
	CommunityDefinition = &K{34550}
	ACLEvent            = &K{39998}
	// ParameterizedReplaceableEnd is an event type that...
	ParameterizedReplaceableEnd = &K{40000}
)

var MapMx sync.RWMutex
var Map = map[uint16]string{
	ProfileMetadata.K:         "User Metadata",
	TextNote.K:                "Short Text Note",
	RecommendRelay.K:          "Recommend Relay",
	FollowList.K:              "Follows",
	EncryptedDirectMessage.K:  "Encrypted Direct Messages",
	EventDeletion.K:           "Event Deletion Request",
	Repost.K:                  "Repost",
	Reaction.K:                "Reaction",
	BadgeAward.K:              "Badge Award",
	ChatMessage.K:             "Chat Message",
	GroupChatThreadedReply.K:  "Group Chat Threaded Reply",
	Thread.K:                  "Thread",
	GroupThreadReply.K:        "Group Thread Reply",
	Seal.K:                    "Seal",
	DirectMessage.K:           "Direct Message",
	FileMessage.K:             "File Message",
	GenericRepost.K:           "Generic Repost",
	ReactionToWebsite.K:       "Reaction to a website",
	Picture.K:                 "Picture",
	ChannelCreation.K:         "Channel Creation",
	ChannelMetadata.K:         "Channel Metadata",
	ChannelMessage.K:          "Channel Message",
	ChannelHideMessage.K:      "Channel Hide Message",
	ChannelMuteUser.K:         "Channel Mute User",
	Bid.K:                     "Bid",
	BidConfirmation.K:         "Bid Confirmation",
	OpenTimestamps.K:          "OpenTimestamps",
	FileMetadata.K:            "File Metadata",
	LiveChatMessage.K:         "Live Chat Message",
	ProblemTracker.K:          "Problem Tracker",
	Reporting.K:               "Reporting",
	Label.K:                   "Label",
	CommunityPostApproval.K:   "Community Post Approval",
	JobRequestStart.K:         "Job Request",
	JobRequestEnd.K:           "Job Request",
	JobResultStart.K:          "Job Result",
	JobResultEnd.K:            "Job Result",
	JobFeedback.K:             "Job Feedback",
	ZapGoal.K:                 "Zap Goal",
	ZapRequest.K:              "Zap Request",
	Zap.K:                     "Zap",
	Highlights.K:              "Highlights",
	BlockList.K:               "Mute list",
	PinList.K:                 "Pin list",
	RelayListMetadata.K:       "Relay List Metadata",
	BookmarkList.K:            "Bookmarks list",
	CommunitiesList.K:         "Communities list",
	PublicChatsList.K:         "Public Chats list",
	BlockedRelaysList.K:       "Blocked Relays list",
	SearchRelaysList.K:        "Search Relays list",
	InterestsList.K:           "Interests",
	UserEmojiList.K:           "User Emoji list",
	DMRelaysList.K:            "DM relays",
	FileStorageServerList.K:   "File Storage Server List",
	NWCWalletServiceInfo.K:    "Wallet Service Info",
	LightningPubRPC.K:         "Lightning pub RPC",
	ClientAuthentication.K:    "Client Authentication",
	WalletRequest.K:           "Wallet Request",
	WalletResponse.K:          "Wallet Response",
	WalletNotificationNip4.K:  "Wallet Notification",
	WalletNotification.K:      "Wallet Notification",
	NostrConnect.K:            "Nostr Connect",
	HTTPAuth.K:                "HTTP Auth",
	FollowSets.K:              "Follow sets",
	GenericLists.K:            "Generic lists",
	RelaySets.K:               "Relay sets",
	BookmarkSets.K:            "Bookmark sets",
	CurationSets.K:            "Curation sets",
	ProfileBadges.K:           "Profile Badges",
	BadgeDefinition.K:         "Badge Definition",
	InterestSets.K:            "Interest sets",
	StallDefinition.K:         "Stall Definition",
	ProductDefinition.K:       "Product Definition",
	MarketplaceUIUX.K:         "Marketplace UI/UX",
	ProductSoldAsAuction.K:    "Product sold as an auction",
	LongFormContent.K:         "Long-form Content",
	DraftLongFormContent.K:    "Draft Long-form Content",
	EmojiSets.K:               "Emoji sets",
	ApplicationSpecificData.K: "Application-specific Data",
	LiveEvent.K:               "Live Event",
	UserStatuses.K:            "User Statuses",
	ClassifiedListing.K:       "Classified Listing",
	DraftClassifiedListing.K:  "Draft Classified Listing",
	DateBasedCalendarEvent.K:  "Date-Based Calendar Event",
	TimeBasedCalendarEvent.K:  "Time-Based Calendar Event",
	Calendar.K:                "Calendar",
	CalendarEventRSVP.K:       "Calendar Event RSVP",
	HandlerRecommendation.K:   "Handler recommendation",
	HandlerInformation.K:      "Handler information",
	CommunityDefinition.K:     "Community Definition",
	VideoEventHorizontal.K:    "Video Event Horizontal",
	VideoEventVertical.K:      "Video Event Vertical",
}
