using log4net;
using Org.BouncyCastle.Math;
using Org.BouncyCastle.Tsp;
using System;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Windows.Forms;
using System.Windows.Input;
using TSManagementApp.Communication;
using TSManagementApp.Enums;
using TSManagementApp.Models;
using TSManagementApp.Properties;
using TSManagementApp.Utils;
using TSManagementApp.Views;

namespace TSManagementApp.ViewModels
{
    public class CreateTimeStampViewModel : BaseViewModel
    {
        #region Logger
        private static readonly ILog log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
        #endregion

        private MainWindowViewModel _vmMainWin;
        private CreateTimeStampView _createTimeStampWindow;
        public CreateTimeStampViewModel(MainWindowViewModel vmMainWin, CreateTimeStampView view, string filePath)
        {
            _vmMainWin = vmMainWin;
            _createTimeStampWindow = view;
            SourceFilePath = filePath;
            InitSettings();
        }

        #region Propertires

        public ObservableCollection<ShaAlgorithmType> ShaAlogirthms { get; set; }
        private string _sourceFilePath;
        public string SourceFilePath { get => _sourceFilePath; set { _sourceFilePath = value; OnPropertyChanged("SourceFilePath"); } }
        private bool _nonceYesIsChecked;
        public bool NonceYesIsChecked { get => _nonceYesIsChecked; set { _nonceYesIsChecked = value; OnPropertyChanged("NonceYesIsChecked"); } }
        private bool _nonceNoIsChecked;
        public bool NonceNoIsChecked { get => _nonceNoIsChecked; set { _nonceNoIsChecked = value; OnPropertyChanged("NonceNoIsChecked"); } }
        private bool _manualOidPoliticIsChecked;
        public bool ManualOidPoliticIsChecked
        {
            get => _manualOidPoliticIsChecked;
            set
            {
                _manualOidPoliticIsChecked = value;
                ManualOidPoliticIsEnabled = value;
                if (value == true || !string.IsNullOrWhiteSpace(Manager.Instance.SettingsManager.Settings.TsaSettings.Politics))
                    ManualOidPolitic = Manager.Instance.SettingsManager.Settings.TsaSettings.Politics;
                if (value == false)
                    ManualOidPolitic = null;
                OnPropertyChanged("ManualOidPoliticIsChecked");
            }
        }
        private string _manualOidPolitic;
        public string ManualOidPolitic { get => _manualOidPolitic; set { _manualOidPolitic = value; OnPropertyChanged("ManualOidPolitic"); } }
        private bool _manualOidPoliticIsEnabled;
        public bool ManualOidPoliticIsEnabled { get => _manualOidPoliticIsEnabled; set { _manualOidPoliticIsEnabled = value; OnPropertyChanged("ManualOidPoliticIsEnabled"); } }

        public string WindowTitle { get; set; }

        #endregion

        #region Commands

        public ICommand CommandChangeSourceFile { get; set; }
        public ICommand CommandCreateTimeStamp { get; set; }

        public void ChangeSourceFile()
        {
            ChooseFileUsingDialog();
        }

