using Sandbox;
using System;
using System.Collections.Generic;
using System.Linq;

namespace GravCoin;

public enum ChatEntryType { Chat, Trade, Info }

public struct ChatEntry
{
	public string Name { get; set; }
	public string Message { get; set; }
	public string Color { get; set; }
	public string Icon { get; set; }
	public float Time { get; set; }
	public ulong SteamId { get; set; }
	public ChatEntryType EntryType { get; set; }
	public float RawValue { get; set; }

	public ChatEntry( string name, string message, string color, string icon, float time, ulong steamId, ChatEntryType type = ChatEntryType.Chat, float rawValue = 0f )
	{
		Name = name;
		Message = message;
		Color = color;
		Icon = icon;
		Time = time;
		SteamId = steamId;
		EntryType = type;
		RawValue = rawValue;
	}
}

public sealed class ChatSystem : Component
{
	private static ChatSystem _instance;
	public static ChatSystem Instance => _instance.IsValid() ? _instance : null;

	[Property] public int MaxMessages { get; set; } = 100;
	private const int HistorySyncLimit = 50;

	public List<ChatEntry> Messages { get; private set; } = new();
	public event Action OnMessageReceived;
	public event Action OnHistoryReceived;

	protected override void OnAwake()
	{
		_instance = this;
	}

	protected override void OnStart()
	{
		if ( Connection.Local != null && !Networking.IsHost )
		{
			RequestHistory();
		}
	}

	/// <summary>Send a regular chat message from the local player.</summary>
	public void Send( string text )
	{
		if ( string.IsNullOrWhiteSpace( text ) ) return;

		var player = Scene.GetAllComponents<PlayerTrader>().FirstOrDefault( x => x.Network.Owner == Connection.Local );
		string name = Connection.Local?.DisplayName ?? "Unknown";
		string color = "#ffffff";
		string icon = "mdi:account-outline";
		ulong steamId = Connection.Local != null ? (ulong)Connection.Local.SteamId : 0UL;

		if ( player.IsValid() )
		{
			name = player.PlayerName;
			color = player.RankColor;
			icon = player.RankIcon;
		}

		HostReceiveMessage( text, name, color, icon, steamId );
	}

	/// <summary>Called by the local player after a manual buy or sell trade.</summary>
	public void SendTradeInfo( bool isBuy, float coins, float price )
	{
		var player = Scene.GetAllComponents<PlayerTrader>().FirstOrDefault( x => x.Network.Owner == Connection.Local );
		string name = player?.PlayerName ?? Connection.Local?.DisplayName ?? "Unknown";
		ulong steamId = Connection.Local != null ? (ulong)Connection.Local.SteamId : 0UL;
		HostReceiveTradeInfo( isBuy, coins, price, name, steamId );
	}

	/// <summary>Called on the host to announce a player joining.</summary>
	public void NotifyPlayerJoined( string displayName )
	{
		if ( !Networking.IsHost ) return;
		BroadcastEntry( "joined", displayName, "#4ade80", "mdi:account-plus", 0UL, ChatEntryType.Info );
	}

	/// <summary>Called on the host to announce a player leaving.</summary>
	public void NotifyPlayerLeft( string displayName )
	{
		if ( !Networking.IsHost ) return;
		BroadcastEntry( "left", displayName, "#fb923c", "mdi:account-minus", 0UL, ChatEntryType.Info );
	}

	[Rpc.Host]
	private void HostReceiveMessage( string message, string senderName, string senderColor, string senderIcon, ulong steamId )
	{
		BroadcastEntry( message, senderName, senderColor, senderIcon, steamId, ChatEntryType.Chat );
	}

	[Rpc.Host]
	private void HostReceiveTradeInfo( bool isBuy, float coins, float price, string name, ulong steamId )
	{
		string icon = isBuy ? "mdi:arrow-up-circle" : "mdi:arrow-down-circle";
		string color = isBuy ? "#4ade80" : "#f87171";
		string msg = isBuy ? "BOUGHT" : "SOLD";
		BroadcastEntry( msg, name, color, icon, steamId, ChatEntryType.Trade, coins );
	}

	[Rpc.Broadcast]
	private void BroadcastEntry( string message, string senderName, string senderColor, string senderIcon, ulong steamId, ChatEntryType type, float rawValue = 0f )
	{
		if ( string.IsNullOrWhiteSpace( message ) ) return;

		var entry = new ChatEntry( senderName, message, senderColor, senderIcon, RealTime.Now, steamId, type, rawValue );
		Messages.Add( entry );

		if ( Messages.Count > MaxMessages )
			Messages.RemoveAt( 0 );

		OnMessageReceived?.Invoke();
	}

	[Rpc.Host]
	private void RequestHistory()
	{
		// Only sync player chat messages to new joiners (not trade/info noise)
		var chatOnly = Messages.Where( m => m.EntryType == ChatEntryType.Chat ).ToList();
		var slice = chatOnly.Count > HistorySyncLimit
			? chatOnly.Skip( chatOnly.Count - HistorySyncLimit ).ToList()
			: chatOnly;
		ReceiveHistory( slice, Rpc.Caller.SteamId );
	}

	[Rpc.Broadcast]
	private void ReceiveHistory( List<ChatEntry> history, ulong targetId )
	{
		if ( Connection.Local == null || Connection.Local.SteamId != targetId ) return;

		Messages = history ?? new();
		OnHistoryReceived?.Invoke();
	}
}
