﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;

using Firebase;
using Firebase.Storage;

namespace Bayat.SaveSystem.Storage
{

    public class FirebaseCloudStorage : StorageBase
    {

        public const long DefaultMaxDownloadSizeBytes = 1 * 1024 * 1024;

        protected bool useBase64 = true;
        protected FirebaseStorage storageInstance;
        protected StorageReference storageReference;

        public FirebaseCloudStorage(string encodingName, bool useBase64, FirebaseStorage storageInstance, StorageReference storageReference) : base(encodingName)
        {
            this.useBase64 = useBase64;
            this.storageInstance = storageInstance ?? FirebaseStorage.DefaultInstance;
            this.storageReference = storageReference ?? this.storageInstance.RootReference;
        }

        public override Task<IStorageStream> GetWriteStream(string identifier)
        {
            return Task.FromResult<IStorageStream>(new FirebaseCloudStorageStream(identifier, new MemoryStream()));
        }

        protected override Task CommitWriteStreamInternal(IStorageStream stream)
        {
            FirebaseCloudStorageStream firebaseStream = (FirebaseCloudStorageStream)stream;
            return this.storageReference.Child(stream.Identifier).PutBytesAsync(firebaseStream.MemoryStream.ToArray());
        }

        public override async Task<IStorageStream> GetReadStream(string identifier)
        {
            byte[] data = await this.storageReference.Child(identifier).GetBytesAsync(DefaultMaxDownloadSizeBytes);
            MemoryStream memoryStream = new MemoryStream(data);
            return new FirebaseCloudStorageStream(identifier, memoryStream);
        }

        protected override Task WriteAllTextInternal(string identifier, string data)
        {
            byte[] bytes = this.TextEncoding.GetBytes(data);
            return this.storageReference.Child(identifier).PutBytesAsync(bytes);
        }

        public override async Task<string> ReadAllText(string identifier)
        {
            byte[] bytes = await this.storageReference.Child(identifier).GetBytesAsync(DefaultMaxDownloadSizeBytes);
            return this.TextEncoding.GetString(bytes);
        }

        protected override Task WriteAllBytesInternal(string identifier, byte[] data)
        {
            return this.storageReference.Child(identifier).PutBytesAsync(data);
        }

        public override Task<byte[]> ReadAllBytes(string identifier)
        {
            return this.storageReference.Child(identifier).GetBytesAsync(DefaultMaxDownloadSizeBytes);
        }

        public override async Task<StorageClearOperationResult> Clear()
        {
            await this.storageReference.DeleteAsync();
            return new StorageClearOperationResult(true, null);
        }

        protected override async Task<StorageDeleteOperationResult> DeleteInternal(string identifier)
        {
            await this.storageReference.Child(identifier).DeleteAsync();
            return new StorageDeleteOperationResult(true);
        }

        public override async Task<bool> Exists(string identifier)
        {
            try
            {
                await this.storageReference.Child(identifier).GetBytesAsync(1);
                return true;
            }
            catch (StorageException)
            {
                return false;
            }
        }

        protected override async Task<StorageMoveOperationResult> MoveInternal(string oldIdentifier, string newIdentifier, bool replace)
        {
            string data = await ReadAllText(oldIdentifier);
            await WriteAllText(newIdentifier, data);
            await Delete(oldIdentifier);
            return new StorageMoveOperationResult(true, newIdentifier);
        }

        protected override async Task<StorageCopyOperationResult> CopyInternal(string fromIdentifier, string toIdentifier, bool replace)
        {
            string data = await ReadAllText(fromIdentifier);
            await WriteAllText(toIdentifier, data);
            return new StorageCopyOperationResult(true, toIdentifier);
        }

        public override async Task<string[]> List(string identifier, StorageListOptions options)
        {
            return (await LoadCatalog()).ToArray();
        }

        public override Task<string[]> ListAll()
        {
            return List(string.Empty, new StorageListOptions());
        }

    }

    /// <summary>
    /// The Firebase Cloud storage stream wrapper.
    /// </summary>
    public class FirebaseCloudStorageStream : StorageStream
    {

        protected readonly MemoryStream memoryStream;

        public virtual MemoryStream MemoryStream
        {
            get
            {
                return this.memoryStream;
            }
        }

        public FirebaseCloudStorageStream(string identifier, MemoryStream memoryStream) : base(identifier, memoryStream)
        {
            this.memoryStream = memoryStream;
        }

    }

}