        public void CreateTimeStamp()
        {
            try
            {
                log.Info($"Creating time stamp for file {SourceFilePath}");
                System.Security.Cryptography.HashAlgorithm sha;
                BigInteger nonce = null;
                ShaAlgorithmType selectedSha = null;
                foreach (ShaAlgorithmType shaAlg in ShaAlogirthms)
                {
                    if (shaAlg.IsChecked)
                        selectedSha = shaAlg;
                }
                switch (selectedSha.Name)
                {
                    case "SHA-1":
                        sha = SHA1.Create(); break;
                    case "SHA-256":
                        sha = SHA256.Create(); break;
                    case "SHA-512":
                        sha = SHA512.Create(); break;
                    default:
                        throw new Exception(Resources.CreateTsVM_UnsupportedHash);
                }
                var fileStream = new FileStream(SourceFilePath, FileMode.Open);
                byte[] hashData = sha.ComputeHash(fileStream);
                fileStream.Dispose();
                if (NonceYesIsChecked)
                {
                    Random random = new Random();
                    nonce = new BigInteger(64, random);
                }

                BouncyCastleTSProvider provider = new BouncyCastleTSProvider();
                TimeStampReqRes response = new TimeStampReqRes();

                SettingsModel settings = Manager.Instance.SettingsManager.Settings;
                response = provider.RequestTimeStamp(selectedSha, hashData, nonce, new TimeStampServer(settings.TsaSettings.SelectedTimeStampServer,
                    settings.TsaSettings.LoginName, settings.TsaSettings.LoginPassword), ManualOidPolitic);
                if (response != null)
                {
                    TimeStampResponse timeStampResponse = new TimeStampResponse(response.TimeStampResponse);
                    if (timeStampResponse.Status == (int)TimeStampResponseStatus.Granted || timeStampResponse.Status == (int)TimeStampResponseStatus.GrantedWithMods)
                    {
                        response.SourceFilePath = SourceFilePath;
                        File.WriteAllBytes(response.SourceFilePath + ".tsr", timeStampResponse.GetEncoded());
                        FileInfo tsFileInfo = new FileInfo(response.SourceFilePath + ".tsr");
                        response.TimeStampFilePath = tsFileInfo.FullName;
                        response.TimeStampFileName = tsFileInfo.Name;
                        response.GenTime = timeStampResponse.TimeStampToken.TimeStampInfo.GenTime.ToString("yyyy/MM/dd HH:mm:ss");
                        response.TimeStampFileType = TimeStampReqRes.FileTypeResponse;
                    }
                    else
                    {
                        response.SourceFilePath = SourceFilePath;
                        File.WriteAllBytes(response.SourceFilePath + ".tsr", timeStampResponse.GetEncoded());
                        response.TimeStampFilePath = new FileInfo(response.SourceFilePath).FullName;
                        response.TimeStampFileName = new FileInfo(response.SourceFilePath).Name;
                        response.TimeStampFileType = TimeStampReqRes.FileTypeResponse;
                        response.GenTime = null;
                    }
                    Manager.Instance.TimeStampsManager.AddTimeStamp(response);
                    Manager.Instance.TimeStampsManager.SerializeTimeStamps();
                    if (timeStampResponse.Status != (int)TimeStampResponseStatus.Granted && timeStampResponse.Status != (int)TimeStampResponseStatus.GrantedWithMods)
                    {
                        Messages.MsgError($"{Resources.CreateTsVM_TsNotIssued_Part1}: {timeStampResponse.Status} - {GetEnumDescription.GetTsrStatusString(timeStampResponse.Status)}, {timeStampResponse.GetStatusString()}\n" +
                            $"{Resources.CreateTsVM_TsNotIssued_Part2} - {GetEnumDescription.GetTsrPkiFailureInfo(timeStampResponse.GetFailInfo().IntValue)}");
                    }
                    _createTimeStampWindow.Close();
                    log.Info($"Time stamp for file {SourceFilePath} was successfully created");
                    _vmMainWin.SelectedTimeStamp = _vmMainWin.TimeStamps.Last();
                    _vmMainWin.GoUcTimeStampDetails();
                }
            }
            catch(Exception ex)
            {
                log.Error($"Error while creating time stamp -  {ex.ToString()}");
                Messages.MsgError($"{Resources.CreateTsVM_ErrorWhileCreatingTs} - {ex.Message}");
            }
        }

        #endregion

        #region Methods

        private void InitSettings()
        {
            CommandChangeSourceFile = new CommandGeneric(ChangeSourceFile);
            CommandCreateTimeStamp = new CommandGeneric(CreateTimeStamp);
            ShaAlogirthms = new ObservableCollection<ShaAlgorithmType>();
            WindowTitle = Resources.CreateTsVM_WindowTitleTsa;
            if (!string.IsNullOrWhiteSpace(Manager.Instance.SettingsManager.Settings.TsaSettings.Politics))
            {
                ManualOidPoliticIsChecked = true;
                ManualOidPolitic = Manager.Instance.SettingsManager.Settings.TsaSettings.Politics;
            }
            foreach (ShaAlgorithmType sha in Manager.Instance.SettingsManager.TsaShaAlogirthms)
                ShaAlogirthms.Add(new ShaAlgorithmType(sha.Name, sha.Oid, sha.IsChecked));
            foreach (ShaAlgorithmType sha in ShaAlogirthms)
            {
                if (sha.Name == Manager.Instance.SettingsManager.Settings.TsaSettings.MessageImprintType)
                    sha.IsChecked = true;
            }
            NonceNoIsChecked = true;
        }

        private void ChooseFileUsingDialog()
        {
            try
            {
                using (OpenFileDialog dialog = new OpenFileDialog())
                {
                    dialog.Title = Resources.CreateTsVM_ChooseFile;
                    dialog.Filter = $"{Resources.CreateTsVM_PdfFile}|*.pdf|{Resources.CreateTsVM_Textfiles}|*.txt;*.docx;*.doc;*.rtf|{Resources.CreateTsVM_Allfiles}|*.*";
                    dialog.Multiselect = false;
                    if(dialog.ShowDialog() == DialogResult.OK)
                        SourceFilePath = dialog.FileName;
                }
            }
            catch (Exception ex)
            {
                Messages.MsgError(ex.Message);
            }
            
        }

        private X509Certificate GetX509Certificate(string subject)
        {
            X509Store certStore = new X509Store(StoreName.My, StoreLocation.CurrentUser);
            certStore.Open(OpenFlags.ReadOnly);
            foreach (X509Certificate2 cert in certStore.Certificates)
            {
                if (cert.Subject == subject)
                    return new X509Certificate(cert.RawData);
            }
            return null;
        }

        #endregion
    }
}
