using Microsoft.Extensions.Logging;
using System.ComponentModel;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Xml;
using System.Xml.Linq;
using System.Xml.Serialization;

namespace File_Folder_Helper.ADO2024.PI1;

#pragma warning disable IDE1006, CS8618

[Serializable()]
[DesignerCategory("code")]
[XmlType(AnonymousType = true)]
[XmlRoot(Namespace = "", IsNullable = false)]
public partial class KeePassFile
{

    private KeePassFileMeta metaField;

    private KeePassFileRoot rootField;

    public KeePassFileMeta Meta
    {
        get => metaField;
        set => metaField = value;
    }

    public KeePassFileRoot Root
    {
        get => rootField;
        set => rootField = value;
    }
}

[Serializable()]
[DesignerCategory("code")]
[XmlType(AnonymousType = true)]
public partial class KeePassFileMeta
{

    private string generatorField;

    private DateTime settingsChangedField;

    private object databaseNameField;

    private DateTime databaseNameChangedField;

    private object databaseDescriptionField;

    private DateTime databaseDescriptionChangedField;

    private object defaultUserNameField;

    private DateTime defaultUserNameChangedField;

    private ushort maintenanceHistoryDaysField;

    private object colorField;

    private DateTime masterKeyChangedField;

    private sbyte masterKeyChangeRecField;

    private sbyte masterKeyChangeForceField;

    private KeePassFileMetaMemoryProtection memoryProtectionField;

    private KeePassFileMetaIcon[] customIconsField;

    private string recycleBinEnabledField;

    private string recycleBinUUIDField;

    private DateTime recycleBinChangedField;

    private string entryTemplatesGroupField;

    private DateTime entryTemplatesGroupChangedField;

    private byte historyMaxItemsField;

    private uint historyMaxSizeField;

    private string lastSelectedGroupField;

    private string lastTopVisibleGroupField;

    private object binariesField;

    private object customDataField;

    public string Generator
    {
        get => generatorField;
        set => generatorField = value;
    }

    public DateTime SettingsChanged
    {
        get => settingsChangedField;
        set => settingsChangedField = value;
    }

    public object DatabaseName
    {
        get => databaseNameField;
        set => databaseNameField = value;
    }

    public DateTime DatabaseNameChanged
    {
        get => databaseNameChangedField;
        set => databaseNameChangedField = value;
    }

    public object DatabaseDescription
    {
        get => databaseDescriptionField;
        set => databaseDescriptionField = value;
    }

    public DateTime DatabaseDescriptionChanged
    {
        get => databaseDescriptionChangedField;
        set => databaseDescriptionChangedField = value;
    }

    public object DefaultUserName
    {
        get => defaultUserNameField;
        set => defaultUserNameField = value;
    }

    public DateTime DefaultUserNameChanged
    {
        get => defaultUserNameChangedField;
        set => defaultUserNameChangedField = value;
    }

    public ushort MaintenanceHistoryDays
    {
        get => maintenanceHistoryDaysField;
        set => maintenanceHistoryDaysField = value;
    }

    public object Color
    {
        get => colorField;
        set => colorField = value;
    }

    public DateTime MasterKeyChanged
    {
        get => masterKeyChangedField;
        set => masterKeyChangedField = value;
    }

    public sbyte MasterKeyChangeRec
    {
        get => masterKeyChangeRecField;
        set => masterKeyChangeRecField = value;
    }

    public sbyte MasterKeyChangeForce
    {
        get => masterKeyChangeForceField;
        set => masterKeyChangeForceField = value;
    }

    public KeePassFileMetaMemoryProtection MemoryProtection
    {
        get => memoryProtectionField;
        set => memoryProtectionField = value;
    }

    [XmlArrayItem("Icon", IsNullable = false)]
    public KeePassFileMetaIcon[] CustomIcons
    {
        get => customIconsField;
        set => customIconsField = value;
    }

    public string RecycleBinEnabled
    {
        get => recycleBinEnabledField;
        set => recycleBinEnabledField = value;
    }

    public string RecycleBinUUID
    {
        get => recycleBinUUIDField;
        set => recycleBinUUIDField = value;
    }

