using C4IT.Logging;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;

namespace C4IT_IAM_Engine
{
    public static class Helper
    {
        public const int MaxAdGroupNameLength = 64;
        public const int MaxAdGroupLoopDigits = 3;
        private const int MinLeadingRelativePathSegmentLength = 3;
        private const int MinSingleLeadingRelativePathSegmentLength = 2;
        private const int MinLastRelativePathSegmentLength = 12;

        public sealed class BoundedTemplateContext
        {
            public string[] SanitizedSegments { get; set; } = Array.Empty<string>();
            public string FolderName { get; set; } = string.Empty;
            public bool WasShortened { get; set; }
            public string OriginalValue { get; set; } = string.Empty;
            public string FinalValue { get; set; } = string.Empty;
            public string Strategy { get; set; } = string.Empty;
        }

        public static string ReplaceLoopTag(this string str, int loop)
        {
            return Regex.Replace(str, @"(?<loopTag>{{(?<prefix>[^}]*)(?<loop>LOOP)(?<postfix>[^{]*)}})", loop <= 0 ? "" : "${prefix}" + loop + "${postfix}");
        }
        public static string ReplaceTags(this string str, IDictionary<string, string> dict)
        {
            if (str.Equals(string.Empty) || str == null || dict == null || dict.Count == 0)
                return str;
            return dict.Aggregate(str, (current, value) =>
                        current.Replace("{{" + value.Key + "}}", value.Value));
        }
        public static string ApplyTemplatePlaceholders(string templateValue, bool allowRelativePath, string defaultRelativePath, string[] sanitizedSegments, string folderName)
        {
            if (templateValue == null)
                return string.Empty;

            var result = Regex.Replace(templateValue, @"{{\s*NAME\s*}}", folderName ?? string.Empty, RegexOptions.IgnoreCase);

            if (allowRelativePath)
            {
                result = Regex.Replace(result, @"{{\s*RELATIVEPATH(?:\s*\(\s*(\d+)\s*\))?\s*}}", match =>
                {
                    if (sanitizedSegments == null || sanitizedSegments.Length == 0)
                        return string.Empty;

                    if (!match.Groups[1].Success)
                        return defaultRelativePath;

                    if (!int.TryParse(match.Groups[1].Value, out var segmentIndex) || segmentIndex < 0)
                        return defaultRelativePath;

                    var segmentCount = Math.Min(sanitizedSegments.Length, segmentIndex + 1);
                    var skip = sanitizedSegments.Length - segmentCount;
                    return string.Join("_", sanitizedSegments.Skip(skip));
                }, RegexOptions.IgnoreCase);
            }

            return result;
        }
        public static BoundedTemplateContext GetBoundedAdGroupTemplateContext(
            string templateValue,
            bool allowRelativePath,
            string defaultRelativePath,
            string[] sanitizedSegments,
            string folderName,
            IDictionary<string, string> replacementTags,
            int maxLength,
            string logContext)
        {
            var effectiveSegments = (sanitizedSegments ?? Array.Empty<string>()).Where(i => i != null).ToArray();
            var effectiveFolderName = folderName ?? string.Empty;
            var currentRelativePath = GetCurrentRelativePath(effectiveSegments, defaultRelativePath);
            var originalValue = MaterializeTemplateValue(templateValue, allowRelativePath, currentRelativePath, effectiveSegments, effectiveFolderName, replacementTags);
            var measuredValue = MaterializeTemplateValueForLength(templateValue, allowRelativePath, currentRelativePath, effectiveSegments, effectiveFolderName, replacementTags);
            var usesRelativePath = allowRelativePath && Regex.IsMatch(templateValue ?? string.Empty, @"{{\s*RELATIVEPATH", RegexOptions.IgnoreCase);
            var usesName = Regex.IsMatch(templateValue ?? string.Empty, @"{{\s*NAME\s*}}", RegexOptions.IgnoreCase);
            var strategy = string.Empty;

            while (measuredValue.Length > maxLength)
            {
                var changed = false;

                if (usesRelativePath && TryShortenRelativePath(ref effectiveSegments))
                {
                    effectiveFolderName = effectiveSegments.Length > 0 ? effectiveSegments[effectiveSegments.Length - 1] : string.Empty;
                    if (string.IsNullOrWhiteSpace(strategy))
                        strategy = "truncate-relativepath";
                    changed = true;
                }
                else if (usesName && !usesRelativePath && TryShortenName(ref effectiveFolderName))
                {
                    if (string.IsNullOrWhiteSpace(strategy))
                        strategy = "truncate-name";
                    changed = true;
                }

                if (!changed)
                    break;

                currentRelativePath = GetCurrentRelativePath(effectiveSegments, defaultRelativePath);
                originalValue = MaterializeTemplateValue(templateValue, allowRelativePath, currentRelativePath, effectiveSegments, effectiveFolderName, replacementTags);
                measuredValue = MaterializeTemplateValueForLength(templateValue, allowRelativePath, currentRelativePath, effectiveSegments, effectiveFolderName, replacementTags);
            }

            var initialValue = MaterializeTemplateValue(
                templateValue,
                allowRelativePath,
                GetCurrentRelativePath(sanitizedSegments, defaultRelativePath),
                sanitizedSegments,
                folderName,
                replacementTags);
            var result = new BoundedTemplateContext
            {
                SanitizedSegments = effectiveSegments,
                FolderName = effectiveSegments.Length > 0 ? effectiveSegments[effectiveSegments.Length - 1] : effectiveFolderName,
                OriginalValue = initialValue,
                FinalValue = originalValue,
                WasShortened = !string.Equals(initialValue, originalValue, StringComparison.Ordinal),
                Strategy = strategy
            };

            if (result.WasShortened)
            {
                cLogManager.DefaultLogger.LogEntry(
                    LogLevels.Warning,
                    $"AD-Gruppenname gekuerzt ({logContext}): '{result.OriginalValue}' ({GetMeasuredTemplateLength(result.OriginalValue)}) -> '{result.FinalValue}' ({GetMeasuredTemplateLength(result.FinalValue)}), Strategie: {result.Strategy}, Limit: {maxLength}.");
            }

            if (measuredValue.Length > maxLength)
            {
                cLogManager.DefaultLogger.LogEntry(
                    LogLevels.Warning,
                    $"AD-Gruppenname ueberschreitet weiterhin das sichere Limit ({logContext}): '{result.FinalValue}' ({measuredValue.Length}), Limit: {maxLength}.");
            }

            return result;
        }
        public static string SanitizePathSegment(string segment)
        {
            if (string.IsNullOrEmpty(segment))
                return string.Empty;

            return Regex.Replace(segment, @"[\s\-]", "_");
        }
        public static void CreatePathWithWriteAccess(string FilePath)
        {
            try
            {
                var PF = Environment.ExpandEnvironmentVariables(FilePath);
                Directory.CreateDirectory(PF);
            }
            catch { }
        }
        public static string MaskAllButLastAndFirst(this string input, char maskingChar = '*')
        {
            if (input.Length > 3)
            {
                var pattern = @"^(.{1})(.+)(.{1})$";
                var match = Regex.Match(input, pattern);
                var mask = new string(maskingChar, match.Groups[2].Length);
                return $"{match.Groups[1]}{mask}{match.Groups[3]}";
            }
            else
                return new string(maskingChar, input.Length);
        }

