@page "/meeting-schedule"
@attribute [Authorize]
@using System.Text
@using Core.Calculation
@using Microsoft.EntityFrameworkCore
@inject IConfiguration Configuration
@inject AppDbContext Context
@inject ClipboardService ClipboardService

<PageTitle>@Configuration["ChapterSettings:Shortname"] TSA Schedule @Configuration["ChapterSettings:CompetitionYear"]</PageTitle>

<MudText Typo="Typo.h3">@Configuration["ChapterSettings:Shortname"] TSA Schedule @Configuration["ChapterSettings:CompetitionYear"]</MudText>

<MudPaper Class="pa-4 mt-5">
    <MudGrid>
        <MudItem xs="7" sm="8" lg="9">
            <MudText Typo="Typo.h4">Time Slots</MudText>
            <MudPaper Class="pa-2 ma-2" Elevation="3">
                <MudGrid>
                    <MudItem xs="6" sm="3" lg="2">
                        <MudNumericField @bind-Value="_parameters.TimeSlots"
                                         Label="Time Slots" Min="1" Max="4">
                        </MudNumericField>
                    </MudItem>
                    <MudFlexBreak/>
                    <MudItem xs="12" sm="6" lg="4">
                        <MudTooltip Text="Schedule teams with Level of Effort >= 3" Inline="false">
                        <MudButton Variant="Variant.Outlined" OnClick="() => AddHighLevelOfEffort()" FullWidth="true">Add High Effort</MudButton>
                        </MudTooltip>
                    </MudItem>
                    <MudItem xs="12" sm="6" lg="4">
                        <MudButton Variant="Variant.Outlined" OnClick="() => AddRegionals()" FullWidth="true">Add Regionals</MudButton>
                    </MudItem>
                    <MudItem xs="12" sm="6" lg="4">
                        <MudButton Variant="Variant.Outlined" OnClick="() => RemoveIndividual()" FullWidth="true">Remove Individual</MudButton>
                    </MudItem>
                    <MudItem xs="12" sm="6" lg="4">
                        <MudButton Variant="Variant.Outlined" OnClick="() => RemoveLowLevelOfEffort()" FullWidth="true">Remove Low Effort</MudButton>
                    </MudItem>
                    <MudItem xs="12" sm="6" lg="4">
                        <MudButton Variant="Variant.Outlined" OnClick="() => Invert()" FullWidth="true">Invert</MudButton>
                    </MudItem>
                    <MudItem xs="12" sm="6" lg="4">
                        <MudButton Variant="Variant.Outlined" Color="Color.Warning" OnClick="() => Reset()" FullWidth="true">Reset</MudButton>
                    </MudItem>
                    <MudItem xs="12">
                        <MudButton Variant="Variant.Filled" Class="ma-3" OnClick="Solve" Color="Color.Primary" Disabled="@_isSolving">Solve</MudButton>
                        <MudTooltip Text="Copy to Clipboard">
                            <MudIconButton OnClick="CopyToClipboard" Icon="@Icons.Material.Filled.ContentCopy"></MudIconButton>
                        </MudTooltip>
                    </MudItem>

                </MudGrid>
            </MudPaper>

            <MudTable T="TeamScheduleTimeSlot" ServerData="SolveSchedule" @ref="_solutionData">
                <HeaderContent>
                </HeaderContent>
                <RowTemplate>
                    <MudTd>
                        <MudGrid Class="">
                            <MudItem xs="12" lg="6">
                                <MudStack>
                                    <MudText Typo="Typo.h6">@context.Name</MudText>
                                    @foreach (var team in context.Teams.OrderBy(e => e.ToString()))
                                    {
                                        var removed = !_scheduledTeams.Contains(team);

                                        <MudLink Typo="Typo.body1"
                                                 Class="d-flex align-center"
                                                 Color="Color.Default"
                                                 OnClick="@(() => ToggleRequiredTeam(team))">
                                            <MudIcon Icon="@Icons.Material.Filled.Clear"
                                                     Size="Size.Small"
                                                     Class="@(removed ? "" : "d-none")">
                                            </MudIcon>
                                            @team -
                                            @{var first = true;}
                                            @foreach (var student in team.Students)
                                            {
                                                var overlap = context.StudentHasOverlaps(student);
                                                var isAbsent = _absentStudents.Contains(student);
                                                var color = overlap ? Color.Warning : Color.Default;

                                                @if(!first) {<MudText>, </MudText>}
                                                {first = false;}
                                                <MudText
                                                    Typo="Typo.body2"
                                                    Color="@color">
                                                    &nbsp;@student.FirstName@(overlap ? "*" : "")@(isAbsent ? " (absent)" : "")
                                                </MudText>
                                            }
                                        </MudLink>

                                    }
                                </MudStack>
                            </MudItem>
                            <MudItem xs="12" lg="6">
                            @if (context.UnscheduledStudents.Any())
                            {
                                <MudText Typo="Typo.body1" HtmlTag="strong">Unscheduled</MudText>
                                <MudStack>
                                    @foreach (var student in context.UnscheduledStudents)
                                    {
                                        var isAbsent = _absentStudents.Contains(student);
                                        <MudItem>
                                        <MudText Typo="Typo.body1" HtmlTag="i">@student.FirstName@(isAbsent ? " (absent)" : "")&nbsp;</MudText>
                                        @{var first = true;}
                                        @foreach (var unassignedTeam in _solution.StudentUnassignedTeams(student))
                                        {
                                            var color = _possibleAdditions.Contains(unassignedTeam, new TeamIdComparer()) ? Color.Success : Color.Default;
                                            var added = _scheduledTeams.Contains(unassignedTeam);
                                            @if(!first) {<span>, </span>}
                                            {first = false;}
                                            <MudLink Typo="Typo.body2"
                                                     Color="@color"
                                                     OnClick="@(() => ToggleRequiredTeam(unassignedTeam))">
                                                <MudIcon Icon="@Icons.Material.Filled.Check"
                                                         Size="Size.Small"
                                                         Class="@(added ? "" : "d-none")">
                                                </MudIcon>
                                                @unassignedTeam 
                                            </MudLink>
                                        }
                                        </MudItem>
                                    }
                                 </MudStack>
                            }
                            </MudItem>
                        </MudGrid>
                    </MudTd>
                </RowTemplate>
            </MudTable>
        </MudItem>
        <MudItem xs="5" sm="4" lg="3">
            <MudStack>
                <StudentTextBoxSelector Students="@_students"
                                        @bind-SelectedStudents="_absentStudents"
                                        Title="Absent Students"
                                        Label="Search for absent students"
                                        ShowFullName="true"/>
                <MudDivider Class="my-4"/>
                <MudText Typo="Typo.h4">Scheduled Teams</MudText>
                <MudToggleGroup T="Team"
                                SelectionMode="SelectionMode.MultiSelection"
                                @bind-Values="_scheduledTeams"
                                Vertical="true"
                                CheckMark>
                    @foreach (var team in _teams.OrderBy(e => e.Event.Name))
                    {
                        <MudToggleItem Value="@team" Style="font-size: .75rem;">
                            <MudTooltip Text="@team.StudentsFirstNames">
                                <div class="d-flex align-center justify-space-between flex-wrap">
                                    <MudText Class="ellipsis">@team.ToString()</MudText>
                                    <EventAttributes EventDefinition="@team.Event"></EventAttributes>
                                </div>
                            </MudTooltip>
                        </MudToggleItem>
                    }
                </MudToggleGroup>
            </MudStack>
        </MudItem>

    </MudGrid>
