using System.Linq;
using pavesys_iRAP.Business;
using pavesys_iRAP.DTO;
using System.Diagnostics;
using CommunityToolkit.Mvvm.ComponentModel;
using System;
using System.Threading.Tasks;
using System.Collections.Generic;
using pavesys_iRAP.Helper;
using Microsoft.EntityFrameworkCore;
using Serilog;
using System.Collections.ObjectModel;
using CommunityToolkit.Mvvm.Input;
using System.IO;
using Avalonia.Controls;
using DocumentFormat.OpenXml.Math;
using MsBox.Avalonia.Dto;
using MsBox.Avalonia.Models;
using MsBox.Avalonia;
using Avalonia.Notification;
using pavesys_iRAP.Views;
using System.Text;
using System.Net.Sockets;
using System.Net;
using System.Reactive.Threading.Tasks;
using NP.Utilities;


namespace pavesys_iRAP.ViewModels
{
    public partial class MainWindowViewModel : ViewModelBase
    {
        public PersistenceContext PersistentCtx;
        [ObservableProperty]
        public ViewModelBase _content;
        //Create all the viewmodels references here:
        public NewProjectPageViewModel NewProjectPage;
        public MainPageViewModel MainPage;
        public ConfigPageViewModel ConfigPage;
        public LoginPageViewModel LoginPage;
        public CodingPageViewModel CodingPage;
        public ProjectsPageViewModel ProjectsPage;
        public ImageSelectionPageViewModel ImageSelectionPage;
        public EditProjectPageViewModel EditProjectPage;
        public InputCorrectionPageViewModel InputCorrectionPage;
        public ViaSeguraPageViewModel ViaSeguraPage;
        public CodingTwoScreenPageViewModel CodingTwoScreenPage;
        public GerenciamentoPageViewModel GerenciamentoPageView;
        public UserValidationScheduler UserValidationScheduler;
        public PassRecoverPageViewModel PassRecoverPage;
        public RelayCommand<string> codingHotkeyManager { get; }
        public MainWindowViewModel()
        {
            PersistentCtx = CrudHelper.dbCtx;
            codingHotkeyManager = new RelayCommand<string>(CodingHotkeyManager);
            InitializeViews(PersistentCtx);
            Content = LoginPage;
        }


        public void InitializeViews(PersistenceContext ctx)
        {
            try
            {
                NewProjectPage = new NewProjectPageViewModel();
                MainPage = new MainPageViewModel();
                ConfigPage = new ConfigPageViewModel();
                LoginPage = new LoginPageViewModel(ctx.Users);
                CodingPage = new CodingPageViewModel();
                ProjectsPage = new ProjectsPageViewModel(ctx.Projects, AuthModule.hasViaSegura);
                ImageSelectionPage = new ImageSelectionPageViewModel();
                EditProjectPage = new EditProjectPageViewModel();
                InputCorrectionPage = new InputCorrectionPageViewModel();
                ViaSeguraPage = new();
                CodingTwoScreenPage = new();
                GerenciamentoPageView = new();
                UserValidationScheduler = new(this);
                PassRecoverPage = new();

            }
            catch (Exception ex) { Log.Error(ex, "Error Initializing Views"); }
        }

        public async void Gerenciamento()
        {
            GerenciamentoPageView.Users = await AuthModule.GetIRapUsersAsync();
            Content = GerenciamentoPageView;
        }
       
        public void Projects()
        {
            ProjectsPage = new ProjectsPageViewModel(PersistentCtx.Projects, AuthModule.hasViaSegura);
            ProjectsPage.Manager = new NotificationMessageManager();
            Content = ProjectsPage;
        }
        public void ProjectsDoubleTap()
        {
            MainView();
            Coding();
        }
        //Moves the view to projects page and reloads the listed projects
        public void ProjectsReload()
        {
            ProjectsPage.Projects = new ObservableCollection<Project>(PersistentCtx.Projects);
            ProjectsPage.Manager = new NotificationMessageManager();

            Content = ProjectsPage;
        }