    public DateTime RecycleBinChanged
    {
        get => recycleBinChangedField;
        set => recycleBinChangedField = value;
    }

    public string EntryTemplatesGroup
    {
        get => entryTemplatesGroupField;
        set => entryTemplatesGroupField = value;
    }

    public DateTime EntryTemplatesGroupChanged
    {
        get => entryTemplatesGroupChangedField;
        set => entryTemplatesGroupChangedField = value;
    }

    public byte HistoryMaxItems
    {
        get => historyMaxItemsField;
        set => historyMaxItemsField = value;
    }

    public uint HistoryMaxSize
    {
        get => historyMaxSizeField;
        set => historyMaxSizeField = value;
    }

    public string LastSelectedGroup
    {
        get => lastSelectedGroupField;
        set => lastSelectedGroupField = value;
    }

    public string LastTopVisibleGroup
    {
        get => lastTopVisibleGroupField;
        set => lastTopVisibleGroupField = value;
    }

    public object Binaries
    {
        get => binariesField;
        set => binariesField = value;
    }

    public object CustomData
    {
        get => customDataField;
        set => customDataField = value;
    }
}

[Serializable()]
[DesignerCategory("code")]
[XmlType(AnonymousType = true)]
public partial class KeePassFileMetaMemoryProtection
{

    private string protectTitleField;

    private string protectUserNameField;

    private string protectPasswordField;

    private string protectURLField;

    private string protectNotesField;

    public string ProtectTitle
    {
        get => protectTitleField;
        set => protectTitleField = value;
    }

    public string ProtectUserName
    {
        get => protectUserNameField;
        set => protectUserNameField = value;
    }

    public string ProtectPassword
    {
        get => protectPasswordField;
        set => protectPasswordField = value;
    }

    public string ProtectURL
    {
        get => protectURLField;
        set => protectURLField = value;
    }

    public string ProtectNotes
    {
        get => protectNotesField;
        set => protectNotesField = value;
    }
}

[Serializable()]
[DesignerCategory("code")]
[XmlType(AnonymousType = true)]
public partial class KeePassFileMetaIcon
{

    private string uUIDField;

    private string dataField;

    private string nameField;

    private DateTime lastModificationTimeField;

    private bool lastModificationTimeFieldSpecified;

    public string UUID
    {
        get => uUIDField;
        set => uUIDField = value;
    }

    public string Data
    {
        get => dataField;
        set => dataField = value;
    }

    public string Name
    {
        get => nameField;
        set => nameField = value;
    }

    public DateTime LastModificationTime
    {
        get => lastModificationTimeField;
        set => lastModificationTimeField = value;
    }

    [XmlIgnore()]
    public bool LastModificationTimeSpecified
    {
        get => lastModificationTimeFieldSpecified;
        set => lastModificationTimeFieldSpecified = value;
    }
}

[Serializable()]
[DesignerCategory("code")]
[XmlType(AnonymousType = true)]
public partial class KeePassFileRoot
{

    private KeePassFileGroup[] groupField;

    private KeePassFileGroupEntry[] entryField;

    private KeePassFileRootDeletedObject[] deletedObjectsField;

    [XmlElement("Group")]
    public KeePassFileGroup[] Group
    {
        get => groupField;
        set => groupField = value;
    }

    [XmlElement("Entry")]
    public KeePassFileGroupEntry[] Entry
    {
        get => entryField;
        set => entryField = value;
    }

    [XmlArrayItem("DeletedObject", IsNullable = false)]
    public KeePassFileRootDeletedObject[] DeletedObjects
    {
        get => deletedObjectsField;
        set => deletedObjectsField = value;
    }
}

[Serializable()]
[DesignerCategory("code")]
[XmlType(AnonymousType = true)]
public partial class KeePassFileGroup
{

    private string uUIDField;

    private string nameField;

    private object notesField;

    private byte iconIDField;

    private KeePassFileGroupTimes timesField;

    private string isExpandedField;

    private object defaultAutoTypeSequenceField;

    private string enableAutoTypeField;

    private string enableSearchingField;

