using System.Diagnostics;
using System.Text.Json;
using System.Text.RegularExpressions;
using Core.Calculation;
using Core.Entities;
using Core.Parsers;
using Core.Utility;
using Microsoft.AspNetCore.Mvc;
using Web.Models;

namespace Web.Controllers
{
	public partial class HomeController : Controller
	{
		private const string ContentDirectory = @"C:\Users\james\source\TSA Chapter Organizer\Tests\Parsers\TestInput\";

		private EventDefinition[] GetEvents()
		{
			var fileInfo = FileUtility.GetContentFile(ContentDirectory, "2024-25 RMS TSA student & eventDefinition - EventDefinition Definitions.csv");
			var eventRankingsParser = new EventDefinitionParser(fileInfo);
			return eventRankingsParser.Parse();
		}

		private Student[] GetStudents(IList<EventDefinition> events)
		{
			var fileInfo = FileUtility.GetContentFile(ContentDirectory, "2024-25 RMS TSA student & eventDefinition - Nationals Student EventDefinition Rankings.csv");
			var eventRankingsParser = new StudentParser(fileInfo);
			return eventRankingsParser.Parse(events);
		}

        public static StudentEventRanking[] GetStudentEventRankings(Student[] students, EventDefinition[] events)
        {
            var fileInfo = FileUtility.GetContentFile(ContentDirectory, "2024-25 RMS TSA -  Student Event Rankings.csv");

            var rankingParser = new StudentEventRankingParser(fileInfo);
            return rankingParser.Parse(students, events);
        }

        public Team[] GetTeams(IList<EventDefinition> competitiveEvents, IList<Student> students)
		{
			var studentEventRankingsCsv = "2024-25 RMS TSA student & eventDefinition - Nationals Teams.csv";

			var fileInfo = FileUtility.GetContentFile(ContentDirectory, studentEventRankingsCsv);
			var eventRankingsParser = new TeamParser(fileInfo);
			var teams = eventRankingsParser.Parse(competitiveEvents, students);

			foreach (var student in students)
			{
				student.Teams = teams.Where(t => t.Students.Contains(student)).ToList();
			}
			return teams;
		}

		public AssignmentAssumption[] GetAssignmentAssumptions(IList<EventDefinition> competitiveEvents, IList<Student> students)
		{
			var assumptionsCsv = "2024-25 RMS TSA student & eventDefinition - assumptions.csv";

			var fileInfo = FileUtility.GetContentFile(ContentDirectory, assumptionsCsv);
			var assumptionParser = new AssignmentAssumptionParser(fileInfo);
			var assumptions = assumptionParser.Parse(competitiveEvents, students);

			return assumptions;
		}

		public IDictionary<EventDefinition, List<EventOccurrence>> GetStateEventOccurrences(IList<EventDefinition> competitiveEvents)
		{
			var eventTimesFilename = "2025 TN TSA State Competition EventDefinition Times.txt";
			var fileInfo = FileUtility.GetContentFile(ContentDirectory, eventTimesFilename);
			var parser = new EventOccurrenceParser(fileInfo, competitiveEvents);
			return parser.Parse();
		}

		public IDictionary<EventDefinition, List<EventOccurrence>> GetNationalEventOccurrences(IList<EventDefinition> competitiveEvents)
		{
			var eventTimesFilename = "2025 TSA Nationals Competition EventDefinition Times.txt";
			var fileInfo = FileUtility.GetContentFile(ContentDirectory, eventTimesFilename);
			var parser = new EventOccurrenceParser(fileInfo, competitiveEvents);
			var nationalEventOccurrences = parser.Parse();

			var locationPrefixes = new[] { "Cheekwood", "Ryman", "Lincoln", "Canal", "Online", "Magnolia", "Tennessee", "Bayou", "Hermitage", "Belmont", "Davidson", "Washington", "Belle Meade"};
			var oredLocations = string.Join("|", locationPrefixes);
			var regex = new Regex($"^(.*)((?:{oredLocations}).*)");

			foreach (var occurrence in nationalEventOccurrences)
			{
				foreach (var eventOccurrence in occurrence.Value)
				{
					if (!string.IsNullOrEmpty(eventOccurrence.Location))
						continue;
					var match = regex.Match(eventOccurrence.Name);
					if (!match.Success)
						continue;
					eventOccurrence.Name = match.Groups[1].Value.Trim();
					eventOccurrence.Location = match.Groups[2].Value.Trim();
				}
			}
			return nationalEventOccurrences;

		}

		private readonly ILogger<HomeController> _logger;

		public HomeController(ILogger<HomeController> logger)
		{
			_logger = logger;
		}

		public IActionResult Index()
		{
			return View();
		}

