using DotNext;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Cors;
using Microsoft.AspNetCore.Mvc;

namespace server.Controllers;

/// <summary>
/// 远程更新
/// </summary>
[ApiController]
[Route("api/[controller]")]
public class RemoteUpdateController : ControllerBase
{
    private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();

    private const string BITSTREAM_PATH = "bitstream/RemoteUpdate";

    /// <summary>
    /// 上传远程更新比特流文件
    /// </summary>
    /// <param name="address"> 设备地址 </param>
    /// <param name="goldenBitream">黄金比特流文件</param>
    /// <param name="bitstream1">比特流文件1</param>
    /// <param name="bitstream2">比特流文件2</param>
    /// <param name="bitstream3">比特流文件3</param>
    /// <returns>上传结果</returns>
    [Authorize("Admin")]
    [HttpPost("UploadBitstream")]
    [EnableCors("Users")]
    [ProducesResponseType(typeof(bool), StatusCodes.Status200OK)]
    [ProducesResponseType(typeof(ArgumentException), StatusCodes.Status400BadRequest)]
    public async ValueTask<IResult> UploadBitstreams(
        string address,
        IFormFile? goldenBitream,
        IFormFile? bitstream1,
        IFormFile? bitstream2,
        IFormFile? bitstream3)
    {
        if ((goldenBitream is null || goldenBitream.Length == 0) &&
            (bitstream1 is null || bitstream1.Length == 0) &&
            (bitstream2 is null || bitstream2.Length == 0) &&
            (bitstream3 is null || bitstream3.Length == 0))
            return TypedResults.BadRequest("未选择文件");

        // 生成安全的文件名（避免路径遍历攻击）
        var fileName = Path.GetRandomFileName();
        var uploadsFolder = Path.Combine(Environment.CurrentDirectory, $"{BITSTREAM_PATH}/{address}");

        // 如果存在文件，则删除原文件再上传
        if (Directory.Exists(uploadsFolder))
        {
            Directory.Delete(uploadsFolder, true);
        }
        Directory.CreateDirectory(uploadsFolder);

        for (int bitstreamNum = 0; bitstreamNum < 4; bitstreamNum++)
        {
            IFormFile file;
            if (bitstreamNum == 0 && goldenBitream is not null)
                file = goldenBitream;
            else if (bitstreamNum == 1 && bitstream1 is not null)
                file = bitstream1;
            else if (bitstreamNum == 2 && bitstream2 is not null)
                file = bitstream2;
            else if (bitstreamNum == 3 && bitstream3 is not null)
                file = bitstream3;
            else continue;

            var fileFolder = Path.Combine(uploadsFolder, bitstreamNum.ToString());
            Directory.CreateDirectory(fileFolder);

            var filePath = Path.Combine(fileFolder, fileName);

            using (var stream = new FileStream(filePath, FileMode.Create))
            {
                await file.CopyToAsync(stream);
            }
        }

        logger.Info($"Device {address} Upload Bitstream Successfully");
        return TypedResults.Ok(true);
    }

    private async ValueTask<Result<byte[]>> ProcessBitstream(string filePath)
    {
        using (var fileStream = System.IO.File.Open(filePath, System.IO.FileMode.Open))
        {
            if (fileStream is null || fileStream.Length <= 0)
                return new(new ArgumentException("Wrong bitstream path"));

            // 定义缓冲区大小: 32KB
            byte[] buffer = new byte[32 * 1024];
            byte[] revBuffer = new byte[32 * 1024];
            long totalBytesRead = 0;

            // 使用异步流读取文件
            using (var memoryStream = new MemoryStream())
            {
                int bytesRead;
                while ((bytesRead = await fileStream.ReadAsync(buffer, 0, buffer.Length)) > 0)
                {
                    // 反转 32bits
                    var retBuffer = Common.Number.ReverseBytes(buffer, 4);
                    if (!retBuffer.IsSuccessful)
                        return new(retBuffer.Error);
                    revBuffer = retBuffer.Value;

                    await memoryStream.WriteAsync(revBuffer, 0, bytesRead);
                    totalBytesRead += bytesRead;
                }

                // 将所有数据转换为字节数组（注意：如果文件非常大，可能不适合完全加载到内存）
                var restStreamLen = memoryStream.Length % (4 * 1024);
                if (restStreamLen != 0)
                {
                    var appendLen = ((int)(4 * 1024 - restStreamLen));
                    var bytesAppend = new byte[appendLen];
                    Array.Fill<byte>(bytesAppend, 0xFF);
                    await memoryStream.WriteAsync(bytesAppend, 0, appendLen);
                }

                return new(memoryStream.ToArray());
            }
        }
    }