        public void EditProjectSave()
        {
            EditProjectPage.SaveProjEdit();
            ProjectsReload();
        }
        public void EditProject()
        {
            if (ProjectsPage.SelecProject != null)
            {
                EditProjectPage.SetContext((int)ProjectsPage.SelecProject.ProjectId);
                Content = EditProjectPage;
            }
            else
                DialogHelper.ShowOkDialog("NoProjectSelected", "SelectProject");
        }
        public async void SaveSelectionImagePage()
        {
            if (ImageSelectionPage.isFirstPhotoSelected && ImageSelectionPage.isLastPhotoSelected)
            {
                NewProjectPage.IsPhotosNotSelected = false;
                NewProjectPage.lastImage = ImageSelectionPage.LastImage;
                NewProjectPage.firstImage = ImageSelectionPage.FirstImage;
                NewProjectPage.isDistanceCalculated = false;
                NewProjectPage.LoadXmlProj(NewProjectPageViewModel.LoadXmlOption.StartKM);
                Content = NewProjectPage;
            }
            else
            {
                string errorPhotosSelected = string.Empty;
                if (!ImageSelectionPage.isFirstPhotoSelected)
                    errorPhotosSelected += Localizer.Localizer.Instance["FirstPhotoNotSelected"];
                if (!ImageSelectionPage.isLastPhotoSelected)
                    errorPhotosSelected += Localizer.Localizer.Instance["LastPhotoNotSelected"];
                var dialog = MessageBoxManager.GetMessageBoxCustom(
                   new MessageBoxCustomParams
                   {
                       ButtonDefinitions = new List<ButtonDefinition>
                       {
                            new ButtonDefinition { Name = "Ok", }
                       },

                       ContentTitle = Localizer.Localizer.Instance["Warning"],
                       ContentMessage = errorPhotosSelected,
                       Icon = MsBox.Avalonia.Enums.Icon.Error,
                       WindowStartupLocation = WindowStartupLocation.CenterOwner,
                       CanResize = false,
                       MaxWidth = 500,
                       MaxHeight = 800,

                       SizeToContent = SizeToContent.Width,
                       ShowInCenter = true,
                       Topmost = false,

                   });
                var result = await dialog.ShowAsync();
            }

        }

        public async Task LoadCodingOneScreen()
        {
            try
            {
                if (ProjectsPage?.SelecProject != null)
                {
                    CodingPage.segment = 0;
                    CodingPage.SegmentCurrent = 1;
                    MainPage.IsNotLoading = false;
                    DialogHelper.ShowLoadingQueryNotification(Localizer.Localizer.Instance["LoadingCodings"], Localizer.Localizer.Instance["ChangeToCodings"], MainPage.Manager);
                    await LoadCodingContextOneScreen();
                    await DialogHelper.DismissLoadingNotification(MainPage.Manager);
                    MainPage.IsNotLoading = true;
                    Content = CodingPage;

                }
            }
            catch (Exception ex)
            {
                Log.Error(ex, "Entering coding without selecting a project");
            }


        }

        public async Task LoadCodingTwoScreen()
        {
            try
            {
                if (ProjectsPage?.SelecProject != null)
                {
                    CodingTwoScreenPage.segment = 0;
                    CodingTwoScreenPage.SegmentCurrent = 1;
                    MainPage.IsNotLoading = false;
                    DialogHelper.ShowLoadingQueryNotification(Localizer.Localizer.Instance["LoadingCodings"], Localizer.Localizer.Instance["ChangeToCodings"], MainPage.Manager);
                    await LoadCodingContextTwoScreen();
                    await DialogHelper.DismissLoadingNotification(MainPage.Manager);
                    MainPage.IsNotLoading = true;
                    Content = CodingTwoScreenPage;

                }
            }
            catch (Exception ex)
            {
                Log.Error(ex, "Entering coding without selecting a project");
            }
        }
        public async void Coding()
        {
            if (ConfigManager.GetTwoScreenMode())
                await LoadCodingTwoScreen();
            else
                await LoadCodingOneScreen();



        }
        public void Config()
        {
            ConfigPage.configLanguage();
            ConfigPage.configStretch();
            ConfigPage.InputErrorLimit = ConfigManager.GetInputErrorLimit();
            ConfigPage.ExportPath = ConfigManager.GetExportPath();
            ConfigPage.ShowTooltips = ConfigManager.GetShowTooltips();
            ConfigPage.TwoScreenMode = ConfigManager.GetTwoScreenMode();
            Content = ConfigPage;
        }

        public void CancelConfig()
        {
            Localizer.Localizer.Instance.LoadLanguage(ConfigPage.initialLanguage);
            Content = MainPage;
        }
        public void SaveConfig()
        {
            ConfigManager.SetLanguage(ConfigPage.language);
            ConfigManager.SetInputErrorLimit((int)ConfigPage.InputErrorLimit);
            ConfigManager.SetStretchType(ConfigPage.currentStretch);
            ConfigManager.SetExportPath(ConfigPage.ExportPath);
            ConfigManager.SetShowTooltips(ConfigPage.ShowTooltips);
            ConfigManager.SetTwoScreenMode(ConfigPage.TwoScreenMode);
            //Localizer.Localizer.Instance = new();

            Content = MainPage;
        }