        public IActionResult Events()
        {
            var competitiveEvents = GetEvents().Where(e => e.Name != "Chapter Team").ToArray();
            return View(competitiveEvents);
        }
 
        public IActionResult StudentEventHandout()
        {
            var competitiveEvents = GetEvents();
            var students = GetStudents(competitiveEvents);
            var teams = GetTeams(competitiveEvents, students);

            return View(Tuple.Create(students));
        }

        public IActionResult StudentEvents()
		{
			var events = GetEvents();
			var students = GetStudents(events);
            var rankings = GetStudentEventRankings(students, events);


            var eventStudentPicksArray = StudentEventRanking.GetEventStudentRankings(rankings);
			var assignmentParameters = new AssignmentParameters
			{
				EffortUpperBound = 9,
				RequireOnSite = true,
				RequireRegional = true,
				TeamSizeLimit = 4
			};

			var eventAssignment = new EventAssigner(events, students, assignmentParameters);

			var assignmentAssumptions = GetAssignmentAssumptions(events, students);

			foreach (var assumption in assignmentAssumptions)
			{
				switch (assumption.Assumption)
				{
					case Assumption.Exclude:
						eventAssignment.ExcludeFromEvent(assumption.EventAssignment);
						break;
					case Assumption.Include:
						eventAssignment.AssignToEvent(assumption.EventAssignment);
						break;

				}
			}

			eventAssignment.RemoveEvent(new List<EventDefinition>
			{
				events.First(e => e.Name == "Chapter Team")
			});

			eventAssignment.IncludedEvents(new List<EventDefinition>
			{
				//competitiveEvents.First(e => e.Name == "System Control Technology")
			});


			var eventAssignmentsList = eventAssignment.Solve();
			return View(Tuple.Create(events, students, eventStudentPicksArray, eventAssignmentsList, assignmentParameters));
		}

		public IActionResult Teams()
		{
			var competitiveEvents = GetEvents();
			var students = GetStudents(competitiveEvents);
			var teams = GetTeams(competitiveEvents, students);

			//teams = teams.Where(t => t.EventDefinition.RegionalEvent).ToArray();

			return View(Tuple.Create(teams));
		}

        public IActionResult Regionals()
		{
			var competitiveEvents = GetEvents();
			var students = GetStudents(competitiveEvents);
			var teams = GetTeams(competitiveEvents, students);

			teams = teams.Where(t => t.EventDefinition.RegionalEvent).ToArray();
			var enumerable = students.Where(s => !teams.SelectMany(ts => ts.Students).Contains(s)).ToArray();

			return View(Tuple.Create(teams, enumerable));
		}

		public IActionResult State()
		{
			var competitiveEvents = GetEvents();
			var students = GetStudents(competitiveEvents);
			var teams = GetTeams(competitiveEvents, students);
			var eventOccurrences = GetStateEventOccurrences(competitiveEvents);

			// Filter out pre-conference
			eventOccurrences =
				(from kv in eventOccurrences
					let newV = kv.Value.Where(eo => !eo.Name.Contains("Pre-Conference"))
					select Tuple.Create(kv.Key, newV))
				.ToDictionary(s => s.Item1, s => s.Item2.ToList());

			return View(Tuple.Create(teams, students, eventOccurrences));
		}
		public IActionResult Nationals()
		{
			var competitiveEvents = GetEvents();
			var students = GetStudents(competitiveEvents);
			var teams = GetTeams(competitiveEvents, students);
			var eventOccurrences = GetNationalEventOccurrences(competitiveEvents);

			// Filter out pre-conference
			eventOccurrences =
				(from kv in eventOccurrences
					let newV = kv.Value.Where(eo => !eo.Name.Contains("Pre-Conference"))
					select Tuple.Create(kv.Key, newV))
				.ToDictionary(s => s.Item1, s => s.Item2.ToList());

			return View(Tuple.Create(teams, students, eventOccurrences));
		}

		public IActionResult TeamGrid()
		{
			var competitiveEvents = GetEvents();
			var students = GetStudents(competitiveEvents);
			var teams = GetTeams(competitiveEvents, students);

			return View(Tuple.Create(teams, students));
		}

		public IActionResult Students()
		{
			var competitiveEvents = GetEvents();
			var students = GetStudents(competitiveEvents);
			GetTeams(competitiveEvents, students);

			return View(Tuple.Create(students));
		}

		public static Func<T, bool> And<T>(params Func<T, bool>[] predicates)
		{
			return t => predicates.All(predicate => predicate(t));
		}

