@using Microsoft.Extensions.Logging
@using SteUp.Shared.Components.Layout
@using SteUp.Shared.Components.Layout.Overlay
@using SteUp.Shared.Components.Layout.Spinner
@using SteUp.Shared.Components.SingleElements.Card.ModalForm
@using SteUp.Shared.Components.SingleElements.MessageBox
@using SteUp.Shared.Core.Dto
@using SteUp.Shared.Core.Entities
@using SteUp.Shared.Core.Enum
@using SteUp.Shared.Core.Interface.IntegryApi
@using SteUp.Shared.Core.Interface.LocalDb
@using SteUp.Shared.Core.Interface.System
@using SteUp.Shared.Core.Messages.Scanner
@inject INetworkService NetworkService
@inject IDialogService Dialog
@inject IIntegryApiService IntegryApiService
@inject IFileManager FileManager
@inject IIspezioniService IspezioniService
@inject IIntegrySteupService IntegrySteupService
@inject OnScannerService OnScannerService
@inject ILogger<ModalFormScheda> Logger

<MudDialog Class="customDialog-form">
    <DialogContent>
        <MudForm ReadOnly="ReadOnly" @ref="_form">
            <HeaderLayout Cancel="true" OnCancel="@Cancel" LabelSave="@LabelSave"
                          OnSave="Save" Title="Scheda"/>

            <div class="content">

                <CardFormModal Title="Reparto" Loading="SteupDataService.Reparti.IsNullOrEmpty()">
                    <MudSelectExtended T="JtbFasiDto?" Variant="Variant.Text"
                                       @bind-Value="Scheda.Reparto" ToStringFunc="@(x => x?.Descrizione)"
                                       @bind-Value:after="OnAfterChangeReparto" Required="true"
                                       RequiredError="Reparto obbligatorio">
                        @foreach (var fasi in SteupDataService.Reparti)
                        {
                            <MudSelectItemExtended Class="custom-item-select" Value="@fasi">
                                @fasi.Descrizione
                            </MudSelectItemExtended>
                        }
                    </MudSelectExtended>
                </CardFormModal>

                <CardFormModal Title="Motivo" Loading="SteupDataService.TipiAttività.IsNullOrEmpty()">
                    <MudSelectExtended ReadOnly="@Scheda.CodJfas.IsNullOrEmpty()" T="string?"
                                       Variant="Variant.Text"
                                       @bind-Value="Scheda.ActivityTypeId" @bind-Value:after="OnAfterChangeValue"
                                       Required="true" RequiredError="Motivo obbligatorio">
                        @foreach (var type in SteupDataService.TipiAttività.Where(x => x.CodJfas.EqualsIgnoreCase(Scheda.CodJfas!)))
                        {
                            <MudSelectItemExtended Class="custom-item-select"
                                                   Value="@type.ActivityTypeId">@type.ActivityTypeId</MudSelectItemExtended>
                        }
                    </MudSelectExtended>
                </CardFormModal>

                @if (FileLoading)
                {
                    <CardFormModal Title="Immagini">
                        <div class="container-attached">
                            <MudProgressLinear Color="Color.Primary" Indeterminate="true" Class="my-3"/>
                        </div>
                    </CardFormModal>
                }
                else if (!AttachedList.IsNullOrEmpty())
                {
                    <CardFormModal Title="Immagini">
                        <div class="container-attached">
                            <div class="scroll-attached">
                                @foreach (var item in AttachedList!.Select((p, index) => new { p, index }))
                                {
                                    <MudCard>
                                        @if (!item.p.ThumbPath.IsNullOrEmpty())
                                        {
                                            <MudCardMedia Image="@item.p.ThumbPath" Height="100"/>
                                        }
                                        <MudCardContent Class="image_card">
                                            <MudText Typo="Typo.subtitle1"><b>@item.p.Name</b></MudText>
                                            @if (item.p.ToUpload)
                                            {
                                                <MudIconButton Variant="Variant.Outlined"
                                                               Icon="@Icons.Material.Rounded.Close"
                                                               Size="Size.Small" Color="Color.Error"
                                                               OnClick="@(() => OnRemoveAttached(item.index))"/>
                                            }
                                        </MudCardContent>
                                    </MudCard>
                                }
                            </div>
                        </div>
                    </CardFormModal>
                }
                
                @if (!ReadOnly)
                {
                    <div class="container-button ripple-container">
                        <MudButton Class="button-settings green-icon"
                                   FullWidth="true"
                                   StartIcon="@Icons.Material.Rounded.AttachFile"
                                   Size="Size.Medium"
                                   OnClick="@OpenAddAttached"
                                   Variant="Variant.Outlined">
                            Aggiungi foto
                        </MudButton>
                    </div>
                }

                <CardFormModal Title="Articoli">
                    @if (!ReadOnly)
                    {
                        <div class="input-manual-barcode">
                            <MudTextField FullWidth="true" T="string?" Variant="Variant.Text"
                                          @bind-Value="ManualBarcode" Placeholder="Digita manualmente il codice"/>
                            <MudIconButton Color="Color.Primary" OnClick="@OnManualBarcodeSet"
                                           Size="Size.Small" Icon="@Icons.Material.Rounded.Send"/>
                        </div>
                    }
                    @if (!Scheda.Articoli.IsNullOrEmpty())
                    {
                        <div class="art-list">
                            @foreach (var articolo in Scheda.Articoli)
                            {
                                <MudChip T="string" OnClose="@(() => RemoveArt(articolo.Barcode))"
                                         style="height: auto;">
                                    <MudStack Direction="Column" Spacing="0" class="py-1">
                                        <MudText Typo="Typo.subtitle2" Style="line-height: 1.1; font-weight: 700;">
                                            @articolo.Descrizione
                                        </MudText>
                                        <MudText Typo="Typo.body2" Style="line-height: 1.5;">
                                            Barcode: @articolo.Barcode
                                        </MudText>
                                    </MudStack>
                                </MudChip>
                            }
                        </div>
                    }
                </CardFormModal>

                @if (NetworkService.ConnectionAvailable && !ReadOnly)
                {
                    <div class="container-button ripple-container">
                        <MudButton Class="button-settings red-icon"
                                   FullWidth="true"
                                   StartIcon="@Icons.Material.Rounded.Balance"
                                   Size="Size.Medium"
                                   OnClick="@OpenSelectArt"
                                   Variant="Variant.Outlined">
                            Consulta articoli
                        </MudButton>
                    </div>
                }

                <CardFormModal Title="Scadenza">
                    <MudSelectExtended FullWidth="true" T="int" Variant="Variant.Text"
                                       @bind-Value="@Scheda.Scadenza" @bind-Value:after="OnAfterChangeValue">
                        <MudSelectItemExtended Class="custom-item-select" Text="24H" Value="3"/>
                        <MudSelectItemExtended Class="custom-item-select" Text="1 Settimana" Value="2"/>
                        <MudSelectItemExtended Class="custom-item-select" Text="1 Mese" Value="1"/>
                        <MudSelectItemExtended Class="custom-item-select" Text="2 Mesi" Value="0"/>
                    </MudSelectExtended>
                </CardFormModal>

                <CardFormModal Title="Responsabile">
                    <MudTextField FullWidth="true" T="string?" Variant="Variant.Text"
                                  @bind-Value="Scheda.Responsabile" @bind-Value:after="OnAfterChangeValue"
                                  DebounceInterval="500" OnDebounceIntervalElapsed="OnAfterChangeValue"/>
                </CardFormModal>

                <CardFormModal Title="Note">
                    <MudTextField T="string?" Variant="Variant.Text" Lines="3"
                                  @bind-Value="Scheda.Note" @bind-Value:after="OnAfterChangeValue"
                                  DebounceInterval="500" OnDebounceIntervalElapsed="OnAfterChangeValue"/>
                </CardFormModal>

                <div class="container-button">
                    <MudButton Class="button-settings primary-icon"
                               FullWidth="true"
                               StartIcon="@Icons.Material.Rounded.Description"
                               Size="Size.Medium"
                               OnClick="@SuggestActivityDescription"
                               Variant="Variant.Outlined">
                        Suggerisci note descrittive
                    </MudButton>
                </div>
            </div>
        </MudForm>

        <ConfirmUpdateActivity @ref="_confirmUpdateMessage"/>
    </DialogContent>