    private KeePassFileGroup[] groupField;

    private KeePassFileGroupEntry[] entryField;

    public string UUID
    {
        get => uUIDField;
        set => uUIDField = value;
    }

    public string Name
    {
        get => nameField;
        set => nameField = value;
    }

    public object Notes
    {
        get => notesField;
        set => notesField = value;
    }

    public byte IconID
    {
        get => iconIDField;
        set => iconIDField = value;
    }

    public KeePassFileGroupTimes Times
    {
        get => timesField;
        set => timesField = value;
    }

    public string IsExpanded
    {
        get => isExpandedField;
        set => isExpandedField = value;
    }

    public object DefaultAutoTypeSequence
    {
        get => defaultAutoTypeSequenceField;
        set => defaultAutoTypeSequenceField = value;
    }

    public string EnableAutoType
    {
        get => enableAutoTypeField;
        set => enableAutoTypeField = value;
    }

    public string EnableSearching
    {
        get => enableSearchingField;
        set => enableSearchingField = value;
    }

    [XmlElement("Group")]
    public KeePassFileGroup[] Group
    {
        get => groupField;
        set => groupField = value;
    }

    [XmlElement("Entry")]
    public KeePassFileGroupEntry[] Entry
    {
        get => entryField;
        set => entryField = value;
    }
}

[Serializable()]
[DesignerCategory("code")]
[XmlType(AnonymousType = true)]
public partial class KeePassFileGroupTimes
{

    private DateTime creationTimeField;

    private DateTime lastModificationTimeField;

    private DateTime expiryTimeField;

    private string expiresField;

    public DateTime CreationTime
    {
        get => creationTimeField;
        set => creationTimeField = value;
    }

    public DateTime LastModificationTime
    {
        get => lastModificationTimeField;
        set => lastModificationTimeField = value;
    }

    public DateTime ExpiryTime
    {
        get => expiryTimeField;
        set => expiryTimeField = value;
    }

    public string Expires
    {
        get => expiresField;
        set => expiresField = value;
    }
}

[Serializable()]
[DesignerCategory("code")]
[XmlType(AnonymousType = true)]
public partial class KeePassFileGroupEntry
{

    private string uUIDField;

    private byte iconIDField;

    private object foregroundColorField;

    private object backgroundColorField;

    private object overrideURLField;

    private object tagsField;

    private KeePassFileGroupEntryTimes timesField;

    private KeePassFileGroupEntryString[] stringField;

    private KeePassFileGroupEntryAutoType autoTypeField;

    private KeePassFileGroupEntryEntry[] historyField;

    public string UUID
    {
        get => uUIDField;
        set => uUIDField = value;
    }

    public byte IconID
    {
        get => iconIDField;
        set => iconIDField = value;
    }

    public object ForegroundColor
    {
        get => foregroundColorField;
        set => foregroundColorField = value;
    }

    public object BackgroundColor
    {
        get => backgroundColorField;
        set => backgroundColorField = value;
    }

    public object OverrideURL
    {
        get => overrideURLField;
        set => overrideURLField = value;
    }

    public object Tags
    {
        get => tagsField;
        set => tagsField = value;
    }

    public KeePassFileGroupEntryTimes Times
    {
        get => timesField;
        set => timesField = value;
    }

    [XmlElement("String")]
    public KeePassFileGroupEntryString[] String
    {
        get => stringField;
        set => stringField = value;
    }

    public KeePassFileGroupEntryAutoType AutoType
    {
        get => autoTypeField;
        set => autoTypeField = value;
    }

    [XmlArrayItem("Entry", IsNullable = false)]
    public KeePassFileGroupEntryEntry[] History
    {
        get => historyField;
        set => historyField = value;
    }
}

[Serializable()]
[DesignerCategory("code")]
[XmlType(AnonymousType = true)]
public partial class KeePassFileGroupEntryTimes
{

    private DateTime creationTimeField;

    private DateTime lastModificationTimeField;

    private DateTime expiryTimeField;

    private string expiresField;

    public DateTime CreationTime
    {
        get => creationTimeField;
        set => creationTimeField = value;
    }

