using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using Newtonsoft.Json;

namespace FasdCockpitCommunicationDemo
{
    public class DemoTicketJournalEntry
    {
        public string Header { get; set; }
        public string Description { get; set; }
        public string DescriptionHtml { get; set; }
        public bool IsVisibleForUser { get; set; }
        public DateTime CreationDate { get; set; }
    }

    public class DemoTicketDetail
    {
        public string AffectedUser { get; set; }
        public string Asset { get; set; }
        public string Category { get; set; }
        public string Description { get; set; }
        public string DescriptionHtml { get; set; }
        public int? Priority { get; set; }
        public string Solution { get; set; }
        public string SolutionHtml { get; set; }
        public List<DemoTicketJournalEntry> Journal { get; set; } = new List<DemoTicketJournalEntry>();
    }

    public class DemoTicketTemplate
    {
        public string TileKey { get; set; } = "TicketsNew";
        public bool UseRoleScope { get; set; }
        public Guid? UserId { get; set; }
        public string ActivityType { get; set; }
        public string DisplayNamePrefix { get; set; } = "TCK";
        public string Summary { get; set; }
        public string StatusId { get; set; } = "New";
        public string UserDisplayName { get; set; }
        public string UserAccount { get; set; }
        public string UserDomain { get; set; }
        public DemoTicketDetail Detail { get; set; } = new DemoTicketDetail();
    }

    public class DemoTicketRecord
    {
        public Guid TicketId { get; set; }
        public Guid UserId { get; set; }
        public string TileKey { get; set; }
        public bool UseRoleScope { get; set; }
        public string ActivityType { get; set; }
        public string DisplayName { get; set; }
        public string Summary { get; set; }
        public string StatusId { get; set; }
        public string UserDisplayName { get; set; }
        public string UserAccount { get; set; }
        public string UserDomain { get; set; }
        public DateTime CreatedAt { get; set; }
        public DemoTicketDetail Detail { get; set; } = new DemoTicketDetail();
    }

    public class DemoTicketData
    {
        public List<DemoTicketTemplate> Templates { get; set; } = new List<DemoTicketTemplate>();
        public List<DemoTicketRecord> Tickets { get; set; } = new List<DemoTicketRecord>();
    }

    public static class TicketOverviewDataStore
    {
        private const string FileName = "TicketOverviewGeneratedTickets.json";
        private static readonly object SyncRoot = new object();
        private const int DefaultNumberWidth = 5;

        private static readonly Dictionary<string, int> SeedNumbers = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase)
        {
            ["TCK"] = 766,
            ["INC"] = 475
        };

