--[[
	Brand — instance brands, one weak-key set per brand.
	`.claude/base/brand-plan.md` "구현 — 인스턴스 브랜드" / "`isX` wrapper" sections,
	as-is.

	`Brand()` makes one brand object holding a weak-key member set; a value
	registers itself into the brand(s) it belongs to (multi-tagging is the
	point — `Source` is both `SourceBrand` and `EpochBrand`). There is no
	reverse lookup (`Brand.get(x) -> tag` was reversed, see
	`archive/brand-shared-registry-reversed.md`).

	Every brand instance lives in this leaf file (M2 first-unit plan,
	`qa-request/pre-implementation-handtrace-round11-brief.md` §6): `EpochBrand`
	is shared by `Source`/`Ref`/`GateNode`, so keeping the instances per type
	module would force a circular require. Each *type* still registers only
	into its own brand at its own construction sites; subtype relations are
	expressed once, in the predicate (`isState`/`isRef` below), never by
	double registration.

	Method names are lowercase (`:register`/`:is`) on purpose — this is a
	base-internal utility, not a public quad surface (`brand-plan.md`).

	`Brand` depends on nothing. In particular there is no `None` special
	case here — `isNone` is `v == None` and lives with `None.luau` (M3).
]]

export type Brand = {
	register: (self: Brand, x: any) -> (),
	is: (self: Brand, x: any) -> boolean,
}

local function Brand(): Brand
	local members = setmetatable({}, { __mode = "k" }) :: { [any]: true }
	return {
		register = function(_self: Brand, x: any)
			members[x] = true
		end,
		is = function(_self: Brand, x: any): boolean
			return members[x] == true
		end,
	}
end

-- Each type owns exactly one brand. The full list is declared here even for
-- types that arrive in later milestones (`Tag`/`Attribute`/`Tween`/`Slot`)
-- so that the set of brands has one source; their `is*` predicates are added
-- with the type.
local ObserverBrand = Brand()
local EffectBrand = Brand()
local TagBrand = Brand()
local AttributeBrand = Brand()
local TweenBrand = Brand()
local BlockerBrand = Brand()
local StateBrand = Brand()
local SourceBrand = Brand()
local StoreBrand = Brand()
local SlotBrand = Brand()
local RefBrand = Brand()
local PreRefBrand = Brand()
local PostRefBrand = Brand()
local ModifierBrand = Brand()
local EpochBrand = Brand()

-- Plain identity predicates.
local function isEpoch(x: any): boolean
	return EpochBrand:is(x)
end
local function isSource(x: any): boolean
	return SourceBrand:is(x)
end
local function isStore(x: any): boolean
	return StoreBrand:is(x)
end
local function isObserver(x: any): boolean
	return ObserverBrand:is(x)
end
local function isEffect(x: any): boolean
	return EffectBrand:is(x)
end
local function isBlocker(x: any): boolean
	return BlockerBrand:is(x)
end
local function isModifier(x: any): boolean
	return ModifierBrand:is(x)
end

-- Subtype relations: the more specific predicate is defined first and the
-- wider one is composed on top of it, so the direction of inclusion is
-- visible in the code (`brand-plan.md`).
local function isState(x: any): boolean
	return isSource(x) or StateBrand:is(x) -- Source structurally satisfies State
end

local function isPreRef(x: any): boolean
	return PreRefBrand:is(x)
end
local function isPostRef(x: any): boolean
	return PostRefBrand:is(x)
end
local function isRef(x: any): boolean
	-- PreRef/PostRef reuse the Ref runtime = both are a kind of Ref.
	-- They are exclusive siblings of each other.
	return isPreRef(x) or isPostRef(x) or RefBrand:is(x)
end

return {
	Brand = Brand,

	ObserverBrand = ObserverBrand,
	EffectBrand = EffectBrand,
	TagBrand = TagBrand,
	AttributeBrand = AttributeBrand,
	TweenBrand = TweenBrand,
	BlockerBrand = BlockerBrand,
	StateBrand = StateBrand,
	SourceBrand = SourceBrand,
	StoreBrand = StoreBrand,
	SlotBrand = SlotBrand,
	RefBrand = RefBrand,
	PreRefBrand = PreRefBrand,
	PostRefBrand = PostRefBrand,
	ModifierBrand = ModifierBrand,
	EpochBrand = EpochBrand,

	isEpoch = isEpoch,
	isSource = isSource,
	isState = isState,
	isStore = isStore,
	isObserver = isObserver,
	isEffect = isEffect,
	isBlocker = isBlocker,
	isModifier = isModifier,
	isRef = isRef,
	isPreRef = isPreRef,
	isPostRef = isPostRef,
}