    public DateTime LastModificationTime
    {
        get => lastModificationTimeField;
        set => lastModificationTimeField = value;
    }

    public DateTime ExpiryTime
    {
        get => expiryTimeField;
        set => expiryTimeField = value;
    }

    public string Expires
    {
        get => expiresField;
        set => expiresField = value;
    }
}

[Serializable()]
[DesignerCategory("code")]
[XmlType(AnonymousType = true)]
public partial class KeePassFileGroupEntryString
{

    private string keyField;

    private KeePassFileGroupEntryStringValue valueField;

    public string Key
    {
        get => keyField;
        set => keyField = value;
    }

    public KeePassFileGroupEntryStringValue Value
    {
        get => valueField;
        set => valueField = value;
    }
}

[Serializable()]
[DesignerCategory("code")]
[XmlType(AnonymousType = true)]
public partial class KeePassFileGroupEntryStringValue
{

    private string protectInMemoryField;

    private string valueField;

    [XmlAttribute()]
    public string ProtectInMemory
    {
        get => protectInMemoryField;
        set => protectInMemoryField = value;
    }

    [XmlText()]
    public string Value
    {
        get => valueField;
        set => valueField = value;
    }
}

[Serializable()]
[DesignerCategory("code")]
[XmlType(AnonymousType = true)]
public partial class KeePassFileGroupEntryAutoType
{

    private string enabledField;

    private byte dataTransferObfuscationField;

    public string Enabled
    {
        get => enabledField;
        set => enabledField = value;
    }

    public byte DataTransferObfuscation
    {
        get => dataTransferObfuscationField;
        set => dataTransferObfuscationField = value;
    }
}

[Serializable()]
[DesignerCategory("code")]
[XmlType(AnonymousType = true)]
public partial class KeePassFileGroupEntryEntry
{

    private string uUIDField;

    private byte iconIDField;

    private object foregroundColorField;

    private object backgroundColorField;

    private object overrideURLField;

    private object tagsField;

    private KeePassFileGroupEntryEntryTimes timesField;

    private KeePassFileGroupEntryEntryString[] stringField;

    private KeePassFileGroupEntryEntryAutoType autoTypeField;

    public string UUID
    {
        get => uUIDField;
        set => uUIDField = value;
    }

    public byte IconID
    {
        get => iconIDField;
        set => iconIDField = value;
    }

    public object ForegroundColor
    {
        get => foregroundColorField;
        set => foregroundColorField = value;
    }

    public object BackgroundColor
    {
        get => backgroundColorField;
        set => backgroundColorField = value;
    }

    public object OverrideURL
    {
        get => overrideURLField;
        set => overrideURLField = value;
    }

    public object Tags
    {
        get => tagsField;
        set => tagsField = value;
    }

    public KeePassFileGroupEntryEntryTimes Times
    {
        get => timesField;
        set => timesField = value;
    }

    [XmlElement("String")]
    public KeePassFileGroupEntryEntryString[] String
    {
        get => stringField;
        set => stringField = value;
    }

    public KeePassFileGroupEntryEntryAutoType AutoType
    {
        get => autoTypeField;
        set => autoTypeField = value;
    }
}

[Serializable()]
[DesignerCategory("code")]
[XmlType(AnonymousType = true)]
public partial class KeePassFileGroupEntryEntryTimes
{

    private DateTime creationTimeField;

    private DateTime lastModificationTimeField;

    private DateTime expiryTimeField;

    private string expiresField;

    public DateTime CreationTime
    {
        get => creationTimeField;
        set => creationTimeField = value;
    }

    public DateTime LastModificationTime
    {
        get => lastModificationTimeField;
        set => lastModificationTimeField = value;
    }

    public DateTime ExpiryTime
    {
        get => expiryTimeField;
        set => expiryTimeField = value;
    }

    public string Expires
    {
        get => expiresField;
        set => expiresField = value;
    }
}

[Serializable()]
[DesignerCategory("code")]
[XmlType(AnonymousType = true)]
public partial class KeePassFileGroupEntryEntryString
{

    private string keyField;

    private KeePassFileGroupEntryEntryStringValue valueField;