        private static string MaterializeTemplateValue(
            string templateValue,
            bool allowRelativePath,
            string defaultRelativePath,
            string[] sanitizedSegments,
            string folderName,
            IDictionary<string, string> replacementTags)
        {
            return ApplyTemplatePlaceholders(templateValue, allowRelativePath, defaultRelativePath, sanitizedSegments, folderName)
                .ReplaceTags(replacementTags)
                .ToUpper();
        }

        private static string MaterializeTemplateValueForLength(
            string templateValue,
            bool allowRelativePath,
            string defaultRelativePath,
            string[] sanitizedSegments,
            string folderName,
            IDictionary<string, string> replacementTags)
        {
            return NormalizeLoopPlaceholderLength(
                MaterializeTemplateValue(templateValue, allowRelativePath, defaultRelativePath, sanitizedSegments, folderName, replacementTags));
        }

        private static string NormalizeLoopPlaceholderLength(string templateValue)
        {
            if (string.IsNullOrWhiteSpace(templateValue))
                return templateValue ?? string.Empty;

            return Regex.Replace(
                templateValue,
                @"{{(?<prefix>[^}]*)(?<loop>LOOP)(?<postfix>[^{]*)}}",
                match => match.Groups["prefix"].Value + new string('9', MaxAdGroupLoopDigits) + match.Groups["postfix"].Value,
                RegexOptions.IgnoreCase);
        }

