using Valg.EVA.Skanning.Contracts.CommonEnums;
using Valg.EVA.Skanning.Services.TransferCounting;
using Valg.EVA.Skanning.UserInterfaceElements;
using Valg.EVA.Skanning.Identity;
using Valg.EVA.Skanning.Services.Count;
using Microsoft.Win32;
using System;
using System.IO;
using System.Linq;
using System.Threading;
using System.Windows.Input;
using Prism.Commands;
using Valg.EVA.Skanning.Contracts.Security;
using Valg.EVA.Skanning.JobManagement.Startup.Data;
using Valg.EVA.Skanning.JobManagement.Startup.Data.Transfer;
using Valg.EVA.Skanning.JobManagement.Startup.UI.Counting;
using System.Windows.Forms;
using Valg.EVA.Skanning.JobManagement.Startup.Properties;

namespace Valg.EVA.Skanning.JobManagement.Startup.UI.TransferOverview
{
    public class TransferDetailsViewModel : ReactiveViewModel
    {
        private readonly ICountingService countingService;
        private readonly IIdentityProvider identityProvider;
        private readonly ITransferCountingService transferCountingService;
        private readonly IWaitCursorProvider waitCursorProvider;
        private TransferDetailsModel model;
        private int activeTransferJobId;
        private int activeCountingId;
        private readonly IJobManagementApplication jobManagementApplication;
        private string saveFileName = "";

        public Action CloseWindowAction { get; set; }

        public TransferDetailsViewModel(
            ICountingService countingService,
            IJobManagementApplication jobManagementApplication,
            IIdentityProvider identityProvider,
            ITransferCountingService transferCountingService,
            IWaitCursorProvider waitCursorProvider)
        {
            this.countingService = countingService;
            this.jobManagementApplication = jobManagementApplication;
            this.identityProvider = identityProvider;
            this.transferCountingService = transferCountingService;
            this.waitCursorProvider = waitCursorProvider;

            SaveCommand = new DelegateCommand(SaveResultAsFile);
            TransferCommand = new DelegateCommand(TransferCounting);
        }

        private async void TransferCounting()
        {
            CloseWindowAction();
            await jobManagementApplication.TransferResultFileAsync(activeCountingId, activeTransferJobId);
        }

        private void SaveResultAsFile()
        {
            var result = jobManagementApplication.CreateResultFile(activeCountingId);

            if (result != null)
            {
                var directory = GetSaveDirectory();
                if (string.IsNullOrWhiteSpace(directory))
                {
                    return;
                }
                var filename = GetUniqueFilename(directory, Path.GetFileNameWithoutExtension(saveFileName), "zip");
                using (waitCursorProvider.GetWaitCursor())
                {
                    File.WriteAllBytes(filename, result);
                }
                CloseWindowAction();
            }
        }

        public TransferState State => model.State;

        public string ErrorMessage => TransferErrorsPresenter.GetErrorMessage(model.ErrorCode);

        public int TotalVotes => model.TotalVotes;

        public int VerifiedVotes => model.VerifiedVotes;

        public int SuggestedRejectedVotes => model.RejectedVotes;

        public bool IsProcessedDuringEmergencyMode { get; set; }

        public string DisctrictCode { get; set; }

        public string DisctrictName { get; set; }

        public string CountingName { get; set; }

        public bool IsTransferButtonEnabled => IsNotEmergencyModeAndHasTransferClaim();

        public bool IsErrorState => State.Equals(TransferState.ErrorOnServer) || State.Equals(TransferState.FileTransferFailed);

        private CountingType CountingType { get; set; }

        public ICommand SaveCommand { get; }

        public ICommand TransferCommand { get; set; }

        public string VotingCategory { get; set; }

        public string VerifiedOrInDoubtText
        {
            get
            {
                if (CountingType == CountingType.Final)
                {
                    return Resources.Language.JobManagementLanguageString.TransferDetailsPopup_VerifiedVotes;
                }
                else
                {
                    return Resources.Language.JobManagementLanguageString.BoxCounting__ColumnTitle_InDoubt.ToLower();
                }
            }
        }

        public bool IsCountSuggestedRejectedVisible => CountingType == CountingType.Final;
        public string ErrorOnLastAttemptText => Valg.EVA.Skanning.Resources.Language.JobManagementLanguageString.ErrorOnLastTransferAttempt;