        public async void MainView()
        {
            //Verifica conexão com banco antes de login
            if (!CrudHelper.CanConnectDB())
            {
                await DialogHelper.ShowOkDialog("Error", "NotConnectedDatabase");
                //usado só para logar os erros
                try
                {
                    CrudHelper.FindCodingImagesByProjectId(1);
                }
                catch (Exception ex)
                {
                    Log.Error(ex, "Error connecting to database");
                }
                return;
            }




            (bool, int, bool) TryLoginResult = new();
            if (AuthModule.accessToken == null || AuthModule.accessToken == string.Empty)
                TryLoginResult = await AuthModule.TryLogin(LoginPage.txtLogin, LoginPage.txtPassword);
            else
                TryLoginResult = (true, 0, false); //Esse false aqui não importa pois no authModule é onde é usado para verificar se tem acesso ao viasegura ou não
            if (TryLoginResult.Item1)
            {

                GerenciamentoPageView.HideCreateUserFields();
                ProjectsPage.IsViaEnabled = TryLoginResult.Item3;
                MainPage.Manager = new NotificationMessageManager();
                MainPage.isUserAdmin = AuthModule.isAdmin;

                Content = MainPage;

            }
            else
            {
                LoginPage.IsPopUpNotOn = false;

                if (TryLoginResult.Item2 == 400)
                {
                    if (await DialogHelper.ShowOkDialog("ErrorLogin", "SuspendedUser", MsBox.Avalonia.Enums.Icon.Error) == MsBox.Avalonia.Enums.ButtonResult.Ok)
                        LoginPage.IsPopUpNotOn = true;
                }
                else if (TryLoginResult.Item2 == 403)
                {
                    if (await DialogHelper.ShowOkDialog("ErrorLogin", "NoUserPass", MsBox.Avalonia.Enums.Icon.Error) == MsBox.Avalonia.Enums.ButtonResult.Ok)
                        LoginPage.IsPopUpNotOn = true;
                }
                else
                {
                    if (await DialogHelper.ShowOkDialog("ErrorLogin", "FailConnectServer", MsBox.Avalonia.Enums.Icon.Error) == MsBox.Avalonia.Enums.ButtonResult.Ok)
                        LoginPage.IsPopUpNotOn = true;
                }
            }
        }


        public async void ImageSelect()
        {
            Log.Debug("ImageSelect click");
            if (NewProjectPage.ProjXmlPath != null)
            {
                if (!Path.Exists(Path.Combine(NewProjectPage.ProjPath, "CAM 1")))
                {
                    await DialogHelper.ShowOkDialog("Error", "NoPhotoFolder");
                    return;
                }
                if (NewProjectPage.ProjXmlPath != null && NewProjectPage.ProjXmlPath != "")
                {
                    ImageSelectionPage.loadPhotosList(Path.Combine(NewProjectPage.ProjPath, "CAM 1"));
                    if (NewProjectPage.xmlLog.logs.FirstOrDefault() != null)
                    {
                        ImageSelectionPage.xmlLog = NewProjectPage.xmlLog;
                        Content = ImageSelectionPage;
                        Log.Debug("ImageSelect Entered");
                    }
                }
            }
        }

        public void NewProject()
        {
            NewProjectPage.Manager = new NotificationMessageManager();
            Content = NewProjectPage;
        }

        public void NewProjectReload()
        {
            NewProjectPage = new NewProjectPageViewModel();
            ImageSelectionPage = new();
            NewProjectPage.Manager = new NotificationMessageManager();
            Content = NewProjectPage;
        }

