namespace SharpChat.Channels;

public class ChannelsContext {
    private readonly List<Channel> Channels = [];

    public int Count => Channels.Count;

    private Channel? DefaultChannelValue;
    public Channel DefaultChannel {
        get {
            if(DefaultChannelValue is not null) {
                if(Channels.Contains(DefaultChannelValue))
                    return DefaultChannelValue;

                DefaultChannelValue = null;
            }

            return GetChannel(c => c.IsPublic && !c.IsTemporary) ?? throw new NoDefaultChannelException();
        }

        set => DefaultChannelValue = value;
    }

    public bool ChannelExists(Func<Channel, bool> predicate) {
        return Channels.Any(predicate);
    }

    public bool ChannelExists(string name) {
        return ChannelExists(c => c.NameEquals(name));
    }

    public Channel? GetChannel(Func<Channel, bool> predicate) {
        return Channels.FirstOrDefault(predicate);
    }

    public Channel? GetChannel(string name) {
        return GetChannel(c => c.NameEquals(name));
    }

    public IEnumerable<Channel> GetChannels(Func<Channel, bool> predicate) {
        return Channels.Where(predicate);
    }

    public IEnumerable<Channel> GetChannels(IEnumerable<string> names) {
        return GetChannels(c => names.Any(n => c.NameEquals(n)));
    }

    public IEnumerable<Channel> GetChannels(int maxRank) {
        return GetChannels(c => c.Rank <= maxRank);
    }

    public Channel CreateChannel(
        string name,
        string password = "",
        bool temporary = false,
        int rank = 0,
        string ownerId = ""
    ) {
        if(!Channel.CheckName(name))
            throw new ChannelNameFormatException(nameof(name));
        if(ChannelExists(name))
            throw new ChannelExistsException(nameof(name));

        Channel channel = new(
            name,
            password ?? string.Empty,
            temporary,
            rank,
            ownerId ?? string.Empty
        );

        Channels.Add(channel);

        return channel;
    }

    public Channel UpdateChannel(
        Channel channel,
        string? name = null,
        string? password = null,
        bool? temporary = null,
        int? rank = null,
        string? ownerId = null
    ) => UpdateChannel(channel.Name, name, password, temporary, rank, ownerId);

    public Channel UpdateChannel(
        string currentName,
        string? name = null,
        string? password = null,
        bool? temporary = null,
        int? rank = null,
        string? ownerId = null
    ) {
        Channel channel = GetChannel(currentName) ?? throw new ChannelNotFoundException(nameof(currentName));

        if(name is not null && currentName != name) {
            if(!Channel.CheckName(name))
                throw new ChannelNameFormatException(nameof(name));
            if(ChannelExists(name))
                throw new ChannelExistsException(nameof(name));
        }

        if(name is not null)
            channel.Name = name;
        if(password is not null)
            channel.Password = password;
        if(temporary.HasValue)
            channel.IsTemporary = temporary.Value;
        if(rank.HasValue)
            channel.Rank = rank.Value;
        if(ownerId is not null)
            channel.OwnerId = ownerId;

        return channel;
    }

    public void RemoveChannel(string name) {
        Channel channel = GetChannel(name) ?? throw new ChannelNotFoundException(nameof(name));

        Channel defaultChannel = DefaultChannel;
        if(channel == defaultChannel || defaultChannel.NameEquals(channel.Name))
            throw new ChannelIsDefaultException(nameof(name));

        Channels.Remove(channel);
    }
}