using Hochbaustatistik.Business;
using Hochbaustatistik.Database;
using NLog;
using NLog.Fluent;
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;

namespace Hochbaustatistik.Utilities
{
    //Thread safe implementierung, sodass Konstruktor nur 1x aufgerufen wird
    internal sealed class DataOpsSingleton
    {
        private static readonly Logger _logger = NLog.LogManager.GetCurrentClassLogger();

        #region Properties
        public int CountErrors { get; set; }

        public int CountErrorsNotCorrected { get; set; }

        public string LastValidAz { get; set; }

        public List<string> Hochbauliste { get; set; }
        #endregion

        #region Constructor
        static DataOpsSingleton()
        {

        }

        public static DataOpsSingleton Instance
        {
            get
            {
                _logger.Info("Singleton-Instance erzeugt.");
                return Nested.instance;
            }
        }
        #endregion
              

        #region Methods

        #region Korrekturen
        public string CorrectAZ(string az, string name = "", string strasse = "")
        {
            try
            {
                if (String.IsNullOrEmpty(az))
                {
                    return az = "NOT_CORRECTED";
                }

                CheckForSlashAndCorrect(ref az);
                CheckForDoubleSlashAndCorrect(ref az);
                CheckFor7AndCorrect(ref az);
                AddZero(ref az);
                SplitUpSpaces(ref az);
                CheckForHyphenAndCorrect(ref az);

                if (!IsValid(az))
                {
                    az = "NOT_CORRECTED";
                }
            }
            catch (Exception)
            {
                _logger.Error("Korrektur für {0} nicht möglich.", az);
                az = string.Empty;
            }

            return az;
        }
         
        [ExcludeFromCodeCoverage]
        private void CheckForDoubleSlashAndCorrect(ref string az)
        {
            string result = string.Empty;

            if (az.Length > 13 && az.LastIndexOf('/') > 2)
            {
                int idx = az.LastIndexOf('/');
                result = az.Substring(0, idx);
                az = result;
            }
        }

        [ExcludeFromCodeCoverage]
        private void SplitUpSpaces(ref string az)
        {
            if (az.All(char.IsWhiteSpace))
                az = az.Split(' ').FirstOrDefault();
        }

        [ExcludeFromCodeCoverage]
        private void CheckForSlashAndCorrect(ref string az)
        {
            StringBuilder sb = new StringBuilder();

            if (az.Length == 12 && az.ElementAt(2) == '5')
            {
                char[] chars = az.ToCharArray();

                for (int i = 0; i < chars.Length; i++)
                {
                    if (i == 2)
                    {
                        sb.Append('/');
                    }
                    sb.Append(chars[i]);
                    az = sb.ToString();
                }
            }
        }

        [ExcludeFromCodeCoverage]
        private void CheckFor7AndCorrect(ref string az)
        {
            StringBuilder sb = new StringBuilder();

            if (az.Length == 13 && az.ElementAt(2) == '7')
            {
                char[] chars = az.ToCharArray();

                for (int i = 0; i < chars.Length; i++)
                {
                    if (chars[2] == '7')
                    {
                        chars[2] = '/';
                    }
                    sb.Append(chars[i]);
                    az = sb.ToString();
                }
            }
        }

        [ExcludeFromCodeCoverage]
        private void AddZero(ref string az)
        {
            if (IsValid(az))
                return;

            if (az == "" || az.Length < 10 || !az.Any(char.IsDigit))
                return;

            if (az.Contains('-'))
            {
                string splitted = az.Substring(az.LastIndexOf('-'));

                if (!splitted[1].Equals('0'))
                {
                    var temp = splitted.Insert(1, "0");
                    az = az.Replace(az.Substring(az.LastIndexOf('-')), temp);
                }
            }
        }

        [ExcludeFromCodeCoverage]
        private void CheckForHyphenAndCorrect(ref string az)
        {
            if (!az.ElementAt(4).Equals('-'))
                az = az.Insert(4, "-");
            if (!az.ElementAt(6).Equals('-'))
                az = az.Insert(6, "-");
        }
        #endregion

        public bool IsValid(string az)
        {
            string pattern = @"\b(61[/][0-9]{1}[-][0-9]{1}[-][0-9]{6})";
            Regex r = new Regex(pattern,RegexOptions.None, TimeSpan.FromMilliseconds(5));
            bool matched = r.IsMatch(az);
            bool len = az.Length == 13;
            if (matched && len)
                return true;
            else
                return false;
        }

        //Ersten 3 Ziffern von Identifikator verwerfen
        internal string ReshapeIdentifikator(string ident)
        {
            ident = ident.Substring(3);
            if (ident.StartsWith("0"))
                ident = ident.Substring(1);
            return ident;
        }

        //Erstellt aus IT-NRW Zeile ein Objekt
        internal string CreateItnrwRecord(int i, string correctedAZ, string origAZ, List<VorgangItnrw> hochbauListe)
        {
            if (!String.IsNullOrEmpty(correctedAZ) && !correctedAZ.Equals("NOT_CORRECTED"))
            {
                hochbauListe.ElementAt(i).BauscheinNr = correctedAZ;
            }
            else
            {
                // Wenn NOT_CORRECTED, dann das originale AZ aus der IT-NRW-Liste aufnehmen
                hochbauListe.ElementAt(i).BauscheinNr = origAZ;
            }

            string strVorgangHochbau = string.Concat(hochbauListe.ElementAt(i).AGS, ";",
                hochbauListe.ElementAt(i).Identifikator, ";",
                hochbauListe.ElementAt(i).BauscheinNr, ";",
                hochbauListe.ElementAt(i).MonatGenehmigung, ";",
                hochbauListe.ElementAt(i).JahrGenehmigung, ";",
                hochbauListe.ElementAt(i).NameBauherr, ";",
                hochbauListe.ElementAt(i).Strasse, ";",
                hochbauListe.ElementAt(i).PLZ, ";",
                hochbauListe.ElementAt(i).Gemeinde, ";",
                hochbauListe.ElementAt(i).Gemeindeteil, ";");
            return strVorgangHochbau;
        }