    public string Key
    {
        get => keyField;
        set => keyField = value;
    }

    public KeePassFileGroupEntryEntryStringValue Value
    {
        get => valueField;
        set => valueField = value;
    }
}

[Serializable()]
[DesignerCategory("code")]
[XmlType(AnonymousType = true)]
public partial class KeePassFileGroupEntryEntryStringValue
{

    private string protectInMemoryField;

    private string valueField;

    [XmlAttribute()]
    public string ProtectInMemory
    {
        get => protectInMemoryField;
        set => protectInMemoryField = value;
    }

    [XmlText()]
    public string Value
    {
        get => valueField;
        set => valueField = value;
    }
}

[Serializable()]
[DesignerCategory("code")]
[XmlType(AnonymousType = true)]
public partial class KeePassFileGroupEntryEntryAutoType
{

    private string enabledField;

    private byte dataTransferObfuscationField;

    public string Enabled
    {
        get => enabledField;
        set => enabledField = value;
    }

    public byte DataTransferObfuscation
    {
        get => dataTransferObfuscationField;
        set => dataTransferObfuscationField = value;
    }
}

[Serializable()]
[DesignerCategory("code")]
[XmlType(AnonymousType = true)]
public partial class KeePassFileRootDeletedObject
{

    private string uUIDField;

    private DateTime deletionTimeField;

    public string UUID
    {
        get => uUIDField;
        set => uUIDField = value;
    }

    public DateTime DeletionTime
    {
        get => deletionTimeField;
        set => deletionTimeField = value;
    }
}

#pragma warning restore IDE1006, CS8618

internal static partial class Helper20240105
{

    // Folders with these names will be put in the root instead.
    private static readonly string[] _BlacklistedFolders =
    [
        "KeePassHttp Passwords",
        "KeePassXC-Browser Passwords"
    ];

    private static readonly string[] _BlacklistedFields = [
        "KeePassXC-Browser Settings",
        "KeePassHttp Settings"
    ];

    private record Field(
        [property: JsonPropertyName("name")] string? Name,
        [property: JsonPropertyName("value")] string? Value,
        [property: JsonPropertyName("type")] int? Type
    );

    [JsonSourceGenerationOptions(WriteIndented = true)]
    [JsonSerializable(typeof(Field))]
    private partial class FieldSourceGenerationContext : JsonSerializerContext
    {
    }

    private record Folder(
        [property: JsonPropertyName("id")] Guid? Id,
        [property: JsonPropertyName("name")] string? Name
    );

    [JsonSourceGenerationOptions(WriteIndented = true)]
    [JsonSerializable(typeof(Folder))]
    private partial class FolderSourceGenerationContext : JsonSerializerContext
    {
    }

    private record Item(
        [property: JsonPropertyName("revisionDate")] DateTime? RevisionDate,
        [property: JsonPropertyName("creationDate")] DateTime? CreationDate,
        [property: JsonPropertyName("folderId")] Guid? FolderId,
        [property: JsonPropertyName("folderName")] string? FolderName,
        [property: JsonPropertyName("type")] int Type,
        [property: JsonPropertyName("name")] string? Name,
        [property: JsonPropertyName("notes")] string? Notes,
        [property: JsonPropertyName("fields")] IReadOnlyList<Field>? Fields,
        [property: JsonPropertyName("login")] Login Login
    );

    [JsonSourceGenerationOptions(WriteIndented = true)]
    [JsonSerializable(typeof(Item))]
    private partial class ItemSourceGenerationContext : JsonSerializerContext
    {
    }

    private record Login(
        [property: JsonPropertyName("uris")] IReadOnlyList<Uri> Uris,
        [property: JsonPropertyName("username")] string? Username,
        [property: JsonPropertyName("password")] string? Password
    );

    [JsonSourceGenerationOptions(WriteIndented = true)]
    [JsonSerializable(typeof(Login))]
    private partial class LoginSourceGenerationContext : JsonSerializerContext
    {
    }

    private record Root(
        [property: JsonPropertyName("folders")] IReadOnlyList<Folder> Folders,
        [property: JsonPropertyName("items")] IReadOnlyList<Item> Items
    );

