using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Concilium.Bot
{
    public class PredicateLogic
    {
        private static string allowedChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_";
        public Dictionary<string, List<string>> Predicates;

        public string Parse(string input)
        {
            if (Predicates == null)
                Predicates = new Dictionary<string, List<string>>();

            var lastQuestionMark = input.Last() == '?';
            var firstQuestionMark = input.First() == '?';
            var rand = new Random((int)DateTime.Now.TimeOfDay.TotalMilliseconds);
            if (lastQuestionMark && firstQuestionMark)
            {
                bool randomPredicate = rand.Next(2) == 0;
                if (randomPredicate)
                {
                    var predicates = Predicates.Where(p => p.Value.Count > 0).ToList();
                    var index = rand.Next(predicates.Count);
                    var kvp = predicates.ElementAt(index);
                    return DisplayPredicate(kvp.Key);
                }
                else // randomUser
                {
                    var users = Predicates.SelectMany(kvp => kvp.Value).Distinct().ToList();
                    var index = rand.Next(users.Count);
                    return DisplayUser(users[index]);
                }
            }
            else if (lastQuestionMark)
            {
                var predicate = input.Substring(0, input.Length - 1).Trim();

                if (!predicate.All(c => allowedChars.IndexOf(c) != -1))
                    return "Syntax error: unexpected character in predicate";

                if (!Predicates.ContainsKey(predicate) || Predicates[predicate].Count == 0)
                    return "No one is " + predicate;

                return DisplayPredicate(predicate);
            }
            else if (firstQuestionMark)
            {
                input = input.Substring(1);

                var leftBracket = input.IndexOf('(');
                var rightBracket = input.IndexOf(')');

                if (leftBracket == -1)
                    return "Syntax error: expecting '('";
                if (rightBracket == -1)
                    return "Syntax error: expecting ')'";
                if (rightBracket < leftBracket)
                    return "Syntax error: unexpected ')', was expecting '('";

                var parameter = input.Substring(leftBracket + 1, rightBracket - leftBracket - 1).Trim();

                if (!parameter.Any(c => allowedChars.IndexOf(c) != -1))
                    return "Syntax error: unexpected character in parameter";

                return DisplayUser(parameter);
            }
            else
            {
                var leftBracket = input.IndexOf('(');
                var rightBracket = input.IndexOf(')');
                if (leftBracket == -1)
                    return "Syntax error: expecting '('";
                if (rightBracket == -1)
                    return "Syntax error: expecting ')'";
                if (rightBracket < leftBracket)
                    return "Syntax error: unexpected ')', was expecting '('";

                var predicate = input.Substring(0, leftBracket).Trim();
                var parameter = input.Substring(leftBracket + 1, rightBracket - leftBracket - 1).Trim();

                var inverse = predicate[0] == '!';
                if (inverse)
                    predicate = predicate.Substring(1);

                if (!predicate.All(c => allowedChars.IndexOf(c) != -1))
                    return "Syntax error: unexpected character in predicate";

                if (!parameter.Any(c => allowedChars.IndexOf(c) != -1))
                    return "Syntax error: unexpected character in parameter";

                if (!Predicates.ContainsKey(predicate))
                    Predicates[predicate] = new List<string>();

                if (inverse)
                {
                    if (Predicates[predicate].Contains(parameter))
                    {
                        Predicates[predicate].Remove(parameter);
                        return parameter + " is no longer " + predicate;
                    }
                    else
                    {
                        return parameter + " is not currently " + predicate;
                    }
                }
                else
                {
                    if (Predicates[predicate].Contains(parameter))
                    {
                        return parameter + " is already " + predicate;
                    }
                    else
                    {
                        Predicates[predicate].Add(parameter);
                        return parameter + " is now " + predicate;
                    }
                }
            }
        }

        private string DisplayUser(string user)
        {
            var predicates = Predicates.Where(kvp => kvp.Value.Contains(user)).Select(kvp => kvp.Key);
            if (predicates.Count() == 0)
                return "I don't know nothing about " + user;

            var list = string.Join(", ", predicates);
            var lastComma = list.LastIndexOf(',');
            if (lastComma != -1)
                list = list.Substring(0, lastComma) + " and" + list.Substring(lastComma + 1, list.Length - lastComma - 1);
            return user + " is " + list;
        }

        private string DisplayPredicate(string predicate)
        {
            var list = string.Join(", ", Predicates[predicate]);
            var lastComma = list.LastIndexOf(',');
            if (lastComma != -1)
                list = list.Substring(0, lastComma) + " and" + list.Substring(lastComma + 1, list.Length - lastComma - 1);
            return list + (Predicates[predicate].Count > 1 ? " are " : " is ") + predicate;
        }
    }
}