        public void NewProjectNextButton()
        {
            bool hasJump = false;
            StringBuilder sbXmlError = new StringBuilder();
            //Verifica se não há pulos maiores de 100 metros no xml, se houver emite erro e não deixa continuar;
            for (int i = 0; i < NewProjectPage.xmlLog.logs.Count - 1; i++)
            {
                if (NewProjectPage.xmlLog.logs[i + 1].Odometro - NewProjectPage.xmlLog.logs[i].Odometro > 100)
                {
                    sbXmlError.AppendLine($"Odometro:{NewProjectPage.xmlLog.logs[i].Odometro}" +
                        $" -  Odometro:{NewProjectPage.xmlLog.logs[i + 1].Odometro}");
                    hasJump = true;
                }
                //Debug.WriteLine($"Odo{NewProjectPage.xmlLog.logs[i].Odometro} odoTrecho = {NewProjectPage.xmlLog.logs[i].OdometroTrecho} diff = {NewProjectPage.xmlLog.logs[i].Odometro - NewProjectPage.xmlLog.logs[i + 1].Odometro}");
            }
            if (hasJump)
            {
                DialogHelper.ShowOkDialog("Error", "", nonLocalizedContent: $"Há salto de mais de 100m no xml nas seguintes leituras:\n {sbXmlError}");
                return;
            }
            Log.Debug("NewProject Next Button clicked");
            try
            {
                string invalidFields;
                if (NewProjectPage.ValidateNewProj(out invalidFields))
                {
                    if (!(CrudHelper.dbCtx.Projects.Where(p => p.ProjectDescription == NewProjectPage.ProjDescription && NewProjectPage.ProjRoad == p.Road && NewProjectPage.ProjPath == p.ProjectPath).Count() > 0))
                    {
                        InputCorrectionPage.errorDic = NewProjectPage.errorDictionary;
                        InputCorrectionPage.xmlLogs = NewProjectPage.xmlLog.logs;
                        InputCorrectionPage.xmlInterpolatedLogs = NewProjectPage.xmlLog.logs;
                        InputCorrectionPage.interpolateXml();
                        InputCorrectionPage.Manager = new NotificationMessageManager();
                        Content = InputCorrectionPage;
                    }
                    else
                        DialogHelper.ShowOkDialog("Error", "ProjectAlreadyExists");

                }
                else
                    DialogHelper.ShowOkDialog("InvalidFields", "", nonLocalizedContent: invalidFields);
            }
            catch (Exception ex)
            {
                Log.Debug(ex, "Erro na criação de projeto");
            }
        }

        //Essa função usa o SaveProject do NewProjectViewModel pois essa tela de correção de input não existia antes.
        public async void InputCorrectionUseButton(string parameter)
        {
            Log.Debug("InputCorrection Use map");
            DialogHelper.ShowLoadingQueryNotification("CreateProject", "CreatingProject", InputCorrectionPage.Manager);
            InputCorrectionPage.IsNotUpdating = false;

            try
            {
                if (parameter == "xml")
                {
                    if (await NewProjectPage.SaveProject(InputCorrectionPage.xmlLogs))
                        ProjectsReload();

                }
                else if (parameter == "interpolated")
                {
                    if (await NewProjectPage.SaveProject(InputCorrectionPage.xmlInterpolatedLogs))
                        ProjectsReload();
                }
                else
                {

                    var DensifiedLine = InputCorrectionPage.DensifyLineString(InputCorrectionPage.LineStringKml);
                    if (await NewProjectPage.SaveProject(DensifiedLine))
                        ProjectsReload();
                }
                await DialogHelper.DismissLoadingNotification(InputCorrectionPage.Manager);
                InputCorrectionPage.IsNotUpdating = true;
            }
            catch (Exception ex)
            {
                await DialogHelper.DismissLoadingNotification(InputCorrectionPage.Manager);
                InputCorrectionPage.IsNotUpdating = true;
                Log.Error(ex, "Error Creating Project");

            }
        }
        public async void NewProjectSaveButton()
        {
            //if(await NewProjectPage.SaveXmlProject())
            //    ProjectsReload();

        }
        public async void RecoverPassChangePassButton()
        {
            if (!await PassRecoverPage.ValidatePass())
                return;

            if (await PassRecoverPage.TryChangePassword()) 
              Content = LoginPage;

        }
        public async void GotoLoginPage()
        {
            AuthModule.accessToken = string.Empty;
            Content = LoginPage;
        }
        public async void GotoPassRecover()
        {
            PassRecoverPage = new();
            Content = PassRecoverPage;
        }