</MudDialog>

<SpinnerOverlay VisibleOverlay="VisibleOverlay" SuccessAnimation="SuccessAnimation"/>
<LoaderLayout Visible="OnLoading" Text="@TextLoading"/>

@code {
    [CascadingParameter] private IMudDialogInstance MudDialog { get; set; } = null!;
    [Parameter] public required string CodMdep { get; set; }
    [Parameter] public required DateTime Data { get; set; }
    [Parameter] public bool IsNew { get; set; }
    [Parameter] public bool ReadOnly { get; set; }
    [Parameter] public Scheda Scheda { get; set; } = new();

    private string? ManualBarcode { get; set; }

    //Overlay
    private bool VisibleOverlay { get; set; }
    private bool SuccessAnimation { get; set; }
    private bool OnLoading { get; set; }
    private string? TextLoading { get; set; }

    private bool FileLoading { get; set; }

    private ConfirmUpdateActivity _confirmUpdateMessage = null!;
    private MudForm _form = null!;

    private string? LabelSave { get; set; }
    private bool IsDirty { get; set; }
    private Scheda _originalScheda = null!;
    private List<AttachedDto>? AttachedList { get; set; }

    protected override void OnInitialized()
    {
        Snackbar.Configuration.PositionClass = Defaults.Classes.Position.TopCenter;

        OnScannerService.OnNewScanSuccessful += HandleNewScanSuccessful;
        OnScannerService.OnErrorScan += OnErrorScan;

        _originalScheda = Scheda.Clone();
        Snackbar.Configuration.PositionClass = Defaults.Classes.Position.TopCenter;

        LoadAttached();
    }

    private void LoadAttached()
    {
        if (Scheda.ImageNames == null) return;

        FileLoading = true;
        StateHasChanged();

        Task.Run(async () =>
        {
            var fileList = await FileManager.GetInspectionFiles(
                new Ispezione
                {
                    CodMdep = CodMdep,
                    Data = Data,
                    Rilevatore = UserSession.User.Username
                },
                Scheda.ImageNames
            );

            await InvokeAsync(() =>
            {
                AttachedList = fileList;
                FileLoading = false;

                StateHasChanged();
            });
        });
    }

    private async Task Save()
    {
        VisibleOverlay = true;
        StateHasChanged();
        
        SaveSchedaResponseDto? apiResponse = null;
        try
        {
            if (NetworkService.ConnectionAvailable)
            {
                apiResponse = await IntegrySteupService.SaveScheda(
                    new SaveRequestDto
                    {
                        LocalIdScheda = Scheda.Id,
                        ActivityTypeId = Scheda.ActivityTypeId,
                        CodJfas = Scheda.CodJfas,
                        CodMdep = CodMdep,
                        DataCreazione = Data,
                        Note = Scheda.Note,
                        PersonaRif = Scheda.Responsabile,
                        Barcodes = Scheda.Articoli.ConvertAll(x => x.Barcode),
                        Scandeza = (ScadenzaEnum)Scheda.Scadenza,
                        ParentActivityId = IsNew ? null : Scheda.Ispezione?.ActivityId
                    }
                );
            }
        }
        catch (Exception e)
        {
            var message = e.Message;
            
            Logger.LogError(e, message);
            Console.WriteLine(message);
            await Dialog.ShowError(message);
        }

        if (IsNew) await NewSave(apiResponse);
        else await Update(apiResponse);

        if (Scheda.ActivityId.IsValorized()) await UploadFile(Scheda);

        await FileManager.CleanTempStorageAsync();

        SuccessAnimation = true;
        StateHasChanged();

        await Task.Delay(1250);
        DisposeMessage();
        MudDialog.Close(Scheda);
    }

    private async Task NewSave(SaveSchedaResponseDto? apiResponse)
    {
        if (!AttachedList.IsNullOrEmpty())
        {
            foreach (var attached in AttachedList!)
            {
                var fileNameAdded = await FileManager.SaveInspectionFile(
                    new Ispezione
                    {
                        CodMdep = CodMdep,
                        Data = Data,
                        Rilevatore = UserSession.User.Username
                    },
                    attached.FileBytes!,
                    attached.Name!
                );

                Scheda.ImageNames ??= [];
                if (fileNameAdded != null)
                    Scheda.ImageNames.Add(fileNameAdded);
            }
        }

        if (apiResponse != null)
        {
            await IspezioniService.UpdateActivityIdIspezioneAsync(CodMdep, Data,
                UserSession.User.Username, apiResponse.ActivityIdIspezione
            );
            Scheda.ActivityId = apiResponse.ActivityIdScheda;
        }

        await IspezioniService.AddSchedaAsync(CodMdep, Data, UserSession.User.Username, Scheda);
    }

    private async Task Update(SaveSchedaResponseDto? apiResponse)
    {
        if (!AttachedList.IsNullOrEmpty())
        {
            foreach (var attached in AttachedList!.Where(x => !x.SavedOnAppData))
            {
                var ispezione = new Ispezione
                {
                    CodMdep = CodMdep,
                    Data = Data,
                    Rilevatore = UserSession.User.Username
                };

                if (!attached.ToRemove)
                {
                    var fileNameAdded = await FileManager.SaveInspectionFile(
                        ispezione,
                        attached.FileBytes!,
                        attached.Name!
                    );

                    Scheda.ImageNames ??= [];
                    if (fileNameAdded != null)
                        Scheda.ImageNames.Add(fileNameAdded);
                }
                else
                    _ = FileManager.RemoveInspectionFile(ispezione, attached.Name!);
            }
        }

        Scheda.ActivityId = apiResponse?.ActivityIdScheda;
        await IspezioniService.UpdateSchedaAsync(Scheda);

        if (apiResponse != null)
            await IspezioniService.UpdateActivityIdIspezioneAsync(CodMdep, Data,
                UserSession.User.Username, apiResponse.ActivityIdIspezione
            );
    }

    private async Task UploadFile(Scheda scheda)
    {
        if (AttachedList.IsNullOrEmpty()) return;

        var ispezione = new Ispezione
        {
            CodMdep = CodMdep,
            Data = Data,
            Rilevatore = UserSession.User.Username
        };

        foreach (var file in AttachedList!.Where(x => x.ToUpload))
        {
            if (file.FileBytes == null || file.Name == null) continue;

            await IntegrySteupService.UploadFile(scheda.ActivityId!, file.FileBytes, file.Name);
            var newPath = await FileManager.MoveInspectionFileFromToUploadToFinal(ispezione, file.Name);
            if (newPath == null) continue;
            
            var filePathToRemove = FileManager.GetFileToUploadDir(ispezione, file.Name);
            scheda.ImageNames!.Remove(filePathToRemove);
            scheda.ImageNames.Add(newPath);
        }

        await IspezioniService.UpdateFileListSchedaAsync(scheda.Id, scheda.ImageNames);
    }

    private async Task Cancel()
    {
        if (await CheckSavePreAction())
        {
            await FileManager.CleanTempStorageAsync();
            DisposeMessage();
            MudDialog.Cancel();
        }
    }

    #region Form

    private void OnAfterChangeReparto()
    {
        Scheda.ActivityTypeId = null;
        OnAfterChangeValue();
    }

    private void OnAfterChangeValue()
    {
        RecalcDirty();
        StateHasChanged();
    }

    private void RecalcDirty(bool forceTrue = false)
    {
        IsDirty = forceTrue || !ValueComparer.AreEqual(Scheda, _originalScheda);

        if (IsDirty) LabelSave = !IsNew ? "Aggiorna" : "Salva";
        else LabelSave = null;
    }

    private static class ValueComparer
    {
        public static bool AreEqual(Scheda? a, Scheda? b)
        {
            if (a is null || b is null) return a == b;

            return
                a.CodJfas == b.CodJfas &&
                a.DescrizioneReparto == b.DescrizioneReparto &&
                a.ActivityTypeId == b.ActivityTypeId &&
                a.Note == b.Note &&
                a.Responsabile == b.Responsabile &&
                a.Scadenza == b.Scadenza;
        }
    }

    private async Task<bool> CheckSavePreAction()
    {
        if (!IsDirty) return true;

        var resul = await _confirmUpdateMessage.ShowAsync();
        if (resul is not true) return true;

        VisibleOverlay = true;
        StateHasChanged();

        await Submit();

        VisibleOverlay = false;
        StateHasChanged();

        return false;
    }

    private async Task Submit()
    {
        await _form.Validate();
        if (_form.IsValid) await Save();
    }

    #endregion

    #region File

    private async Task OpenAddAttached()
    {
        var result = await Dialog.OpenAddAttached();
        if (result is not { Canceled: false, Data: List<AttachedDto> attachedList }) return;

        OnLoading = true;
        await InvokeAsync(StateHasChanged);
        await Task.Yield();

        // prepara placeholder in UI subito (così vedi le card con spinner)
        AttachedList ??= [];
        foreach (var a in attachedList)
            AttachedList.Add(new AttachedDto { Name = a.Name, MimeType = a.MimeType, FileBytes = a.FileBytes });

        await InvokeAsync(StateHasChanged);

        RecalcDirty(true);

        // Processa in background e aggiorna UI man mano (o a blocchi)
        _ = Task.Run(async () =>
        {
            for (var i = 0; i < attachedList.Count; i++)
            {
                var a = attachedList[i];
                if (a.FileBytes is null || a.Name is null) continue;

                var (origUrl, thumbUrl) = await FileManager.SaveAndCreateThumbAsync(a.FileBytes, a.Name);

                await InvokeAsync(() =>
                {
                    var target = AttachedList![AttachedList.Count - attachedList.Count + i];
                    target.TempPath = origUrl;
                    target.ThumbPath = thumbUrl;
                    target.ToUpload = true;
                    StateHasChanged();
                });
            }

            await InvokeAsync(() =>
            {
                OnLoading = false;
                StateHasChanged();
            });
        });
    }

    private void OpenSelectArt()
    {
        if (Scheda.CodJfas.IsNullOrEmpty())
        {
            Snackbar.Clear();
            Snackbar.Add("Selezionare prima il reparto", Severity.Error);
            return;
        }

        OnLoading = true;
        TextLoading = "Download articoli in griglia";
        StateHasChanged();

        _ = Task.Run(async () =>
        {
            List<ArticoliInGrigliaDto>? articoli;
            try
            {
                articoli = await IntegrySteupService.RetrieveGrigliaPlu(
                    new RetrieveGrigliaPluRequestDto
                    {
                        CodMdep = CodMdep,
                        ActivityTypeId = Scheda.ActivityTypeId,
                        CodJfas = Scheda.CodJfas
                    }
                );
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
                
                await InvokeAsync(() =>
                {
                    OnLoading = false;
                    TextLoading = null;
                    StateHasChanged();

                    OnError(e, e.Message);
                });

                return;
            }

            var modal = await Dialog.OpenSelectArt(articoli);

            await InvokeAsync(() =>
            {
                OnLoading = false;
                TextLoading = null;

                StateHasChanged();
            });

            if (modal is { Canceled: false, Data: List<ArticoliInGrigliaDto> articoliSelezionati })
            {
                Scheda.Articoli.AddRange(articoliSelezionati.ConvertAll(x => new SchedaArticolo
                    {
                        Barcode = x.Barcode,
                        Descrizione = x.Descrizione
                    })
                );

                await InvokeAsync(StateHasChanged);
            }
        });
    }

    private void OnRemoveAttached(int index)
    {
        if (AttachedList is null || index < 0 || index >= AttachedList.Count) return;

        if (AttachedList[index].SavedOnAppData)
            AttachedList[index].ToRemove = true;

        AttachedList.RemoveAt(index);
        RecalcDirty(true);
        StateHasChanged();
    }

    #endregion

    private void SuggestActivityDescription()
    {
        if (Scheda.ActivityTypeId == null)
        {
            Snackbar.Add("Indicare prima il motivo", Severity.Error);
            return;
        }

        VisibleOverlay = true;
        StateHasChanged();

        _ = Task.Run(async () =>
        {
            var activityDescriptions = await IntegryApiService.SuggestActivityDescription(Scheda.ActivityTypeId);

            var modal = await Dialog.OpenSuggestActivityDescription(activityDescriptions);

            if (modal is { Canceled: false, Data: not null })
                Scheda.Note = modal.Data!.ToString();

            VisibleOverlay = false;
            await InvokeAsync(StateHasChanged);
        });
    }

    #region Scanner

    private void OnManualBarcodeSet()
    {
        if (ManualBarcode == null) return;

        HandleNewScanSuccessful(ManualBarcode);
        ManualBarcode = null;
    }

    private async void HandleNewScanSuccessful(string? value)
    {
        try
        {
            if (value is null) return;
            if (Scheda.Articoli.Any(x => x.Barcode.Equals(value))) return;

            await InvokeAsync(() =>
            {
                OnLoading = true;
                StateHasChanged();
            });

            var art = await IntegrySteupService.RetrieveArtFromBarcode(value);

            await InvokeAsync(() =>
            {
                OnLoading = false;
                StateHasChanged();

                if (art != null)
                {
                    RecalcDirty(true);

                    Scheda.Articoli.Add(new SchedaArticolo
                    {
                        Barcode = art.Barcode,
                        Descrizione = art.Descrizione
                    });

                    StateHasChanged();
                }
                else
                {
                    OnError(null, "Nessun articolo trovato");
                }
            });
        }
        catch (Exception e)
        {
            Console.WriteLine(e.Message);
            
            await InvokeAsync(() =>
            {
                OnLoading = false;
                StateHasChanged();
            });

            OnError(e, e.Message);
        }
    }

    private void OnErrorScan(string? value) => OnError(new Exception(value), value);

    #endregion

    private void OnError(Exception? e, string? errorMessage)
    {
        if (e != null) Logger.LogError(e, errorMessage);
        if (errorMessage == null) return;
        
        _ = Dialog.ShowError(errorMessage);
    }

    private void RemoveArt(string barcode)
    {
        var index = Scheda.Articoli.FindIndex(x => x.Barcode.Equals(barcode));
        if (index < 0) return;

        RecalcDirty(true);
        Scheda.Articoli.RemoveAt(index);
        StateHasChanged();
    }

    private void DisposeMessage()
    {
        OnScannerService.OnNewScanSuccessful -= HandleNewScanSuccessful;
        OnScannerService.OnErrorScan -= OnErrorScan;
    }

}