    /// <summary>
    /// 远程更新单个比特流文件
    /// </summary>
    /// <param name="address"> 设备地址 </param>
    /// <param name="port"> 设备端口 </param>
    /// <param name="bitstreamNum"> 比特流位号 </param>
    [Authorize("Admin")]
    [HttpPost("DownloadBitstream")]
    [EnableCors("Users")]
    [ProducesResponseType(typeof(bool), StatusCodes.Status200OK)]
    [ProducesResponseType(typeof(ArgumentException), StatusCodes.Status400BadRequest)]
    [ProducesResponseType(typeof(Exception), StatusCodes.Status500InternalServerError)]
    public async ValueTask<IResult> UpdateBitstream(string address, int port, int bitstreamNum)
    {
        // 检查文件
        var fileDir = Path.Combine(Environment.CurrentDirectory, $"{BITSTREAM_PATH}/{address}/{bitstreamNum}");
        if (!Directory.Exists(fileDir))
            return TypedResults.BadRequest("Empty bitstream, Please upload it first");

        try
        {
            // 读取文件
            var filePath = Directory.GetFiles(fileDir)[0];

            var fileBytes = await ProcessBitstream(filePath);
            if (!fileBytes.IsSuccessful) return TypedResults.InternalServerError(fileBytes.Error);

            // 下载比特流
            var remoteUpdater = new Peripherals.RemoteUpdateClient.RemoteUpdater(address, port);
            var ret = await remoteUpdater.UpdateBitstream(bitstreamNum, fileBytes.Value);

            if (ret.IsSuccessful)
            {
                logger.Info($"Device {address} Update bitstream successfully");
                return TypedResults.Ok(ret.Value);
            }
            else
            {
                logger.Error(ret.Error);
                return TypedResults.InternalServerError(ret.Error);
            }

        }
        catch (Exception error)
        {
            return TypedResults.InternalServerError(error);
        }
    }


