using System;
using System.Drawing;
using System.IO;
using System.Linq;
using KattekerCreator.Exceptions;
using Semver;
using TsudaKageyu;
using Vestris.ResourceLib;

namespace KattekerCreator.Types
{
    public class AssemblyFileInfo
    {
        private readonly string? _company;
        private readonly string? _copyright;
        private readonly string? _description;
        private readonly string? _productName;

        public AssemblyFileInfo(string programFilePath, string? version, string tempDir)
        {
            if (programFilePath is null) throw new ArgumentNullException(nameof(programFilePath));
            if (tempDir is null) throw new ArgumentNullException(nameof(tempDir));

            FileName = Path.GetFileName(programFilePath) ?? throw new KattekerCreatorException("Program file has to be valid");
            FileNameWithoutExtension = Path.GetFileNameWithoutExtension(FileName) ?? throw new KattekerCreatorException("Program file has to be valid");
            FilePath = Path.GetDirectoryName(programFilePath) ?? string.Empty;
            using var resourceInfo = new ResourceInfo();
            resourceInfo.Load(programFilePath);
            AssemblyIconPath = GetAssemblyIcon(programFilePath, tempDir);
            if (resourceInfo.ResourceTypes.All(x => x.ResourceType != Kernel32.ResourceTypes.RT_VERSION)) return;
            var versionResource = (VersionResource)resourceInfo[Kernel32.ResourceTypes.RT_VERSION][0];
            var stringFileInfo = ((StringFileInfo)versionResource[nameof(StringFileInfo)]).Strings.FirstOrDefault().Value;
            if (stringFileInfo.Strings.ContainsKey("CompanyName"))
            {
                _company = stringFileInfo.Strings["CompanyName"].StringValue.TrimEnd('\0');
            }

            if (stringFileInfo.Strings.ContainsKey("FileDescription"))
            {
                _description = stringFileInfo.Strings["FileDescription"].StringValue.TrimEnd('\0');
            }

            if (stringFileInfo.Strings.ContainsKey("LegalCopyright"))
            {
                _copyright = stringFileInfo.Strings["LegalCopyright"].StringValue.TrimEnd('\0');
            }

            if (stringFileInfo.Strings.ContainsKey("ProductName"))
            {
                _productName = stringFileInfo.Strings["ProductName"].StringValue.TrimEnd('\0');
            }

            if (stringFileInfo.Strings.ContainsKey("ProductVersion"))
            {
                var productVersion = stringFileInfo.Strings["ProductVersion"].StringValue.TrimEnd('\0');
                AssemblyVersion = GetSemanticVersion(version ?? productVersion);
            }
        }

        public string FileNameWithoutExtension { get; }

        public string FileName { get; }

        public string AssemblyIconPath { get; }

        public SemVersion AssemblyVersion { get; } = new(0);

        public string Company => _company ?? string.Empty;

        public string Copyright => _copyright ?? string.Empty;

        public string Description => _description ?? string.Empty;

        public string ProductName => _productName ?? Description;
        public string FilePath { get; }

        private static string GetAssemblyIcon(string programFile, string tempDir)
        {
            var applicationIcon = Path.Combine(tempDir, "application.ico");
            if (string.IsNullOrWhiteSpace(programFile))
            {
                throw new KattekerCreatorException("Program not set.");
            }

            if (!File.Exists(programFile))
            {
                throw new KattekerCreatorException("Program not found.");
            }

            if (File.Exists(applicationIcon))
            {
                File.Delete(applicationIcon);
            }

            using var fileStream = new FileStream(applicationIcon, FileMode.CreateNew);
            try
            {
                var ie = new IconExtractor(programFile);
                using var icon = ie.GetIcon(0);
                icon?.Save(fileStream);
            }
            catch (Exception)
            {
                using var icon = Icon.ExtractAssociatedIcon(programFile);
                icon?.Save(fileStream);
            }

            return applicationIcon;
        }

        private static SemVersion GetSemanticVersion(string productVersion)
        {
            productVersion = productVersion.Replace(',', '.');
            return SemVersion.TryParse(productVersion, out var semanticVersion)
                ? semanticVersion.Change(build: string.Empty)
                : Version.TryParse(productVersion, out var version)
                    ? SemVersion.Parse(version.ToString(3))
                    : new SemVersion(0);
        }
    }
}