        public void LoadData(int countingId, int transferJobId, string countingName, PollingDistrictModel pollingDistrict, CountingType countingType)
        {
            activeTransferJobId = transferJobId;
            activeCountingId = countingId;
            var details = transferCountingService.GetTransferDetails(transferJobId);
            if (details == null)
                return;

            model = new TransferDetailsModel
            {
                Id = details.Id,
                CountingId = details.CountingId,
                ErrorCode = details.ErrorCode,
                RejectedVotes = details.RejectedVotes,
                State = details.State,
                TotalVotes = details.TotalVotes,
                VerifiedVotes = details.VerifiedVotes
            };


            model.Bubble(
                m => m.State,
                this,
                vm => vm.State,
                vm => vm.IsErrorState);

            model.Bubble(
                m => m.ErrorCode,
                this,
                vm => vm.ErrorMessage,
                vm => vm.ErrorOnLastAttemptText);

            CountingName = countingName;
            CountingType = countingType;

            var districtCode = pollingDistrict.Id.Split('.').LastOrDefault();
            DisctrictCode = districtCode + " ";
            DisctrictName = pollingDistrict.Name + " | ";

            SetFileName(countingName, pollingDistrict, districtCode);

            VotingCategory = VotingCategoryMapping.LocaleName(pollingDistrict.VotingCategory.Code);

            SetIsProcessedDuringEmergencyMode(countingId);
        }

        private void SetIsProcessedDuringEmergencyMode(int countingId)
        {
            
            var counting = countingService.GetCounting(countingId);
            IsProcessedDuringEmergencyMode = counting.IsProcessedInEmergencyMode;
        }

        private void SetFileName(string countingName, PollingDistrictModel pollingDistrict, string districtCode)
        {
            if (!IsDistrictUnspecified(districtCode))
            {
                saveFileName = $"{districtCode}_{pollingDistrict.Name}_{pollingDistrict.VotingCategory.Code}_{countingName}";
            }
            else
            {
                saveFileName = $"{pollingDistrict.Name}_{pollingDistrict.VotingCategory.Code}_{countingName}";
            }
        }

        private bool IsDistrictUnspecified(string districtCode)
        {
            return districtCode == "0000";
        }

        private bool IsNotEmergencyModeAndHasTransferClaim()
        {
            if (identityProvider.HasClaim(ClaimDefinitions.IsEmergencyModeClaimType))
            {
                return false;
            }
            else
            {
                return identityProvider.HasAreaSpecificClaim(ClaimDefinitions.TransferAccessClaimType);
            }
        }
        private string GetSaveDirectory()
        {
            string ret = null;
            var initialPath = Settings.Default.SaveCountingDirectory;
            if (string.IsNullOrWhiteSpace(initialPath))
            {
                initialPath = TryFindFlashDrive();
            }
            var t = new Thread(() =>
            {
                var fbd = GetFolderBrowserDialog(initialPath);
                if (fbd.ShowDialog(new System.Windows.Forms.Form() { TopMost = true }) == DialogResult.Cancel)
                {
                    return;
                }

                ret = fbd.SelectedPath;
                Settings.Default.SaveCountingDirectory = ret;
            });

            t.SetApartmentState(ApartmentState.STA);
            t.Start();
            t.Join();

            return ret;

        }

        private static string TryFindFlashDrive()
        {
            var allDrives = DriveInfo.GetDrives();
            return allDrives.FirstOrDefault(driveInfo => driveInfo.DriveType == DriveType.Removable)?.Name;
         }

        private FolderBrowserDialog GetFolderBrowserDialog(string initialPath)
        {
            var ret = new FolderBrowserDialog
            {
                ShowNewFolderButton = false
            };
            ret.SelectedPath = string.IsNullOrWhiteSpace(initialPath) ? TryFindFlashDrive() : initialPath;
            return ret;
        }

        private string GetUniqueFilename(string directory, string basename, string extension)
        {
            var plain = Path.Combine(directory, Path.ChangeExtension(basename, extension));
            if (!File.Exists(plain))
            {
                return plain;
            }

            foreach (var number in Enumerable.Range(1, int.MaxValue - 1))
            {
                var filename = Path.Combine(directory, Path.ChangeExtension($"{basename} ({number})", extension));
                if (!File.Exists(filename))
                {
                    return filename;
                }
            }
            throw new Exception("Unable to generate a unique file name");
        }
    }
}