    [JsonSourceGenerationOptions(WriteIndented = true)]
    [JsonSerializable(typeof(Root))]
    private partial class RootSourceGenerationContext : JsonSerializerContext
    {
    }

    private record Uri(
        [property: JsonPropertyName("uri")] string? Value,
        [property: JsonPropertyName("host")] string? Host,
        [property: JsonPropertyName("port")] int? Port
    );

    [JsonSourceGenerationOptions(WriteIndented = true)]
    [JsonSerializable(typeof(Uri))]
    private partial class UriSourceGenerationContext : JsonSerializerContext
    {
    }

    private static int GetDeterministicHashCode(byte[] value)
    {
        int result;
        unchecked
        {
            int hash1 = (5381 << 16) + 5381;
            int hash2 = hash1;
            for (int i = 0; i < value.Length; i += 2)
            {
                hash1 = ((hash1 << 5) + hash1) ^ value[i];
                if (i == value.Length - 1)
                    break;
                hash2 = ((hash2 << 5) + hash2) ^ value[i + 1];
            }
            result = hash1 + (hash2 * 1566083941);
        }
        return result;
    }

    private static string? ConvertPassword(string? value) =>
        string.IsNullOrEmpty(value) ? "-" : GetDeterministicHashCode(System.Text.Encoding.ASCII.GetBytes(value)).ToString();

    private static Item? GetEntry(Guid folderId, string folderName, KeePassFileGroupEntry keePassFileGroupEntry)
    {
        Item? result;
        string? name = keePassFileGroupEntry.String.Where(x => x.Key == "Title").Select(x => x.Value.Value).FirstOrDefault();
        if (_BlacklistedFields.Contains(name))
            result = null;
        else
        {
            int? port;
            string? host;
            string? uri = null;
            string? notes = null;
            string? password = null;
            string? username = null;
            DateTime creationTime = keePassFileGroupEntry.Times.CreationTime;
            DateTime revisionDate = keePassFileGroupEntry.Times.LastModificationTime;
            List<KeePassFileGroupEntryString> keePassFileGroupEntryStrings = [];
            foreach (KeePassFileGroupEntryString keePassFileGroupEntryString in keePassFileGroupEntry.String)
            {
                if (keePassFileGroupEntryString.Key == "Title")
                    continue;
                if (_BlacklistedFields.Contains(keePassFileGroupEntryString.Key))
                    continue;
                if (keePassFileGroupEntryString.Key == "URL")
                    uri = keePassFileGroupEntryString.Value.Value;
                else if (keePassFileGroupEntryString.Key == "Notes")
                    notes = keePassFileGroupEntryString.Value.Value;
                else if (keePassFileGroupEntryString.Key == "UserName")
                    username = keePassFileGroupEntryString.Value.Value;
                else if (keePassFileGroupEntryString.Key == "Password")
                    password = ConvertPassword(keePassFileGroupEntryString.Value.Value);
                else if (keePassFileGroupEntryString.Key == "URL")
                    uri = keePassFileGroupEntryString.Value.Value;
                else
                    keePassFileGroupEntryStrings.Add(keePassFileGroupEntryString);
            }
            if (string.IsNullOrEmpty(uri) || !uri.Contains(':'))
            {
                host = null;
                port = null;
            }
            else
            {
                host = new System.Uri(uri).Host;
                port = new System.Uri(uri).Port;
            }
            List<Field> itemFields = (from l in keePassFileGroupEntryStrings select new Field(l.Key, l.Value.Value, 0)).ToList();
            Login login = new([new(uri, host, port)], username, password);
            result = new(revisionDate,
                         creationTime,
                         folderId,
                         folderName,
                         1,
                         name,
                         string.IsNullOrEmpty(notes) ? null : notes,
                         itemFields.Count > 0 ? itemFields : null,
                         login);
        }
        return result;
    }

