using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Components;
using Microsoft.JSInterop;
using ASTRAIN.Shared.Dtos;
using ASTRAIN.Shared.Requests;

namespace ASTRAIN.Client.Pages;

public partial class Routines
{
    /// <summary>
    /// Optional user id from route.
    /// </summary>
    [Parameter]
    public string? UserId { get; set; }

    private List<ExerciseDto> ExerciseList { get; set; } = new();
    private List<RoutineDto> RoutineList { get; set; } = new();
    private bool IsLoading { get; set; } = true;
    private bool ShowCreateRoutine { get; set; }

    private string NewRoutineName { get; set; } = string.Empty;
    private HashSet<int> SelectedExerciseIds { get; set; } = new();

    private RoutineDto? EditingRoutine { get; set; }
    private string EditingName { get; set; } = string.Empty;
    private HashSet<int> EditingExerciseIds { get; set; } = new();

    private RoutineDto? ActiveRun { get; set; }
    private List<RoutineRunEntryDto> RunEntries { get; set; } = new();

    /// <inheritdoc/>
    protected override async Task OnInitializedAsync()
    {
        var ensured = await Api.EnsureUserAsync(UserId);
        if (string.IsNullOrWhiteSpace(ensured))
        {
            return;
        }

        UserContext.SetUserId(ensured);
        if (UserId != ensured)
        {
            Navigation.NavigateTo($"/{ensured}/routines", true);
            return;
        }

        await LoadDataAsync();
    }

    private async Task LoadDataAsync()
    {
        IsLoading = true;
        ExerciseList = await Api.GetExercisesAsync(UserContext.UserId);
        RoutineList = await Api.GetRoutinesAsync(UserContext.UserId);
        IsLoading = false;
    }

    /// <summary>
    /// Loads exercises and routines from the API and updates the UI state.
    /// </summary>

    private void ToggleExercise(int exerciseId)
    {
        if (!SelectedExerciseIds.Add(exerciseId))
        {
            SelectedExerciseIds.Remove(exerciseId);
        }
    }

    /// <summary>
    /// Toggles whether an exercise is selected when creating a routine.
    /// </summary>

    private async Task CreateRoutineAsync()
    {
        if (string.IsNullOrWhiteSpace(NewRoutineName))
        {
            return;
        }

        var request = new RoutineUpsertRequest(NewRoutineName, SelectedExerciseIds.ToList());
        var created = await Api.CreateRoutineAsync(UserContext.UserId, request);
        if (created is not null)
        {
            RoutineList.Add(created);
            RoutineList = RoutineList.OrderBy(r => r.Name).ToList();
            NewRoutineName = string.Empty;
            SelectedExerciseIds.Clear();
        }

        /// <summary>
        /// Creates a new routine with the selected exercises.
        /// </summary>
    }

    private void ToggleCreate()
    {
        ShowCreateRoutine = !ShowCreateRoutine;
    }

    /// <summary>
    /// Toggle visibility of the create-routine UI.
    /// </summary>

    private void GoToExercises()
    {
        Navigation.NavigateTo($"/{UserContext.UserId}/exercises");
    }

    /// <summary>
    /// Navigate to the exercises page for the current user.
    /// </summary>

    private void StartEdit(RoutineDto routine)
    {
        EditingRoutine = routine;
        EditingName = routine.Name;
        EditingExerciseIds = routine.Exercises.Select(e => e.ExerciseId).ToHashSet();
    }

    /// <summary>
    /// Begin editing the supplied routine.
    /// </summary>

    private void CancelEdit()
    {
        EditingRoutine = null;
        EditingName = string.Empty;
        EditingExerciseIds.Clear();
    }

    /// <summary>
    /// Cancel the current routine edit and reset state.
    /// </summary>

    private void ToggleEditExercise(int exerciseId)
    {
        if (!EditingExerciseIds.Add(exerciseId))
        {
            EditingExerciseIds.Remove(exerciseId);
        }
    }

    /// <summary>
    /// Toggle whether an exercise is selected in the routine edit UI.
    /// </summary>

    private async Task SaveEditAsync()
    {
        if (EditingRoutine is null)
        {
            return;
        }

        var request = new RoutineUpsertRequest(EditingName, EditingExerciseIds.ToList());
        var updated = await Api.UpdateRoutineAsync(UserContext.UserId, EditingRoutine.Id, request);
        if (updated is not null)
        {
            var index = RoutineList.FindIndex(r => r.Id == EditingRoutine.Id);
            if (index >= 0)
            {
                RoutineList[index] = updated;
                RoutineList = RoutineList.OrderBy(r => r.Name).ToList();
            }
        }

        CancelEdit();
    }

    /// <summary>
    /// Save changes made to the currently edited routine.
    /// </summary>

    private async Task DeleteRoutineAsync(int routineId)
    {
        var confirmed = await JS.InvokeAsync<bool>("confirm", "Are you sure you want to delete this routine?");
        if (!confirmed) return;

        await Api.DeleteRoutineAsync(UserContext.UserId, routineId);
        RoutineList.RemoveAll(r => r.Id == routineId);
    }

    /// <summary>
    /// Delete the routine with the given id after confirmation.
    /// </summary>

    private async Task StartRun(RoutineDto routine)
    {
        ActiveRun = routine;
        var lastRun = await Api.GetLastRunAsync(UserContext.UserId, routine.Id);
        RunEntries = routine.Exercises
            .OrderBy(e => e.Order)
            .Select(e =>
            {
                var last = lastRun.Entries.FirstOrDefault(x => x.ExerciseId == e.ExerciseId);
                return new RoutineRunEntryDto(e.ExerciseId, last?.Weight ?? 0, false);
            }).ToList();
    }

    /// <summary>
    /// Start a routine run and prepare run entries.
    /// </summary>

    private void ToggleRunCompleted(int exerciseId)
    {
        var entry = RunEntries.FirstOrDefault(e => e.ExerciseId == exerciseId);
        if (entry is null)
        {
            return;
        }

        entry.Completed = !entry.Completed;
    }

    /// <summary>
    /// Toggle the completion state of a run entry.
    /// </summary>

    private string GetExerciseName(int exerciseId)
    {
        return ExerciseList.FirstOrDefault(e => e.Id == exerciseId)?.Name ?? "Exercise";
    }

    /// <summary>
    /// Get the display name for an exercise id.
    /// </summary>

    private async Task AbortRun()
    {
        var confirmed = await JS.InvokeAsync<bool>("confirm", "Are you sure you want to abort this routine run?");
        if (!confirmed) return;

        ActiveRun = null;
        RunEntries = new List<RoutineRunEntryDto>();
    }

    /// <summary>
    /// Abort the active routine run after confirmation.
    /// </summary>

    private async Task SaveRunAsync()
    {
        if (ActiveRun is null)
        {
            return;
        }

        var request = new RoutineRunRequest(RunEntries);
        await Api.SaveRunAsync(UserContext.UserId, ActiveRun.Id, request);
        ActiveRun = null;
        RunEntries = new List<RoutineRunEntryDto>();
    }

    /// <summary>
    /// Save the current routine run to the API.
    /// </summary>
}