    /// <summary>
    /// 下载多个比特流文件
    /// </summary>
    /// <param name="address">设备地址</param>
    /// <param name="port">设备端口</param>
    /// <param name="bitstreamNum">比特流编号</param>
    /// <returns>总共上传比特流的数量</returns>
    [Authorize("Admin")]
    [HttpPost("DownloadMultiBitstreams")]
    [EnableCors("Users")]
    [ProducesResponseType(typeof(int), StatusCodes.Status200OK)]
    [ProducesResponseType(typeof(ArgumentException), StatusCodes.Status400BadRequest)]
    [ProducesResponseType(typeof(Exception), StatusCodes.Status500InternalServerError)]
    public async ValueTask<IResult> DownloadMultiBitstreams(string address, int port, int? bitstreamNum)
    {
        // 检查文件
        var bitstreamsFolder = Path.Combine(Environment.CurrentDirectory, $"{BITSTREAM_PATH}/{address}");
        if (!Directory.Exists(bitstreamsFolder))
            return TypedResults.BadRequest("Empty bitstream, Please upload it first");

        try
        {
            var bitstreams = new List<byte[]?>() { null, null, null, null };
            int cnt = 0; // 上传比特流数量
            for (int i = 0; i < 4; i++)
            {
                var bitstreamDir = Path.Combine(bitstreamsFolder, i.ToString());
                if (!Directory.Exists(bitstreamDir))
                    continue;
                cnt++;

                // 读取文件
                var filePath = Directory.GetFiles(bitstreamDir)[0];
                var fileBytes = await ProcessBitstream(filePath);
                if (!fileBytes.IsSuccessful) return TypedResults.InternalServerError(fileBytes.Error);
                bitstreams[i] = fileBytes.Value;
            }

            // 下载比特流
            var remoteUpdater = new Peripherals.RemoteUpdateClient.RemoteUpdater(address, port);
            {
                var ret = await remoteUpdater.UploadBitstreams(bitstreams[0], bitstreams[1], bitstreams[2], bitstreams[3]);
                if (!ret.IsSuccessful) return TypedResults.InternalServerError(ret.Error);
                if (!ret.Value) return TypedResults.InternalServerError("Upload MultiBitstreams failed");
            }

            if (bitstreamNum is not null)
            {
                var ret = await remoteUpdater.HotResetBitstream(bitstreamNum ?? 0);
                if (!ret.IsSuccessful) return TypedResults.InternalServerError(ret.Error);
                if (!ret.Value) return TypedResults.InternalServerError("Hot reset failed");
            }
            return TypedResults.Ok(cnt);
        }
        catch (Exception error)
        {
            return TypedResults.InternalServerError(error);
        }
    }


    /// <summary>
    /// 热复位比特流文件
    /// </summary>
    /// <param name="address">设备地址</param>
    /// <param name="port">设备端口</param>
    /// <param name="bitstreamNum">比特流编号</param>
    /// <returns>操作结果</returns>
    [Authorize("Admin")]
    [HttpPost("HotResetBitstream")]
    [EnableCors("Users")]
    [ProducesResponseType(typeof(bool), StatusCodes.Status200OK)]
    [ProducesResponseType(typeof(ArgumentException), StatusCodes.Status400BadRequest)]
    [ProducesResponseType(typeof(Exception), StatusCodes.Status500InternalServerError)]
    public async ValueTask<IResult> HotResetBitstream(string address, int port, int bitstreamNum)
    {
        var remoteUpdater = new Peripherals.RemoteUpdateClient.RemoteUpdater(address, port);
        var ret = await remoteUpdater.HotResetBitstream(bitstreamNum);

        if (ret.IsSuccessful)
        {
            logger.Info($"Device {address} Update bitstream successfully");
            return TypedResults.Ok(ret.Value);
        }
        else
        {
            logger.Error(ret.Error);
            return TypedResults.InternalServerError(ret.Error);
        }
    }

    /// <summary>
    /// [TODO:description]
    /// </summary>
    /// <param name="address">[TODO:parameter]</param>
    /// <param name="port">[TODO:parameter]</param>
    /// <returns>[TODO:return]</returns>
    [Authorize("Admin")]
    [HttpPost("GetFirmwareVersion")]
    [EnableCors("Users")]
    [ProducesResponseType(typeof(UInt32), StatusCodes.Status200OK)]
    [ProducesResponseType(typeof(ArgumentException), StatusCodes.Status400BadRequest)]
    [ProducesResponseType(typeof(Exception), StatusCodes.Status500InternalServerError)]
    public async ValueTask<IResult> GetFirmwareVersion(string address, int port)
    {
        var remoteUpdater = new Peripherals.RemoteUpdateClient.RemoteUpdater(address, port);
        var ret = await remoteUpdater.GetVersion();

        if (ret.IsSuccessful)
        {
            logger.Info($"Device {address} get firmware version successfully");
            return TypedResults.Ok(ret.Value);
        }
        else
        {
            logger.Error(ret.Error);
            return TypedResults.InternalServerError(ret.Error);
        }
    }

}