        private static int GetMeasuredTemplateLength(string templateValue)
        {
            return NormalizeLoopPlaceholderLength(templateValue).Length;
        }

        private static string GetCurrentRelativePath(string[] sanitizedSegments, string fallbackRelativePath)
        {
            if (sanitizedSegments != null && sanitizedSegments.Length > 0)
                return string.Join("_", sanitizedSegments);

            return fallbackRelativePath ?? string.Empty;
        }

        private static int GetLoopReservationLength(string templateValue)
        {
            if (string.IsNullOrWhiteSpace(templateValue))
                return 0;

            var reservation = 0;
            foreach (Match match in Regex.Matches(templateValue, @"{{(?<prefix>[^}]*)(?<loop>LOOP)(?<postfix>[^{]*)}}", RegexOptions.IgnoreCase))
            {
                reservation += match.Groups["prefix"].Value.Length + MaxAdGroupLoopDigits + match.Groups["postfix"].Value.Length;
            }

            return reservation;
        }

        private static bool TryShortenRelativePath(ref string[] segments)
        {
            if (segments == null || segments.Length == 0)
                return false;

            var lastIndex = segments.Length - 1;
            if (segments.Length > 2)
            {
                var candidateIndex = -1;
                var candidateLength = MinLeadingRelativePathSegmentLength;

                for (var i = 0; i < lastIndex; i++)
                {
                    if (string.IsNullOrWhiteSpace(segments[i]) || segments[i].Length <= candidateLength)
                        continue;

                    candidateIndex = i;
                    candidateLength = segments[i].Length;
                }

                if (candidateIndex >= 0)
                {
                    segments[candidateIndex] = segments[candidateIndex].Substring(0, segments[candidateIndex].Length - 1);
                    return true;
                }

                segments = segments.Skip(1).ToArray();
                return true;
            }

            if (segments.Length == 2)
            {
                if (segments[0].Length > MinSingleLeadingRelativePathSegmentLength)
                {
                    segments[0] = segments[0].Substring(0, segments[0].Length - 1);
                    return true;
                }

                if (segments[lastIndex].Length > MinLastRelativePathSegmentLength)
                {
                    segments[lastIndex] = segments[lastIndex].Substring(0, segments[lastIndex].Length - 1);
                    return true;
                }

                if (segments[0].Length > 1)
                {
                    segments[0] = segments[0].Substring(0, segments[0].Length - 1);
                    return true;
                }

                if (segments[lastIndex].Length > 1)
                {
                    segments[lastIndex] = segments[lastIndex].Substring(0, segments[lastIndex].Length - 1);
                    return true;
                }

                return false;
            }

            if (segments[lastIndex].Length > 1)
            {
                segments[lastIndex] = segments[lastIndex].Substring(0, segments[lastIndex].Length - 1);
                return true;
            }

            return false;
        }

        private static bool TryShortenName(ref string folderName)
        {
            if (string.IsNullOrWhiteSpace(folderName) || folderName.Length <= 1)
                return false;

            folderName = folderName.Substring(0, folderName.Length - 1);
            return true;
        }
    }
}