        private async Task LoadCodingContextTwoScreen()
        {
            //Stopwatch sw = new();
            //sw.Start();
            List<int?> listCodingId;
            try
            {
                CodingTwoScreenPage.projectId = (int)ProjectsPage.SelecProject.ProjectId;
                CodingTwoScreenPage.currentProject = CrudHelper.GetProjectById((int)ProjectsPage.SelecProject.ProjectId);
                CodingTwoScreenPage.projPath = ProjectsPage.SelecProject.ProjectPath;
                CodingTwoScreenPage.ImageStretch = ConfigManager.GetStretchType();
                //Selciona todas codficações do projeto selecionado
                //System.Diagnostics.Stopwatch loadCod = new();
                //loadCod.Start();
                CodingTwoScreenPage.codingCtx = await CrudHelper.LoadProjectCodings((int)ProjectsPage.SelecProject.ProjectId);
                //loadCod.Stop();
                //Adiciona todos os codingIds do projeto selecionado
                //System.Diagnostics.Stopwatch loadFor = new();
                //loadFor.Start();
                listCodingId = CodingTwoScreenPage.codingCtx.Select(c => c.CodingId).ToList();
                //loadFor.Stop();

                //Seleciona todos codingImage que tem um codingId ligado ao projeto selecionado
                //System.Diagnostics.Stopwatch loadImage = new();
                //loadImage.Start();
                CodingTwoScreenPage.codingImagesCtx = await (from codingImage in PersistentCtx.CodingImages
                                                             where listCodingId.Any(id => id == codingImage.CodingId)
                                                             select codingImage).ToListAsync();

                //loadImage.Stop();
                try
                {
                    CodingTwoScreenPage.SetContext();
                    CodingTwoScreenPage.PopulateGridSegData();
                }
                catch (Exception ex)
                {
                    Log.Error(ex, "Error loading coding context");
                }
                //Debug.WriteLine($"Load Coding = {loadCod.ElapsedMilliseconds} + Load For = {loadFor.ElapsedMilliseconds} + Load Image = { loadImage.ElapsedMilliseconds} === {sw.ElapsedMilliseconds}");
            }
            catch (Exception ex)
            {
                Log.Error(ex, "Loading coding context error.");
            }
        }
        public async void Teste()
        {
            await DialogHelper.ShowOkDialog("Warning", "Warn30");
        }
        private async Task LoadCodingContextOneScreen()
        {
            //Stopwatch sw = new();
            //sw.Start();
            List<int?> listCodingId;
            try
            {
                CodingPage.projectId = (int)ProjectsPage.SelecProject.ProjectId;
                CodingPage.currentProject = CrudHelper.GetProjectById((int)ProjectsPage.SelecProject.ProjectId);
                CodingPage.projPath = ProjectsPage.SelecProject.ProjectPath;
                CodingPage.ImageStretch = ConfigManager.GetStretchType();
                //Selciona todas codficações do projeto selecionado
                //System.Diagnostics.Stopwatch loadCod = new();
                //loadCod.Start();
                CodingPage.codingCtx = await CrudHelper.LoadProjectCodings((int)ProjectsPage.SelecProject.ProjectId);
                //loadCod.Stop();
                //Adiciona todos os codingIds do projeto selecionado
                //System.Diagnostics.Stopwatch loadFor = new();
                //loadFor.Start();
                listCodingId = CodingPage.codingCtx.Select(c => c.CodingId).ToList();
                //loadFor.Stop();

                //Seleciona todos codingImage que tem um codingId ligado ao projeto selecionado
                //System.Diagnostics.Stopwatch loadImage = new();
                //loadImage.Start();
                CodingPage.codingImagesCtx = await (from codingImage in PersistentCtx.CodingImages
                                                    where listCodingId.Any(id => id == codingImage.CodingId)
                                                    select codingImage).ToListAsync();

                //loadImage.Stop();
                try
                {
                    CodingPage.SetContext();
                    CodingPage.PopulateGridSegData();
                }
                catch (Exception ex)
                {
                    Log.Error(ex, "Error loading coding context");
                }
                //Debug.WriteLine($"Load Coding = {loadCod.ElapsedMilliseconds} + Load For = {loadFor.ElapsedMilliseconds} + Load Image = { loadImage.ElapsedMilliseconds} === {sw.ElapsedMilliseconds}");
            }
            catch (Exception ex)
            {
                Log.Error(ex, "Loading coding context error.");
            }
        }

        public void ViaSegura()
        {
            if (ProjectsPage.SelecProject != null)
            {
                ViaSeguraPage.CurrentProgress = 0;
                ViaSeguraPage.MaximumProgress = CrudHelper.FindCodingsByProject((int)ProjectsPage.SelecProject.ProjectId, false).Count;
                ViaSeguraPage.projectId = (int)ProjectsPage.SelecProject.ProjectId;
                Content = ViaSeguraPage;
            }
        }


        public void CodingHotkeyManager(string hotkey)
        {
            if (Content.Equals(CodingPage))
            {

                CodingPage.SelectedTab = hotkey switch
                {
                    "F1" => 0,
                    "F2" => 1,
                    "F3" => 2,
                    "F4" => 3,
                    "F5" => 4,
                    "F6" => 5,
                    "F7" => 6,
                    "F8" => 7
                };
            }
        }
    }
}
