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


//Singleton Klasse draus machen
namespace Hochbaustatistik.Utilities
{
    public static class DataOperations
    {
        private static readonly Logger _logger = NLog.LogManager.GetCurrentClassLogger();

        public static int countErrors;
        public static int countErrorsCorrected;

        private static string lastValidAz;

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

        //Korrigiert Bauscheinnummer anhand Ersetzungsmethoden
        public static string CorrectAZ(string az, string name = "", string strasse = "")
        {
            try
            {
                CheckForSlashAndCorrect(ref az);
                CheckForDoubleSlashAndCorrect(ref az);
                CheckFor7AndCorrect(ref az);
                AddZero(ref az);
                SplitUpSpaces(ref az);
                CheckForHyphenAndCorrect(ref az);

                if (!string.IsNullOrEmpty(name) && !string.IsNullOrEmpty(strasse))
                {
                    ApplyLastAzToNewAz(ref az, name, strasse);
                }


                if (IsValid(az))
                {
                    lastValidAz = az;
                    return az;
                }

                else
                    //wenn nicht korrigiert werden konnte...
                    return "NOT_CORRECTED";
            }
            catch (Exception)
            {
                _logger.Error("Korrektur für {0} nicht möglich.", az);
                return string.Empty;
            }

        }

        #region Ersetzungen
        [ExcludeFromCodeCoverage]
        private static 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]
        public static void ApplyLastAzToNewAz(ref string az, string name, string strasse)
        {

        }


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

        [ExcludeFromCodeCoverage]
        private static 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 static 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 static 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 static void CheckForHyphenAndCorrect(ref string az)
        {
            if (!az.ElementAt(4).Equals('-'))
                az = az.Insert(4, "-");
            if (!az.ElementAt(6).Equals('-'))
                az = az.Insert(6, "-");
        }
        #endregion

        //TODO: Redundanzen und Komplexität abbauen
        [ExcludeFromCodeCoverage]
        internal static void MergeInproWithITNRW(string resultFile, List<VorgangItnrw> hochbauListe)
        {
            Vorgang v;

            //FileOperations.ReadCSVGenHochbauVorgang(csvPath); //legt Hochbauliste an
            using (StreamWriter sw = new StreamWriter(resultFile))
            {
                _logger.Info("{0} Datensätze werden verarbeitet.");

                for (int i = 0; i < hochbauListe.Count; i++)
                {
                    v = new Vorgang();

                    //header
                    if (i == 0)
                        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;");

                    v.Aktenzeichen = hochbauListe[i].BauscheinNr;

                    if (DataOperations.IsValid(v.Aktenzeichen))
                    {
                        VorgangInpro vorgangHochbauInpro = DatabaseOperations.ConnectAndExecuteHochbau(v);

                        if (vorgangHochbauInpro.IdStrasse == null)
                        {
                            vorgangHochbauInpro = DatabaseOperations.ConnectAndExecuteHochbau(v, Convert.ToInt32(ReshapeIdentifikator(hochbauListe.ElementAt(i).Identifikator)));
                        }

                        string strVorgangHochbauInpro = vorgangHochbauInpro.GenerateVorgang(v);
                        string strVorgangHochbau = DataOperations.CreateItnrwRecord(i, v.Aktenzeichen, string.Empty, hochbauListe);

                        //Bauzustand anfügen
                        string bauzustand = CreateStatusBauzustand(vorgangHochbauInpro);

                        sw.WriteLine(string.Concat(strVorgangHochbau, strVorgangHochbauInpro, bauzustand, ";", hochbauListe.ElementAt(i).Bemerkungen, ";"));
                        _logger.Info("{0} Datensatz wird verarbeitet.", i);
                        Console.Write("\r{0}/{1}", i + 1, hochbauListe.Count);
                    }
                    else
                    {
                        countErrors++;
                        string origAZ = v.Aktenzeichen;
                        v.Aktenzeichen = DataOperations.CorrectAZ(v.Aktenzeichen);

                        //korrigierte Aktenzeichen
                        if (!v.Aktenzeichen.Equals("NOT_CORRECTED"))
                        {
                            VorgangInpro vorgangHochbauInpro = DatabaseOperations.ConnectAndExecuteHochbau(v);

                            //Wenn Strasse null, dann anhand Identifikator suchen
                            if (vorgangHochbauInpro.IdStrasse == null)
                            {
                                vorgangHochbauInpro = DatabaseOperations.ConnectAndExecuteHochbau(v, Convert.ToInt32(ReshapeIdentifikator(hochbauListe.ElementAt(i).Identifikator)));
                            }

                            string strVorgangHochbauInpro = vorgangHochbauInpro.GenerateVorgang(v);

                            //Bauzustand anfügen
                            string bauzustand = CreateStatusBauzustand(vorgangHochbauInpro);

                            countErrorsCorrected++;
                            _logger.Error("{0} Datensatz fehlerhaft, aber korrigiert {1}", i, origAZ);
                            string strVorgangHochbau = CreateItnrwRecord(i, v.Aktenzeichen, string.Empty, hochbauListe);
                            sw.WriteLine(string.Concat(strVorgangHochbau, strVorgangHochbauInpro, bauzustand, ";", hochbauListe.ElementAt(i).Bemerkungen, ";"));
                        }
                        //nicht korrigierte Aktenzeichen, werden anhand "Identifikator" gesucht oder Daten anhand vorigen AZ übernommen
                        else
                        {
                            string ident = ReshapeIdentifikator(hochbauListe.ElementAt(i).Identifikator);

                            VorgangInpro vorgangHochbauInpro = DatabaseOperations.ConnectAndExecuteHochbau(v, Convert.ToInt32(ident));
                            string strVorgangHochbauInpro = vorgangHochbauInpro.GenerateVorgang(v);

                            string strVorgangHochbau = CreateItnrwRecord(i, v.Aktenzeichen, origAZ, hochbauListe);

                            //Bauzustand anfügen
                            string bauzustand = CreateStatusBauzustand(vorgangHochbauInpro);

                            _logger.Error("{0} Datensatz fehlerhaft. {1}", i, origAZ);
                            sw.WriteLine(string.Concat(strVorgangHochbau, strVorgangHochbauInpro, bauzustand, ";", hochbauListe.ElementAt(i).Bemerkungen, ";"));
                            if (DataOperations.IsValid(v.Aktenzeichen))
                            {
                                _logger.Info("{0} Datensatz korrigiert anhand Identifikator: {1}, originales AZ: {2}", i, ident, origAZ);
                                countErrorsCorrected++;
                            }
                            //Versuche anhand letztem AZ, letzem Namen und letzter Strasse zu vergleichen
                            else
                            {
                                LastData ld = new LastData() { az = v.Aktenzeichen, name = hochbauListe[i - 1].NameBauherr, strasse = hochbauListe[i - 1].Strasse };

                                ApplyLastAzToNewAz(ref v.Aktenzeichen, hochbauListe[i].NameBauherr, hochbauListe[i].Strasse);
                            }

                        }
                    }
                }
            }

            Console.WriteLine();
        }

        //Trenne Präfix "500" ab
        [ExcludeFromCodeCoverage]
        internal static string ReshapeIdentifikator(string ident)
        {
            ident = ident.Substring(3);
            if (ident.StartsWith("0"))
                ident = ident.Substring(1);
            return ident;
        }

        //Erstelle IT.NRW Hochbau-Objekt
        [ExcludeFromCodeCoverage]
        internal static 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;
        }

        [ExcludeFromCodeCoverage]
        internal static 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;
        }
    }

    public struct LastData
    {
        public string az;
        public string name;
        public string strasse;
    }
}