    private static List<Item> GetItems(Guid folderId, string folderName, KeePassFileGroup keePassFileGroup)
    {
        List<Item> results = [];
        if (keePassFileGroup.Entry is not null)
        {
            Item? item;
            foreach (KeePassFileGroupEntry keePassFileGroupEntry in keePassFileGroup.Entry)
            {
                item = GetEntry(folderId, folderName, keePassFileGroupEntry);
                if (item is null)
                    continue;
                results.Add(item);
            }
        }
        if (keePassFileGroup.Group is not null)
        {
            foreach (KeePassFileGroup keePassFileGroupInner in keePassFileGroup.Group)
            {
                folderId = Guid.NewGuid();
                results.AddRange(GetItems(folderId, $"{folderName}/{keePassFileGroupInner.Name}", keePassFileGroupInner));
            }
        }
        return results;
    }

    private static MemoryStream ToStream(string @this)
    {
        MemoryStream memoryStream = new();
        StreamWriter streamWriter = new(memoryStream);
        streamWriter.Write(@this);
        streamWriter.Flush();
        memoryStream.Position = 0;
        return memoryStream;
    }

    private static KeePassFile? ParseXML(string value, bool throwExceptions)
    {
        KeePassFile? result;
        try
        {
            Type type = typeof(KeePassFile);
            Stream stream = ToStream(value.Trim());
            XmlReader xmlReader = XmlReader.Create(stream, new XmlReaderSettings() { ConformanceLevel = ConformanceLevel.Document });
#pragma warning disable IL2026
            XmlSerializer xmlSerializer = new(type, type.GetNestedTypes());
            result = xmlSerializer.Deserialize(xmlReader) as KeePassFile;
#pragma warning restore IL2026
            stream.Dispose();
        }
        catch (Exception)
        {
            if (throwExceptions)
                throw;
            result = null;
        }
        return result;
    }

    private static List<Item> GetItems(KeePassFileRoot keePassFileRoot)
    {
        List<Item> results = [];
        Guid folderId = Guid.NewGuid();
        string folderName = string.Empty;
        if (keePassFileRoot.Entry is not null)
        {
            Item? item;
            foreach (KeePassFileGroupEntry keePassFileGroupEntry in keePassFileRoot.Entry)
            {
                item = GetEntry(folderId, folderName, keePassFileGroupEntry);
                if (item is null)
                    continue;
                results.Add(item);
            }
        }
        if (keePassFileRoot.Group is not null)
        {
            foreach (KeePassFileGroup keePassFileGroup in keePassFileRoot.Group)
            {
                folderId = Guid.NewGuid();
                folderName = keePassFileGroup.Name;
                results.AddRange(GetItems(folderId, folderName, keePassFileGroup));
            }
        }
        return results;
    }

    private static List<Folder> GetFolders(List<Item> items)
    {
        List<Folder> results = [];
        Folder folder;
        List<string> distinct = [];
        foreach (Item item in items)
        {
            if (item.FolderName is null)
                throw new NullReferenceException(nameof(item.FolderName));
            if (distinct.Contains(item.FolderName))
                continue;
            distinct.Add(item.FolderName);
            folder = new(item.FolderId, item.FolderName);
            results.Add(folder);
        }
        return results;
    }