        //Erstellt Status zum Bauzustand als Hilfestellung für Bauaufsicht
        internal string CreateStatusBauzustand(VorgangInpro v)
        {
            string bauzustand = string.Empty;

            if (!String.IsNullOrEmpty(v.Schlussabnahme) || !String.IsNullOrEmpty(v.Fertigstellung_Gemeldet))
            {
                bauzustand = "F Fertigstellung erfolgte bis 31.12. des Vorjahres";
                return bauzustand;
            }
            if (!String.IsNullOrEmpty(v.BaugenehmigungAusgeführt))
            {
                bauzustand = "4 Baugenehmigung erloschen";
                return bauzustand;
            }

            return bauzustand;
        }
            
        internal void MergeInproWithITNRW(string resultFile, List<VorgangItnrw> hochbauListe)
        {
            Vorgang vorgang;

            _logger.Info("{0} Datensätze werden verarbeitet.", hochbauListe.Count);
            string strVorgangHochbauInpro = string.Empty;
            string bauzustand = string.Empty;
            string strVorgangHochbau = string.Empty;
            string ident = string.Empty;

            using (StreamWriter sw = new StreamWriter(resultFile))
            {
                //header
                _logger.Info("Schreibe Header-Zeile");
                sw.WriteLine("AGS;Identifikator;Bauscheinnummer;MonatGenehmigung;JahrGenehmigung;NameBauherr;Strasse;PLZ;Gemeinde;Gemeindeteil;Bezirk;Aktenzeichen;AZNummer;VorgangDef;Strasse1ID;Strasse1;Name;zdA;Genehmigung;Statistik_Relevant;Statistik1;Statistik2;Genehmigung_Gemeldet;Fertigstellung_Gemeldet;Baubeginn;Rohbauabnahme;Schlussabnahme;Vorjahr;Bauüberhang_Vorjahr;Aktenstandort;Bauzustand;Bemerkungen;");
               
                for (int row = 0; row < hochbauListe.Count; row++)
                {
                    vorgang = new Vorgang();
                    
                    vorgang.Aktenzeichen = hochbauListe[row].BauscheinNr;
                    string origAZ = vorgang.Aktenzeichen;
                    ident = ReshapeIdentifikator(hochbauListe.ElementAt(row).Identifikator);

                    //Aktenzeichen nicht korrekt
                    if (!IsValid(vorgang.Aktenzeichen))
                    {
                        _logger.Warn("{0} nicht korrekt.", vorgang.Aktenzeichen);
                        this.CountErrors++;
                        //Korrigiere Aktenzeichen (z.B. zweiten Slash entfernen oder 7 statt Slash oder keine Bindestriche etc.
                        vorgang.Aktenzeichen = CorrectAZ(vorgang.Aktenzeichen);
                    }

                    Console.Write("\r{0}/{1}", row + 1, hochbauListe.Count);
                    CreateMergedRecord(vorgang, row, ref strVorgangHochbauInpro, ref strVorgangHochbau, ref bauzustand, hochbauListe, ident, origAZ);
                    
                    //Schreibe IT-NRW und Inpro in Datei
                    sw.WriteLine(string.Concat(strVorgangHochbau, strVorgangHochbauInpro, bauzustand, ";", hochbauListe.ElementAt(row).Bemerkungen, ";"));
                }
            }

            Console.WriteLine();
        }

        [ExcludeFromCodeCoverage]
        private void CreateMergedRecord(Vorgang vorgang, int record, ref string strVorgangHochbauInpro, ref string strVorgangHochbau, ref string bauzustand, List<VorgangItnrw> hochbauListe, string identifikator, string origAZ = "")
        {
            VorgangInpro vorgangHochbauInpro = DatabaseOperations.ConnectAndExecuteHochbau(vorgang);
            if (vorgangHochbauInpro.IdStrasse == null)
            {
                vorgangHochbauInpro = DatabaseOperations.ConnectAndExecuteHochbau(vorgang, Convert.ToInt32(identifikator));
            }
            if (vorgang.Aktenzeichen.Equals("NOT_CORRECTED"))
            {
                CountErrorsNotCorrected++;
                _logger.Error("{0} konnte nicht korrigiert werden.", origAZ);
            }

            strVorgangHochbauInpro = vorgangHochbauInpro.GenerateVorgang(vorgang);
            strVorgangHochbau = CreateItnrwRecord(record, vorgang.Aktenzeichen, origAZ, hochbauListe);

            //Bauzustand anfügen
            bauzustand = CreateStatusBauzustand(vorgangHochbauInpro);
        }
        #endregion

        private static class Nested
        {
            // Tell C# compiler not to mark type as beforefieldinit (https://csharpindepth.com/articles/BeforeFieldInit)
            static Nested()
            {

            }

            internal static readonly DataOpsSingleton instance = new DataOpsSingleton();
        }

        private DataOpsSingleton()
        {           
            _logger.Info("Singleton erzeugt.");
        }
    }       
}