		public IActionResult Schedule()
		{
			var scheduleOptions =
				new ScheduleOptions(
					timeSlots: 3, 
					mustIncludeEvents:new []
					{
						"Medical Technology", "Electrical Applications"  //, "RegionalTeam",
						,"Dragster", "Flight"
					}, 
					extended: new string[]
					{
						"Invention", "Construction Challenge",  "Mechanical", "Mass", "Micro"
						//"STEM"
						//"Community", "Vlogging"// "Microcontroller"
					},
					omittedEvents: new string[]
					{
						"Vlogging", "Junior", "Community Service Video", "Digital Photography",
						"STEM"
						
						//"Leadership",// "Electrical", //"Construction"
						// "Forensic",
						 //"CAD"
						//"I&I Team 1", "I&I Team 2"//, "Website Design",
					},
					absentStudents: new string[]
					{
						//"Eliam"
					},
					reverse: true,
					slotPush:0);

			var events = GetEvents();
			var students = GetStudents(events);
			
			var allTeams = GetTeams(events, students);

			var omittedEvents = allTeams.Where(t => scheduleOptions.OmittedEvents?.Any(s => t.Name.Contains(s)) == true).ToArray();
			allTeams = allTeams.Where(t => !omittedEvents.Contains(t)).ToArray();

			bool RegionalPredicate(Team t) => t.EventDefinition.RegionalEvent;
			bool TeamPredicate(Team t) => t.EventDefinition.EventFormat is EventFormat.Team;
			bool RegionalTeamPredicate(Team t) => RegionalPredicate(t) && TeamPredicate(t);
			bool HighEffort(Team t) => t.EventDefinition.LevelOfEffort == 3;
			bool LowEffort(Team t) => t.EventDefinition.LevelOfEffort == 1;

			bool RegionalTeamSomeEffortPredicate(Team t) => RegionalTeamPredicate(t) && !LowEffort(t);

			bool IndividualPredicate(Team t) => t.EventDefinition.EventFormat is EventFormat.Individual;
			bool negativePredicate(Team t) => false;

			var mustIncludeTeams =
				from e in events
					from t in allTeams
				 where t.EventDefinition == e &&
				       (scheduleOptions.MustIncludeEvents?.Any(s => s == "RegionalTeam") == true && RegionalTeamSomeEffortPredicate(t) 
						|| scheduleOptions.MustIncludeEvents?.Any( t.EventDefinition.Name.Contains) == true
				        || HighEffort(t))
					select t;
			
			Debug.WriteLine("Must Include: " + string.Join(", ", mustIncludeTeams.Select(t => t.ToStringWithIndividualAndRegional())));
			Debug.WriteLine("Omitted: " + string.Join(", ", omittedEvents.Select(t => t.ToStringWithIndividualAndRegional())));

			var teamScheduler = new TeamScheduler(mustIncludeTeams.ToArray(), scheduleOptions.TimeSlots);
			var schedule = teamScheduler.Solve();

			//schedule = schedule.OrderByDescending(ts => new Random(schedule.GetHashCode() + 1).Next()).ToArray();
			
			if (scheduleOptions.SlotPush != 0)
			{
				schedule = schedule.Skip(scheduleOptions.SlotPush).Concat(schedule.Take(scheduleOptions.SlotPush))
					.ToArray();
			}
			if (scheduleOptions.Reverse)
				schedule = schedule.Reverse().ToArray();

			////// extend schedules
			var extendedTeams =
				from t in allTeams
				where 
				        scheduleOptions.MustIncludeEvents?.Any(s => scheduleOptions.ExtendedTeams?.Any(et => t.Name.Contains(et)) == true) == true
				select t;

			foreach (var extendedTeam in extendedTeams)
			{
				schedule = new UnassignedStudentScheduler(allTeams, schedule).AddAdditionalTimeSlot(extendedTeam);
			}

			schedule = new UnassignedStudentScheduler(allTeams, schedule).ScheduleStrategy(UnassignedScheduleStrategy.LevelOfEffort);
			schedule = new UnassignedStudentScheduler(allTeams, schedule).ScheduleStrategy(UnassignedScheduleStrategy.BiggestGroup);
			schedule = new UnassignedStudentScheduler(allTeams, schedule).ScheduleStrategy(UnassignedScheduleStrategy.IndividualEvents);
			//schedule = new UnassignedStudentScheduler(allTeams, schedule).ScheduleStrategy(UnassignedScheduleStrategy.AnyNotMeetingAlready);

			var unassignedStudents = UnassignedStudentScheduler.UnassignedStudents(students, schedule).ToArray();

			return View(Tuple.Create(schedule, unassignedStudents, scheduleOptions));
		}

		public IActionResult Privacy()
		{
			return View();
		}

		[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
		public IActionResult Error()
		{
			return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
		}
	}
}