using System;
using System.Net;
using UnityEngine;
using Ugc.Wangsu.WcsLib.Core;
using Ugc.Wangsu.WcsLib.HTTP;
using Ugc.Wangsu.WcsLib.Utility;

namespace Unity.Ugc.File
{
    public class FileUploader
    {
        /// <summary>
        /// 最大支持20M的数据直接上传
        /// </summary>
        private const int BlockSize = 5 * 4 * 1024 * 1024;

        private static readonly Auth Auth = new (new Mac("", ""));
        private static readonly Config Config = new ("unitycdn.up27.v1.wcsapi.com", "unitycdn.mgr27.v1.wcsapi.com", true);
        
        /// <summary>
        /// 完整上传
        /// </summary>
        /// <param name="data">文件</param>
        /// <param name="contentType">文件类型</param>
        /// <param name="token">鉴权token</param>
        /// <param name="key">保存的文件名</param>
        /// <returns>成功与否</returns>
        private static void UploadSingle(byte[] data, string contentType, string token, string key)
        {
            var su = new SimpleUpload(Auth, Config);
            
            var putExtra = new PutExtra();
            putExtra.MimeType = contentType;
            
            HttpResult sr = su.UploadData(data, token, key, putExtra);

            if ((int)HttpStatusCode.NotAcceptable == sr.Code || (int)HttpStatusCode.OK == sr.Code)
            {
                return;
            }
            
            throw new Exception(String.Format("upload single error {0}", sr.Code));
        }

        /// <summary>
        /// 分片上传
        /// </summary>
        /// <param name="data">文件</param>
        /// <param name="contentType">文件类型</param>
        /// <param name="token">鉴权token</param>
        /// <param name="key">保存的文件名</param>
        /// <returns>成功与否</returns>
        private static void UploadSlice(byte[] data, string contentType, string token, string key)
        {
            var size = data.Length;
            long blockSize = 4 * 1024 * 1024;
            int firstChunkSize = 1024;

            if (firstChunkSize > size)
            {
                firstChunkSize = size;
            }

            if (blockSize > size)
            {
                blockSize = size;
            }
            
            var su = new SliceUpload(Auth, Config);
            HttpResult result = su.MakeBlock(blockSize, 0, data, 0, firstChunkSize, token, key);

            if ((int)HttpStatusCode.NotAcceptable == result.Code)
            {
                // Debug.Log("File Already Exist. Do not need to upload.");
                return;
            }

            if ((int)HttpStatusCode.OK != result.Code)
            {
                throw new Exception(String.Format("make block error {0}", result.Code));
            }

            CtxResponse Resp = JsonUtility.FromJson<CtxResponse>(result.Text);

            long blockCount = (size + blockSize - 1) / blockSize;
            string[] contexts = new string[blockCount];
            contexts[0] = Resp.Ctx;

            // 上传第 1 个 block 剩下的数据
            if (firstChunkSize < blockSize)
            {
                result = su.Bput(contexts[0], firstChunkSize, data, firstChunkSize, (int)(blockSize - firstChunkSize), token, key);
                if ((int)HttpStatusCode.OK != result.Code)
                {
                    throw new Exception(String.Format("b put error {0}", result.Code));
                }
                
                Resp = JsonUtility.FromJson<CtxResponse>(result.Text);
                contexts[0] = Resp.Ctx;
            }

            // 上传后续 block，每次都是一整块上传
            for (int blockIndex = 1; blockIndex < blockCount; ++blockIndex)
            {
                long leftSize = size - blockSize * blockIndex;
                int chunkSize = (int)(leftSize > blockSize ? blockSize : leftSize);
                result = su.MakeBlock(chunkSize, blockIndex, data, (int)(blockSize * blockIndex), chunkSize, token, key);
                if ((int)HttpStatusCode.OK == result.Code)
                {
                    Resp = JsonUtility.FromJson<CtxResponse>(result.Text);
                    contexts[blockIndex] = Resp.Ctx;
                }
                else
                {
                    throw new Exception(String.Format("make block error {0}", result.Code));
                }
            }
            
            // 合成文件，注意与前面打印的 ETag 对比
            // Debug.Log(entryInfo.objectKey);
            var putExtra = new PutExtra();
            putExtra.MimeType = contentType;
            result = su.MakeFile(size, key, contexts, token, putExtra);
            if ((int)HttpStatusCode.OK != result.Code)
            {
                throw new Exception(String.Format("make file error - {0}", result.Code));
            }
        }

        /// <summary>
        /// 上传文件
        /// </summary>
        /// <param name="data">文件内容</param>
        /// <param name="contentType">文件内容</param>
        /// <param name="token">凭证</param>
        /// <param name="key">文件名</param>
        /// <returns>成功与否</returns>
        protected internal static void UploadFile(byte[] data, string contentType, string token, string key)
        {
            // 上传文件较小，整个上传
            if (data.Length <= BlockSize)
            {
                UploadSingle(data, contentType, token, key);
            }

            // 需要分块上传
            UploadSlice(data, contentType, token, key);
        }
    }
}