using System;
using System.IO;

namespace PKHeX.Core
{
    /// <summary>
    /// Tracks information about where the <see cref="SAV"/> originated from, and provides logic for saving to a file.
    /// </summary>
    public sealed class SaveFileMetadata
    {
        private readonly SaveFile SAV;
        public SaveFileMetadata(SaveFile sav) => SAV = sav;

        /// <summary>
        /// Full path where the <see cref="SAV"/> originated from.
        /// </summary>
        public string? FilePath { get; private set; }

        /// <summary>
        /// File Name of the <see cref="SAV"/>.
        /// </summary>
        /// <remarks>This is not always the original file name. We try to strip out the Backup name markings to get the original filename.</remarks>
        public string? FileName { get; private set; }

        /// <summary>
        /// Directory in which the <see cref="SAV"/> was saved in.
        /// </summary>
        public string? FileFolder { get; private set; }

        private byte[] Footer = Array.Empty<byte>(); // .dsv
        private byte[] Header = Array.Empty<byte>(); // .gci

        private string BAKSuffix => $" [{SAV.ShortSummary}].bak";

        /// <summary>
        /// Simple summary of the save file, to help differentiate it from other save files with the same filename.
        /// </summary>
        public string BAKName => FileName + BAKSuffix;

        public bool HasHeader => Header.Length != 0;
        public bool HasFooter => Footer.Length != 0;

        /// <summary>
        /// File Dialog filter to help save the file.
        /// </summary>
        public string Filter => $"{SAV.GetType().Name}|{GetSuggestedExtension()}|All Files|*.*";

        /// <summary>
        /// Writes the input <see cref="data"/> and appends the <see cref="Header"/> and <see cref="Footer"/> if requested.
        /// </summary>
        /// <param name="data">Finalized save file data (with fixed checksums) to be written to a file</param>
        /// <param name="flags">Toggle flags </param>
        /// <returns>Final save file data.</returns>
        public byte[] Finalize(byte[] data, ExportFlags flags)
        {
            if (Footer.Length > 0 && flags.HasFlagFast(ExportFlags.IncludeFooter))
                return ArrayUtil.ConcatAll(data, Footer);
            if (Header.Length > 0 && flags.HasFlagFast(ExportFlags.IncludeHeader))
                return ArrayUtil.ConcatAll(Header, data);
            return data;
        }

        /// <summary>
        /// Sets the details of any trimmed header and footer arrays to a <see cref="SaveFile"/> object.
        /// </summary>
        public void SetExtraInfo(byte[] header, byte[] footer)
        {
            Header = header;
            Footer = footer;
        }

        /// <summary>
        /// Sets the details of a path to a <see cref="SaveFile"/> object.
        /// </summary>
        /// <param name="path">Full Path of the file</param>
        public void SetExtraInfo(string path)
        {
            var sav = SAV;
            if (!sav.State.Exportable || string.IsNullOrWhiteSpace(path)) // Blank save file
            {
                sav.Metadata.SetAsBlank();
                return;
            }

            SetAsLoadedFile(path);
        }

        private void SetAsLoadedFile(string path)
        {
            FilePath = path;
            FileFolder = Path.GetDirectoryName(path);
            FileName = GetFileName(path, BAKSuffix);
        }

        private static string GetFileName(string path, string bak)
        {
            var bakName = Util.CleanFileName(bak);
            var fn = Path.GetFileName(path);
            return fn.EndsWith(bakName) ? fn[..^bakName.Length] : fn;
        }

        private void SetAsBlank()
        {
            FileFolder = FilePath = string.Empty;
            FileName = "Blank Save File";
        }

        /// <summary>
        /// Gets the suggested file extension when writing to a saved file.
        /// </summary>
        public string GetSuggestedExtension()
        {
            var sav = SAV;
            var fn = sav.Metadata.FileName;
            if (fn != null)
                return Path.GetExtension(fn);

            if ((sav.Generation is 4 or 5) && sav.Metadata.HasFooter)
                return ".dsv";
            return sav.Extension;
        }

        /// <summary>
        /// Gets suggested export options for the save file.
        /// </summary>
        /// <param name="ext">Selected export extension</param>
        public ExportFlags GetSuggestedFlags(string? ext = null)
        {
            var flags = ExportFlags.None;
            if (ext == ".dsv")
                flags |= ExportFlags.IncludeFooter;
            if (ext == ".gci" || SAV is IGCSaveFile {MemoryCard: null})
                flags |= ExportFlags.IncludeHeader;
            return flags;
        }
    }
}