BuyReveal.cs
namespace NoChillquarium;

/// <summary>Pack-open phases for a shop fish buy. Fast on purpose.</summary>
public enum BuyRevealPhase
{
	Off = 0,
	/// <summary>Card back drops in.</summary>
	Deal = 1,
	/// <summary>Paper flaps peel. Tease gold on every card.</summary>
	Unfold = 2,
	/// <summary>Fish visible, rarity still hidden.</summary>
	Show = 3,
	/// <summary>Rarity stamp slams.</summary>
	Stamp = 4,
	/// <summary>Hold, then auto-dismiss (commons fast; jackpots linger).</summary>
	Hold = 5
}

/// <summary>
/// Chillquarium-style buy juice: spend snaps, card unfolds with ticks,
/// rarity stamp is the slot-stop. Shop packs stay clickable so you mash.
/// </summary>
public static class BuyReveal
{
	static BuyRevealPhase _phase;
	static FishSpecies _species;
	static FishRarity _rarity;
	static float _time;
	static float _stampAt;
	static float _holdFor;
	static int _lastTick = -1;
	static int _version;
	static bool _bannered;

	public static int Version => _version;
	public static bool IsActive => _phase != BuyRevealPhase.Off;
	public static BuyRevealPhase Phase => _phase;
	public static FishSpecies Species => _species;
	public static FishRarity Rarity => _rarity;
	public static string SpeciesName => _species?.Name ?? "Fish";
	public static string RarityLabel => FishRarityInfo.Label( _rarity );
	public static string RarityCss => FishRarityInfo.CssClass( _rarity );
	public static string SpriteId => _species?.SpriteId ?? "";
	public static bool HasSprite => _species?.HasSprite == true;
	public static string Color => _species?.Color ?? "#80c090";
	public static bool FishVisible => (int)_phase >= (int)BuyRevealPhase.Show;
	public static bool Revealed => (int)_phase >= (int)BuyRevealPhase.Stamp;
	public static bool IsJackpot => _rarity >= FishRarity.Legendary;
	public static bool IsSpicy => _rarity >= FishRarity.PaintedRare;

	/// <summary>Gold flicker during deal/unfold — every card might be the one.</summary>
	public static bool Teasing =>
		_phase is BuyRevealPhase.Deal or BuyRevealPhase.Unfold or BuyRevealPhase.Show;

	public static string Hint
	{
		get
		{
			if ( !IsActive )
				return "Pick a pack";
			if ( !Revealed )
				return "click · skip";
			if ( IsJackpot )
				return "click · keep it";
			if ( IsSpicy )
				return "click · nice";
			return "click · next";
		}
	}

	public static string CardClass
	{
		get
		{
			var phase = _phase.ToString().ToLowerInvariant();
			var spicy = IsSpicy && Revealed ? " spicy" : "";
			var jack = IsJackpot && Revealed ? " jackpot" : "";
			var tease = Teasing ? " tease" : "";
			var shown = FishVisible ? " fish-on" : "";
			var stamp = Revealed ? " revealed" : "";
			return $"buy-card {RarityCss} phase-{phase}{tease}{shown}{stamp}{spicy}{jack}";
		}
	}

	public static void Clear()
	{
		_phase = BuyRevealPhase.Off;
		_species = null;
		_rarity = FishRarity.Common;
		_time = 0f;
		_stampAt = 0f;
		_holdFor = 0f;
		_lastTick = -1;
		_bannered = false;
		_version++;
	}

	/// <summary>Start a pack-open. Commons snap over an in-flight common; jackpots still yield.</summary>
	public static void Begin( FishSpecies species, FishRarity rarity )
	{
		if ( species is null )
			return;

		_species = species;
		_rarity = rarity;
		_time = 0f;
		_lastTick = -1;
		_bannered = false;
		_phase = BuyRevealPhase.Deal;

		// Variable delay = the slot linger. Subtle per tier so rares feel "heavier".
		var tier = (int)rarity;
		_stampAt = 0.20f + tier * 0.055f;
		_holdFor = rarity switch
		{
			FishRarity.Common => 0.18f,
			FishRarity.Uncommon => 0.30f,
			FishRarity.Rare => 0.52f,
			FishRarity.PaintedRare => 0.85f,
			FishRarity.Legendary => 1.7f,
			_ => 2.3f
		};

		_version++;
		PlayTick( 0 );
	}