    private static List<Item> Filter(string xmlFile, ILogger<Worker> logger, List<Item> items)
    {
        List<Item> results = [];
        int? port;
        string key;
        Item result;
        Login login;
        string? uri;
        string? host;
        string? name;
        Guid? folderId;
        string? username;
        List<Item>? check;
        string? folderName;
        DateTime? creationTime;
        string joinedHostPorts;
        DateTime? revisionDate;
        List<string> notes = [];
        string? password = null;
        string?[] checkPasswords;
        Dictionary<string, List<Item>> keyValuePairs = [];
        foreach (Item item in items)
        {
            joinedHostPorts = string.Join('-', item.Login.Uris.Select(l => $"{l.Host}:{l.Port}"));
            key = string.Concat(item.Login.Username, '-', string.IsNullOrEmpty(joinedHostPorts) ? Guid.NewGuid().ToString() : joinedHostPorts);
            if (!keyValuePairs.TryGetValue(key, out check))
            {
                keyValuePairs.Add(key, []);
                if (!keyValuePairs.TryGetValue(key, out check))
                    throw new NotSupportedException();
            }
            check.Add(item);
        }
        foreach (KeyValuePair<string, List<Item>> keyValuePair in keyValuePairs)
        {
            if (keyValuePair.Value.Count == 1)
                results.AddRange(keyValuePair.Value);
            else
            {
                checkPasswords = keyValuePair.Value.Select(l => l.Login.Password).Distinct().ToArray();
                if (checkPasswords.Length == 1)
                {
                    results.Add(keyValuePair.Value[0]);
                    logger.LogInformation("Filtered XML duplicate Password [{keyValuePair.Key}] <{xmlFile}>.", keyValuePair.Key, xmlFile);
                }
                else
                {
                    uri = null;
                    host = null;
                    name = null;
                    port = null;
                    notes.Clear();
                    folderId = null;
                    username = null;
                    folderName = null;
                    creationTime = null;
                    revisionDate = null;
                    notes.Add("Unset Password");
                    logger.LogInformation("Filtered XML Unset Password [{keyValuePair.Key}] <{xmlFile}>.", keyValuePair.Key, xmlFile);
                    foreach (Item item in from l in keyValuePair.Value orderby l.RevisionDate, l.Login.Password?.Length descending select l)
                    {
                        if (item.Login.Uris.Count == 1)
                        {
                            uri = item.Login.Uris[0].Value;
                            host = item.Login.Uris[0].Host;
                            port = item.Login.Uris[0].Port;
                        }
                        name = item.Name;
                        folderId = item.FolderId;
                        folderName = item.FolderName;
                        username = item.Login.Username;
                        creationTime = item.CreationDate;
                        revisionDate = item.RevisionDate;
                        notes.Add($"{item.Login.Password} on {item.RevisionDate}");
                    }
                    login = new([new(uri, host, port)], username, password);
                    result = new(revisionDate,
                                 creationTime,
                                 folderId,
                                 folderName,
                                 1,
                                 name,
                                 string.Join(Environment.NewLine, notes),
                                 null,
                                 login);
                    results.Add(result);
                }
            }
        }
        if (results.Count != items.Count)
            logger.LogInformation("Filtered XML {results.Count} != {items.Count} <{xmlFile}>.", results.Count, items.Count, xmlFile);
        results = (from l in results orderby l.Login.Uris[0].Host, l.Login.Username select l).ToList();
        return results;
    }

    private static void SaveTabSeparatedValueFiles(List<Item> items, string xmlFile)
    {
        List<string> lines = [];
        foreach (Item item in items)
            lines.Add($"{item.Login.Uris[0].Host}\t{item.Login.Uris[0].Port}\t{item.Login.Username}\t{item.Login.Password}");
        File.WriteAllLines(Path.ChangeExtension(xmlFile, ".tvs"), lines);
    }

    internal static void ConvertKeePassExport(ILogger<Worker> logger, List<string> args)
    {
        Root root;
        string xml;
        string json;
        List<Item> items;
        List<Folder> folders;
        KeePassFile? keePassFile;
        List<Item> filteredItems;
        string newExtension = args[3];
        string[] xmlFiles = Directory.GetFiles(args[0], args[2]);
        foreach (string xmlFile in xmlFiles)
        {
            xml = XDocument.Load(xmlFile).ToString();
            keePassFile = ParseXML(xml, throwExceptions: true);
            if (keePassFile is null)
                throw new NullReferenceException(nameof(keePassFile));
            logger.LogInformation("Loaded XML <{xmlFile}>.", xmlFile);
            items = GetItems(keePassFile.Root);
            folders = GetFolders(items);
            filteredItems = Filter(xmlFile, logger, items);
            logger.LogInformation("Save generic output file...");
            SaveTabSeparatedValueFiles(filteredItems, xmlFile);
            root = new(folders, filteredItems);
            logger.LogInformation("Serializing output file...");
            json = JsonSerializer.Serialize(root, RootSourceGenerationContext.Default.Root);
            logger.LogInformation("Writing output file...");
            File.WriteAllText(Path.ChangeExtension(xmlFile, newExtension), json);
        }
        logger.LogInformation("Done!");
    }

}