using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.Json.Serialization;
using System.Threading.Tasks;
using System.IO.Compression;
using System.Security.Cryptography;
using OpenSaveCloudCore.Core;
using System.Globalization;

namespace OpenSaveCloudCore.Models
{
    public class GameSave
    {

        private long id;
        private string uuid;
        private readonly string name;
        private string folderPath;
        private readonly string description;
        private string hash;
        private string currentHash;
        private readonly string? coverPath;
        private long revision;
        private bool synced;

        public long Id { get { return id; } set { id = value; } }
        public string Uuid { get { return uuid; } }
        public string Name { get { return name; } }
        public string Description { get { return description; } }
        public string FolderPath { get { return folderPath; } set { folderPath = value; } }
        public string Hash { get { return hash; } }
        public string CurrentHash { get { return currentHash; } }
        public string? CoverPath { get { return coverPath; } }   
        public long Revision { get { return revision; } set { revision = value; } }
        public bool Synced { get { return synced; } set { synced = value; } }

        public GameSave(string name, string description, string folderPath, string? coverPath, int revision)
        {
            Guid guid = Guid.NewGuid();
            this.name = name;
            this.uuid = guid.ToString();
            this.description = description;
            this.hash = "";
            this.folderPath = folderPath;
            this.coverPath = coverPath;
            this.revision = revision;
            synced = false;
        }

        [JsonConstructor]
        public GameSave(long id, string uuid, string name, string folderPath, string description, string hash, string currentHash, string? coverPath, long revision, bool synced)
        {
            this.id = id;
            this.uuid = uuid;
            this.name = name;
            this.folderPath = folderPath;
            this.description = description;
            this.hash = hash;
            this.currentHash = currentHash;
            this.coverPath = coverPath;
            this.revision = revision;
            this.synced = synced;
        }

        public void Archive()
        {
            string appdata = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "osc");
            string cachePath = Path.Combine(appdata, "cache");
            string archivePath = Path.Combine(cachePath, uuid + ".bin");
            if (!File.Exists(appdata))
            {
                Directory.CreateDirectory(appdata);
            }
            if (!File.Exists(cachePath))
            {
                Directory.CreateDirectory(cachePath);
            }
            if (File.Exists(archivePath))
            {
                File.Delete(archivePath);
            }
            ZipFile.CreateFromDirectory(folderPath, archivePath, CompressionLevel.SmallestSize, false);
            using (var md5 = MD5.Create())
            {
                using (FileStream stream = File.OpenRead(archivePath))
                {
                    var h = md5.ComputeHash(stream);
                    hash = BitConverter.ToString(h).Replace("-", "").ToLowerInvariant();
                }
            }
        }

        public bool DetectChanges()
        {
            byte[]? hashBytes = HashTool.HashDirectory(FolderPath);
            if (hashBytes == null)
            {
                throw new Exception(message: string.Format(CultureInfo.CurrentCulture, "failed to get hash of directory '{0}'", FolderPath));
            }
            currentHash = BitConverter.ToString(hashBytes).Replace("-", "");
            if (currentHash != hash)
            {
                synced = false;
            }
            return (currentHash != hash);
        }

        public void UpdateHash()
        {
            hash = currentHash;
        }

        public bool PathExist()
        {
            return Directory.Exists(FolderPath);
        }
    }
}