	/// <summary>Click the card: skip to stamp, or dismiss if already shown.</summary>
	public static void Click()
	{
		if ( !IsActive )
			return;
		if ( !Revealed )
		{
			SkipToStamp();
			return;
		}
		Dismiss();
	}

	/// <summary>Shop closed — stamp if we were mid-flip so the SFX still lands, then dump.</summary>
	public static void OnShopClosed()
	{
		if ( !IsActive )
			return;
		if ( !Revealed && _rarity >= FishRarity.Rare )
			SkipToStamp();
		Dismiss();
	}

	public static void Tick( float dt )
	{
		if ( !IsActive )
			return;
		if ( dt <= 0f )
			return;
		if ( dt > 0.05f )
			dt = 0.05f;

		_time += dt;
		var next = ResolvePhase( _time );
		if ( next != _phase )
		{
			_phase = next;
			var tick = (int)_phase - 1; // Deal=0 … Stamp=3
			if ( tick >= 0 && tick <= 3 && tick > _lastTick )
			{
				_lastTick = tick;
				PlayTick( tick );
			}
			if ( _phase == BuyRevealPhase.Stamp )
				OnStamped();
			_version++;
		}

		if ( _phase == BuyRevealPhase.Hold && _time >= _stampAt + _holdFor )
			Dismiss();
	}

	static BuyRevealPhase ResolvePhase( float t )
	{
		var unfoldAt = _stampAt * 0.28f;
		var showAt = _stampAt * 0.58f;
		if ( t < unfoldAt )
			return BuyRevealPhase.Deal;
		if ( t < showAt )
			return BuyRevealPhase.Unfold;
		if ( t < _stampAt )
			return BuyRevealPhase.Show;
		if ( t < _stampAt + 0.08f )
			return BuyRevealPhase.Stamp;
		return BuyRevealPhase.Hold;
	}

	static void SkipToStamp()
	{
		if ( Revealed )
			return;
		_time = _stampAt;
		_phase = BuyRevealPhase.Stamp;
		_lastTick = 3;
		PlayTick( 3 );
		OnStamped();
		_version++;
	}

	static void OnStamped()
	{
		if ( _bannered )
			return;
		_bannered = true;

		var name = SpeciesName;
		var tag = FishRarityInfo.Label( _rarity );
		Shop.SetLastMessage( _rarity == FishRarity.Common ? $"{name}." : $"{tag} {name}!" );
		PixelFx.BurstRarity( PixelFxLayer.Shop, 94f, 118f, _rarity );
		PixelFx.BurstRarity( PixelFxLayer.Tank, TankSim.LastSpawnX, TankSim.LastSpawnY, _rarity );
		if ( _rarity >= FishRarity.Legendary )
		{
			BoomFeel.SoftPunch( _rarity >= FishRarity.Mythical ? 0.85f : 0.65f );
			TankSim.ShowBanner( $"{tag} {name}!! Keep or sell huge." );
			GrandmaReact.OnKeeper( _rarity );
		}
		else if ( _rarity >= FishRarity.Rare )
		{
			BoomFeel.SoftPunch( _rarity >= FishRarity.PaintedRare ? 0.5f : 0.35f );
			TankSim.ShowBanner( $"{tag} {name}! Feed it up." );
			if ( _rarity >= FishRarity.PaintedRare )
				GrandmaReact.OnKeeper( _rarity );
		}
		else if ( _rarity >= FishRarity.Uncommon )
			TankSim.ShowBanner( $"{tag} {name}." );
	}

	static void Dismiss()
	{
		if ( !IsActive )
			return;
		_phase = BuyRevealPhase.Off;
		_species = null;
		_version++;
	}

	static void PlayTick( int step ) =>
		GameAudio.PlayBuyTick( step, _rarity );
}