</MudPaper>

@code {
    private Team[]? _teams;
    private Student[]? _students;
    MudTable<TeamScheduleTimeSlot> _solutionData;
    private TeamSchedulerSolution _solution;
    private TeamSchedulerOptions _parameters;
    bool _isSolving;
    private IEnumerable<Team> _scheduledTeams = [];
    private IEnumerable<Student> _absentStudents = [];
    private IEnumerable<Team> _possibleAdditions = [];

    private async Task AddRegionals()
    {
        _scheduledTeams
            = _teams.Where(e => e.Event.RegionalEvent).Concat(_scheduledTeams).Distinct();
    }

    private async Task AddHighLevelOfEffort()
    {
        _scheduledTeams
            = _teams.Where(e => e.Event.LevelOfEffort >= 3).Concat(_scheduledTeams).Distinct();
    }

    private async Task RemoveIndividual()
    {
        _scheduledTeams
            = _scheduledTeams.Where(t => t.Event.EventFormat != EventFormat.Individual);
    }

    private async Task RemoveLowLevelOfEffort()
    {
        _scheduledTeams
            = _scheduledTeams.Where(t => t.Event.LevelOfEffort > 1);
    }

    private async Task Invert()
    {
        var rt = _scheduledTeams.ToArray();
        _scheduledTeams
            = _teams.Where(t => !rt.Contains(t));
    }

    private async Task Reset()
    {
        _scheduledTeams = [];
    }

    private void ToggleRequiredTeam(Team unassignedTeam)
    {
        if (_scheduledTeams.Contains(unassignedTeam))
            _scheduledTeams = _scheduledTeams.Where(t => t != unassignedTeam);
        else
        {
            _scheduledTeams = _scheduledTeams.Concat(new[] { unassignedTeam });
        }
    }

    protected override async Task OnInitializedAsync()
    {
        _parameters =
            new TeamSchedulerOptions(
                2,
                mustIncludeEvents:
                [
                    // "Medical Technology", "Electrical Applications"  , "RegionalTeam",
                    // ,"Dragster", "Flight"
                ],
                extended:
                [
                    // "Invention", "Construction Challenge",  "Mechanical", "Mass", "Micro"
                    //"STEM"
                    //"Community", "Vlogging"// "Microcontroller"
                ],
                omittedEvents:
                [
                    // "Vlogging", "Junior", "Community Service Video", "Digital Photography",
                    // "STEM"

                    //"Leadership",// "Electrical", //"Construction"
                    // "Forensic",
                    //"CAD"
                    //"I&I Team 1", "I&I Team 2"//, "Website Design",
                ],
                absentStudents:
                [
                ]
            );

        _teams
            = await Context.Teams
                .Include(e => e.Event)
                .Include(e => e.Students)
                .OrderBy(e => e.Event.Name)
                .ThenBy(e => e.Identifier)
                .ToArrayAsync();

        _students =
            await Context.Students
                .Include(e => e.Teams)
                .ThenInclude(e => e.Captain)
                .Include(e => e.EventRankings)
                .ThenInclude(e => e.EventDefinition)
                .OrderBy(e => e.FirstName).ToArrayAsync();
    }

    private async Task<TableData<TeamScheduleTimeSlot>> SolveSchedule(TableState arg1, CancellationToken arg2)
    {
        _isSolving = true;

        // Filter out absent students
        var availableStudents = _students.Where(s => !_absentStudents.Contains(s)).ToArray();

        // Update parameters with absent student names
        _parameters.AbsentStudents = _absentStudents.Select(s => s.FirstNameLastName).ToArray();

        var teamScheduler = new TeamScheduler(_scheduledTeams, _parameters.TimeSlots, availableStudents);
        _solution = teamScheduler.Solve();

        var loe = new UnassignedStudentScheduler(_teams, _solution.TimeSlots).ScheduleStrategy(UnassignedScheduleStrategy.LevelOfEffort);
        var biggest = new UnassignedStudentScheduler(_teams, _solution.TimeSlots).ScheduleStrategy(UnassignedScheduleStrategy.BiggestGroup);
        var individual = new UnassignedStudentScheduler(_teams, _solution.TimeSlots).ScheduleStrategy(UnassignedScheduleStrategy.IndividualEvents);
        var anyNotMeetingAlready = new UnassignedStudentScheduler(_teams, _solution.TimeSlots).ScheduleStrategy(UnassignedScheduleStrategy.AnyNotMeetingAlready);

        _possibleAdditions = loe;
        if (!_possibleAdditions.Any())
            _possibleAdditions = biggest;
        if (!_possibleAdditions.Any())
            _possibleAdditions = anyNotMeetingAlready;
        if (!_possibleAdditions.Any())
            _possibleAdditions = individual;

        await InvokeAsync(StateHasChanged); // let the UI know that the solution has been found

        _isSolving = false;
        return new TableData<TeamScheduleTimeSlot> { Items = _solution.TimeSlots };
    }

    private void Solve()
    {
        _solutionData.ReloadServerData();
    }

    async Task CopyToClipboard()
    {
        var sb = new StringBuilder();
        foreach (var timeslot in _solution.TimeSlots)
        {
            //var overlaps
            //    = TeamSchedulerSolution.GetStudentTeamOverlaps(timeslot).Select(e => e.Item1).ToArray();
            foreach (var scheduledTeam in timeslot.Teams.OrderBy(e => e.ToString()))
            {
                var t = scheduledTeam.ToString();
                var s =
                    string.Join(", ",
                        scheduledTeam.Students
                            .OrderBy(e => e == scheduledTeam.Captain)
                            .ThenBy(e => e.FirstName)
                            .Select(e => e.FirstName + (timeslot.StudentHasOverlaps(e) ? "*" : "") + (_absentStudents.Contains(e) ? " (absent)" : "")));

                if (scheduledTeam.Event.EventFormat is EventFormat.Individual)
                    sb.Append(t);
                else
                    sb.Append($"{t} - {s}");
                sb.Append(Environment.NewLine);
            }

            if (timeslot.UnscheduledStudents.Any())
            {
                sb.Append("--Unscheduled");
                sb.Append(Environment.NewLine);
                foreach (var student in timeslot.UnscheduledStudents)
                {
                    var s = student.FirstName + (_absentStudents.Contains(student) ? " (absent)" : "");
                    var unassignedTeams = _solution.StudentUnassignedTeams(student);
                    var t = string.Join(", ", unassignedTeams.Select(e => e.ToString()));

                    sb.Append($"{s} - {t}");
                    sb.Append(Environment.NewLine);
                }
            }

            sb.Append(Environment.NewLine);
        }

        try
        {
            await ClipboardService.WriteTextAsync(sb.ToString());
        }
        catch
        {
            Console.WriteLine("Cannot write text to clipboard");
        }
    }

}