        private static readonly Dictionary<string, int> SeedWidths = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase)
        {
            ["TCK"] = DefaultNumberWidth,
            ["INC"] = DefaultNumberWidth
        };

        private static List<DemoTicketTemplate> _templateCache = new List<DemoTicketTemplate>();
        private static List<DemoTicketRecord> _runtimeTickets = new List<DemoTicketRecord>();
        private static readonly Dictionary<string, int> _nextNumbers = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);
        private static readonly Dictionary<string, int> _numberWidths = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);
        private static bool _isLoaded;

        private static string ResolveDirectory()
        {
            var assembly = typeof(TicketOverviewDataStore).GetTypeInfo().Assembly;
            var baseDirectory = Path.GetDirectoryName(assembly.Location);
            return Path.Combine(baseDirectory ?? string.Empty, "MockupTicketOverview");
        }

        private static string ResolveFilePath()
        {
            var directory = ResolveDirectory();
            return Path.Combine(directory, FileName);
        }

        private static DemoTicketData ReadDataUnsafe()
        {
            try
            {
                var filePath = ResolveFilePath();
                if (!File.Exists(filePath))
                    return new DemoTicketData();

                var json = File.ReadAllText(filePath);
                if (string.IsNullOrWhiteSpace(json))
                    return new DemoTicketData();

                var data = JsonConvert.DeserializeObject<DemoTicketData>(json);
                return data ?? new DemoTicketData();
            }
            catch
            {
                return new DemoTicketData();
            }
        }

        private static DemoTicketJournalEntry CloneJournalEntry(DemoTicketJournalEntry source)
        {
            if (source == null)
                return new DemoTicketJournalEntry();

            return new DemoTicketJournalEntry
            {
                Header = source.Header,
                Description = source.Description,
                DescriptionHtml = source.DescriptionHtml,
                IsVisibleForUser = source.IsVisibleForUser,
                CreationDate = source.CreationDate
            };
        }

        private static DemoTicketDetail CloneDetail(DemoTicketDetail source)
        {
            if (source == null)
                return new DemoTicketDetail();

            return new DemoTicketDetail
            {
                AffectedUser = source.AffectedUser,
                Asset = source.Asset,
                Category = source.Category,
                Description = source.Description,
                DescriptionHtml = source.DescriptionHtml,
                Priority = source.Priority,
                Solution = source.Solution,
                SolutionHtml = source.SolutionHtml,
                Journal = source.Journal?.Select(CloneJournalEntry).ToList() ?? new List<DemoTicketJournalEntry>()
            };
        }

        private static DemoTicketTemplate CloneTemplate(DemoTicketTemplate source)
        {
            if (source == null)
                return new DemoTicketTemplate();

            return new DemoTicketTemplate
            {
                TileKey = source.TileKey,
                UseRoleScope = source.UseRoleScope,
                UserId = source.UserId,
                ActivityType = source.ActivityType,
                DisplayNamePrefix = source.DisplayNamePrefix,
                Summary = source.Summary,
                StatusId = source.StatusId,
                UserDisplayName = source.UserDisplayName,
                UserAccount = source.UserAccount,
                UserDomain = source.UserDomain,
                Detail = CloneDetail(source.Detail)
            };
        }

        private static DemoTicketRecord CloneRecord(DemoTicketRecord source)
        {
            if (source == null)
                return null;

            return new DemoTicketRecord
            {
                TicketId = source.TicketId,
                UserId = source.UserId,
                TileKey = source.TileKey,
                UseRoleScope = source.UseRoleScope,
                ActivityType = source.ActivityType,
                DisplayName = source.DisplayName,
                Summary = source.Summary,
                StatusId = source.StatusId,
                UserDisplayName = source.UserDisplayName,
                UserAccount = source.UserAccount,
                UserDomain = source.UserDomain,
                CreatedAt = source.CreatedAt,
                Detail = CloneDetail(source.Detail)
            };
        }

        private static void EnsureLoaded()
        {
            if (_isLoaded)
                return;

            var data = ReadDataUnsafe();
            _templateCache = data.Templates?.Select(CloneTemplate).ToList() ?? new List<DemoTicketTemplate>();
            _runtimeTickets = data.Tickets?.Select(CloneRecord).Where(r => r != null).ToList() ?? new List<DemoTicketRecord>();
            ResetNumberCounters();
            _isLoaded = true;
        }

        private static void ResetNumberCounters()
        {
            _nextNumbers.Clear();
            _numberWidths.Clear();

            foreach (var kvp in SeedNumbers)
            {
                _nextNumbers[kvp.Key] = kvp.Value;
                _numberWidths[kvp.Key] = SeedWidths.TryGetValue(kvp.Key, out var width)
                    ? Math.Max(width, DefaultNumberWidth)
                    : DefaultNumberWidth;
            }

            foreach (var ticket in _runtimeTickets)
            {
                UpdateNumberCounterFromDisplayName(ticket?.DisplayName);
            }
        }

        private static void UpdateNumberCounterFromDisplayName(string displayName)
        {
            if (string.IsNullOrWhiteSpace(displayName))
                return;

            int i = 0;
            while (i < displayName.Length && char.IsLetter(displayName[i]))
                i++;

            if (i == 0)
                return;

            var prefix = displayName.Substring(0, i).ToUpperInvariant();

            int j = i;
            while (j < displayName.Length && char.IsDigit(displayName[j]))
                j++;

            if (j == i)
                return;

            var numericSegment = displayName.Substring(i, j - i);
            if (!int.TryParse(numericSegment, out var number))
                return;

            var width = Math.Max(DefaultNumberWidth, numericSegment.Length);

            if (!_nextNumbers.TryGetValue(prefix, out var current))
                current = SeedNumbers.TryGetValue(prefix, out var seedValue) ? seedValue : 1;

            if (number >= current)
                _nextNumbers[prefix] = number + 1;

            if (_numberWidths.TryGetValue(prefix, out var currentWidth))
                _numberWidths[prefix] = Math.Max(currentWidth, width);
            else
                _numberWidths[prefix] = width;
        }

        private static string ReserveDisplayName(string prefix)
        {
            prefix = string.IsNullOrWhiteSpace(prefix) ? "TCK" : prefix.ToUpperInvariant();

            if (!_nextNumbers.TryGetValue(prefix, out var next))
                next = SeedNumbers.TryGetValue(prefix, out var seedValue) ? seedValue : 1;

            var width = _numberWidths.TryGetValue(prefix, out var storedWidth) ? Math.Max(storedWidth, DefaultNumberWidth) : DefaultNumberWidth;
            var nextString = next.ToString();
            if (nextString.Length > width)
                width = nextString.Length;

            var displayName = $"{prefix}{nextString.PadLeft(width, '0')}";
            _nextNumbers[prefix] = next + 1;
            _numberWidths[prefix] = width;
            return displayName;
        }
        public static DemoTicketData LoadData()
        {
            lock (SyncRoot)
            {
                EnsureLoaded();
                return new DemoTicketData
                {
                    Templates = _templateCache.Select(CloneTemplate).ToList(),
                    Tickets = _runtimeTickets.Select(CloneRecord).Where(r => r != null).ToList()
                };
            }
        }

        public static List<DemoTicketRecord> LoadTickets()
        {
            lock (SyncRoot)
            {
                EnsureLoaded();
                return _runtimeTickets.Select(CloneRecord).Where(r => r != null).ToList();
            }
        }

        public static List<DemoTicketTemplate> LoadTemplates()
        {
            lock (SyncRoot)
            {
                EnsureLoaded();
                return _templateCache.Select(CloneTemplate).ToList();
            }
        }

        public static bool AppendTicket(DemoTicketRecord record)
        {
            if (record == null)
                return false;

            lock (SyncRoot)
            {
                EnsureLoaded();

                if (_runtimeTickets.Any(t => t.TicketId == record.TicketId))
                    return false;

                var clone = CloneRecord(record);
                if (clone == null)
                    return false;

                _runtimeTickets.Add(clone);
                UpdateNumberCounterFromDisplayName(clone.DisplayName);
                return true;
            }
        }

        public static string GetNextDisplayName(string prefix)
        {
            lock (SyncRoot)
            {
                EnsureLoaded();
                return ReserveDisplayName(prefix);
            }
        }
    }
}
