using Kit.Helpers.Service;

namespace Kit.Helpers.Repository
{
    public abstract class SQLiteFileRepository<TContentKey> : ContentBase<TContentKey>, IContent<TContentKey>
    {
        public override ContentTypes Type => ContentTypes.Sqlite;

        protected readonly ILockService<TContentKey> _lockService;
        protected readonly ISQLiteVersionService _sqLiteVersionService;

        /// <summary> Ключ типа файла для версионности </summary>
        protected readonly string _fileTypeKey;

        /// <summary> </summary>
        /// <param name="lockService"></param>
        /// <param name="sqLiteVersionService"></param>
        /// <param name="fileTypeKey">Ключ типа файла для версионности</param>
        public SQLiteFileRepository(
            ISQLiteVersionService sqLiteVersionService,
            ISQLiteGlobalVarRepository sqLiteGlobalVarRepository,
            string fileTypeKey
        )
        {
            _lockService = new LockService<TContentKey>();
            _sqLiteVersionService = sqLiteVersionService;
            _fileTypeKey = fileTypeKey;
        }

        protected abstract Stream? ReadFile(TContentKey key);
        protected abstract bool ExistsFile(TContentKey key);
        protected abstract void SaveFile(TContentKey key, Stream stream);

        private void CheckFileContentType(IFileContent fileContent)
        {
            Check.IsTrue(fileContent is FileContentSqlite, $"{nameof(fileContent)} is not {nameof(FileContentSqlite)}");
        }
        private FileContentSqlite CreateTempFile() => new FileContentSqlite(new ConnectionString
        {
            Type = ConnectionStringType.SQLite,
            Value = Path.GetTempFileName()
        });

        public IFileContent GetContentData(TContentKey key)
        {
            return _lockService.Lock(key, () =>
            {
                bool isUpdated = false;

                var fileContent = CreateTempFile();
                try
                {
                    using (Stream? srcStream = ReadFile(key))
                    {
                        Check.IsNotNull(srcStream, $"{nameof(srcStream)} is null");
                        using (FileStream fileStream = File.OpenWrite(fileContent.ConnectionString.Value))
                        {
                            srcStream.CopyTo(fileStream);
                        }
                    }

                    isUpdated = _sqLiteVersionService.UpdateByTypes(fileContent);
                    if (isUpdated)
                    {
                        ExecSave(key, fileContent);
                    }

                    return fileContent;
                }
                finally
                {
                    File.Delete(fileContent.ConnectionString.Value);
                }
            });
        }

        public IFileContent GetContentDataDefault()
        {
            FileContentSqlite fileContent = CreateTempFile();

            _sqLiteVersionService.AppendType(fileContent, FileTypeKeys.SQLiteAll);
            _sqLiteVersionService.AppendType(fileContent, _fileTypeKey);
            _sqLiteVersionService.UpdateByTypes(fileContent);

            return fileContent;
        }

        public bool CreateContentData(TContentKey key, IFileContent content, bool skipIfExists = true)
        {
            CheckFileContentType(content);
            return _lockService.Lock(key, () =>
            {
                if (ExistsFile(key))
                {
                    if (skipIfExists) return false;
                    Check.IsNotTrue(true, "Документ уже существует");
                }

                ExecSave(key, (FileContentSqlite)content);

                return true;
            });
        }

        public void UpdateContentData(TContentKey key, IFileContent file)
        {
            Check.IsTrue(file is FileContentSqlite, $"{nameof(file)} is not {nameof(FileContentSqlite)}");
            _lockService.Lock(key, () =>
            {
                ExecSave(key, (FileContentSqlite)file);
            });
        }

        private void ExecSave(TContentKey key, FileContentSqlite fileContent)
        {
            using (Stream stream = File.OpenRead(fileContent.ConnectionString.Value))
            {
                SaveFile(key, stream);
            }
        }

        public int GetRowVersion(IFileContent fileContent)
        {
            CheckFileContentType(fileContent);
            return _sqLiteVersionService.GetRowVersion((FileContentSqlite)fileContent);
        }
    }

    public class SQLiteFileFullPathRepository : SQLiteFileRepository<string>
    {
        public SQLiteFileFullPathRepository(ISQLiteVersionService sqLiteVersionService, ISQLiteGlobalVarRepository sqLiteGlobalVarRepository, string fileTypeKey) : base(sqLiteVersionService, sqLiteGlobalVarRepository, fileTypeKey) { }

        protected override bool ExistsFile(string fullPath)
        {
            return File.Exists(fullPath);
        }

        protected override Stream? ReadFile(string fullPath)
        {
            return File.Open(fullPath, FileMode.OpenOrCreate);
        }

        protected override void SaveFile(string fullPath, Stream stream)
        {
            using (Stream streamFile = File.Open(fullPath, FileMode.Create))
            {
                long posOld = 0;

                if (stream.CanSeek)
                    posOld = stream.Position;

                stream.CopyTo(streamFile);

                if (stream.CanSeek)
                    stream.Position = posOld;
            }
        }

        public void Init(string fullPath)
        {
            if (File.Exists(fullPath))
                GetContentData(fullPath);
            else
                CreateContentData(fullPath, GetContentDataDefault(), true);
        }
    }

    public interface ISQLiteFileFullPathFactory
    {
        SQLiteFileFullPathRepository Create(string fileTypeKey);
    }

    public class SQLiteFileFullPathFactory : ISQLiteFileFullPathFactory
    {

        private readonly ISQLiteVersionService _sqLiteVersionService;
        private readonly ISQLiteGlobalVarRepository _sqLiteGlobalVarRepository;
        public SQLiteFileFullPathFactory(ISQLiteVersionService sqLiteVersionService, ISQLiteGlobalVarRepository sqLiteGlobalVarRepository)
        {
            _sqLiteVersionService = sqLiteVersionService;
            _sqLiteGlobalVarRepository = sqLiteGlobalVarRepository;
        }

        public SQLiteFileFullPathRepository Create(string fileTypeKey) => new SQLiteFileFullPathRepository(_sqLiteVersionService, _sqLiteGlobalVarRepository, fileTypeKey);


    }
}