using Blahblah.FlowerStory.Server.Data;
using Blahblah.FlowerStory.Server.Data.Model;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using System.ComponentModel.DataAnnotations;

namespace Blahblah.FlowerStory.Server.Controller;

/// <summary>
/// 花草相关 API 服务
/// </summary>
[ApiController]
[Produces("application/json")]
[Route("api/flower")]
public class FlowerApiController : BaseController
{
    /// <inheritdoc/>
    public FlowerApiController(FlowerDatabase database, ILogger<BaseController>? logger = null) : base(database, logger)
    {
    }

    /// <summary>
    /// 获取用户名下所有符合条件的花草
    /// </summary>
    /// <remarks>
    /// 请求示例：
    /// 
    ///     GET /api/flower/query
    ///     Authorization: authorization id
    /// 
    /// 参数：
    /// 
    ///     cid: int?
    ///     key: string?
    ///     from: long?
    ///     to: long?
    ///     cfrom: decimal?
    ///     cto: decimal?
    ///     photo: bool?
    ///     lon: double?
    ///     lat: double?
    ///     distance: int?
    ///     p: int?
    ///     size: int?
    /// 
    /// </remarks>
    /// <param name="categoryId">类别 id</param>
    /// <param name="key">查询关键字</param>
    /// <param name="buyFrom">起始购买日期</param>
    /// <param name="buyTo">结束购买日期</param>
    /// <param name="costFrom">开销最小值</param>
    /// <param name="costTo">开销最大值</param>
    /// <param name="includePhoto">是否包含封面图片</param>
    /// <param name="latitude">纬度</param>
    /// <param name="longitude">经度</param>
    /// <param name="distance">距离（米）</param>
    /// <param name="page">页数</param>
    /// <param name="pageSize">分页大小</param>
    /// <returns>会话有效则返回符合条件的花草集</returns>
    /// <response code="200">返回符合条件的花草集</response>
    /// <response code="401">未找到登录会话或已过期</response>
    /// <response code="403">用户已禁用</response>
    /// <response code="404">未找到关联用户</response>
    [Route("query", Name = "queryFlowers")]
    [ProducesResponseType(StatusCodes.Status200OK)]
    [ProducesResponseType(StatusCodes.Status401Unauthorized)]
    [ProducesResponseType(StatusCodes.Status403Forbidden)]
    [ProducesResponseType(StatusCodes.Status404NotFound)]
    [ProducesErrorResponseType(typeof(ErrorResponse))]
    [HttpGet]
    [ResponseCache(NoStore = true, Location = ResponseCacheLocation.None)]
    public ActionResult<FlowerResult> GetFlowers(
        [FromQuery(Name = "cid")] int? categoryId,
        [FromQuery] string? key,
        [FromQuery(Name = "from")] long? buyFrom,
        [FromQuery(Name = "to")] long? buyTo,
        [FromQuery(Name = "cfrom")] decimal? costFrom,
        [FromQuery(Name = "cto")] decimal? costTo,
        [FromQuery(Name = "photo")] bool? includePhoto,
        [FromQuery(Name = "lat")] double? latitude,
        [FromQuery(Name = "lon")] double? longitude,
        [FromQuery] int? distance,
        [FromQuery(Name = "p")] int? page = 0,
        [FromQuery(Name = "size")] int? pageSize = 20)
    {
        var (result, user) = CheckPermission();
        if (result != null)
        {
            return result;
        }
        if (user == null)
        {
            return NotFound();
        }

        SaveDatabase();

        var flowers = GetFlowerResult(user.Id, null, categoryId, key, buyFrom, buyTo, costFrom, costTo, includePhoto, latitude, longitude, distance, page, pageSize);
        return Ok(flowers);
    }

    /// <summary>
    /// 获取最新、最近的花草
    /// </summary>
    /// <remarks>
    /// 请求示例：
    /// 
    ///     GET /api/flower/latest
    /// 
    /// 参数：
    /// 
    ///     cid: int?
    ///     key: string?
    ///     from: long?
    ///     to: long?
    ///     cfrom: decimal?
    ///     cto: decimal?
    ///     photo: bool?
    ///     lon: double?
    ///     lat: double?
    ///     distance: int?
    ///     p: int?
    ///     size: int?
    /// 
    /// </remarks>
    /// <param name="categoryId">类别 id</param>
    /// <param name="key">查询关键字</param>
    /// <param name="buyFrom">起始购买日期</param>
    /// <param name="buyTo">结束购买日期</param>
    /// <param name="costFrom">开销最小值</param>
    /// <param name="costTo">开销最大值</param>
    /// <param name="includePhoto">是否包含封面图片</param>
    /// <param name="latitude">纬度</param>
    /// <param name="longitude">经度</param>
    /// <param name="distance">距离（米）</param>
    /// <param name="page">页数</param>
    /// <param name="pageSize">分页大小</param>
    /// <returns>会话有效则返回符合条件的花草集</returns>
    /// <response code="200">返回符合条件的花草集</response>
    /// <response code="401">未找到登录会话或已过期</response>
    /// <response code="403">用户已禁用</response>
    /// <response code="404">未找到关联用户</response>
    [Route("latest", Name = "queryLatestFlowers")]
    [ProducesResponseType(StatusCodes.Status200OK)]
    [HttpGet]
    [ResponseCache(NoStore = true, Location = ResponseCacheLocation.None)]
    public ActionResult<FlowerResult> GetLatestFlowers(
        [FromQuery(Name = "cid")] int? categoryId,
        [FromQuery] string? key,
        [FromQuery(Name = "from")] long? buyFrom,
        [FromQuery(Name = "to")] long? buyTo,
        [FromQuery(Name = "cfrom")] decimal? costFrom,
        [FromQuery(Name = "cto")] decimal? costTo,
        [FromQuery(Name = "photo")] bool? includePhoto,
        [FromQuery(Name = "lat")] double? latitude,
        [FromQuery(Name = "lon")] double? longitude,
        [FromQuery] int? distance,
        [FromQuery(Name = "p")] int? page = 0,
        [FromQuery(Name = "size")] int? pageSize = 20)
    {
        var result = GetFlowerResult(null, (int)EventTypes.Share, categoryId, key, buyFrom, buyTo, costFrom, costTo, includePhoto, latitude, longitude, distance, page, pageSize);

        return Ok(result);
    }

    private FlowerResult GetFlowerResult(int? userId, int? eventId, int? categoryId, string? key,
        long? buyFrom, long? buyTo, decimal? costFrom, decimal? costTo,
        bool? includePhoto, double? latitude, double? longitude, int? distance,
        int? page = 0, int? pageSize = 20)
    {
        IEnumerable<FlowerItem> flowers;
        if (userId != null)
        {
            flowers = database.Flowers.Where(f => f.OwnerId == userId);
        }
        else
        {
            flowers = database.Flowers;
        }
        if (categoryId != null)
        {
            flowers = flowers.Where(f => f.CategoryId == categoryId);
        }
        if (key != null)
        {
            flowers = flowers.Where(f =>
                f.Name.ToLower().Contains(key.ToLower()) ||
                f.Purchase != null &&
                f.Purchase.ToLower().Contains(key.ToLower()));
        }
        if (buyFrom != null)
        {
            flowers = flowers.Where(f => f.DateBuyUnixTime >= buyFrom);
        }
        if (buyTo != null)
        {
            flowers = flowers.Where(f => f.DateBuyUnixTime <= buyTo);
        }
        if (costFrom != null)
        {
            flowers = flowers.Where(f => f.Cost != null && f.Cost >= costFrom);
        }
        if (costTo != null)
        {
            flowers = flowers.Where(f => f.Cost != null && f.Cost <= costTo);
        }

        if (eventId != null)
        {
            flowers = flowers.Where(f => database.Records.Any(r => r.FlowerId == f.Id && r.EventId == eventId));
        }

        if (distance != null && latitude != null && longitude != null)
        {
            flowers = flowers.Where(f => f.Latitude != null && f.Longitude != null)
                .AsEnumerable()
                .Where(f =>
                {
                    var d = GetDistance(latitude.Value, longitude.Value, f.Latitude ?? 0, f.Longitude ?? 0);
                    f.Distance = (int)d;
                    return d <= distance;
                });
        }

        int count = flowers.Count();

        var size = pageSize ?? 20;
        var p = page ?? 0;
        flowers = flowers.OrderByDescending(f => f.DateBuyUnixTime).Skip(p * size).Take(size);

        if (includePhoto == true)
        {
            foreach (var f in flowers)
            {
                f.Photos = database.Photos.Where(p => p.FlowerId == f.Id && p.RecordId == null).ToList();
                foreach (var photo in f.Photos)
                {
                    photo.Url = $"photo/flower/{f.Id}/{photo.Path}";
                }
            }
        }

        return new()
        {
            Flowers = flowers.ToArray(),
            Count = count
        };
    }

    /// <summary>
    /// 查询用户的花草
    /// </summary>
    /// <remarks>
    /// 请求示例：
    /// 
    ///     GET /api/flower/get
    ///     Authorization: authorization id
    /// 
    /// 参数：
    /// 
    ///     id: int
    ///     photo: bool?
    /// 
    /// </remarks>
    /// <param name="id">花草唯一 id</param>
    /// <param name="includePhoto">是否包含封面图片</param>
    /// <returns>会话有效则返回查询到的花草对象</returns>
    /// <response code="200">返回查询到的花草对象</response>
    /// <response code="401">未找到登录会话或已过期</response>
    /// <response code="403">用户已禁用</response>
    /// <response code="404">未找到关联用户或者未找到花草</response>
    [Route("get", Name = "getFlower")]
    [ProducesResponseType(StatusCodes.Status200OK)]
    [ProducesResponseType(StatusCodes.Status401Unauthorized)]
    [ProducesResponseType(StatusCodes.Status403Forbidden)]
    [ProducesResponseType(StatusCodes.Status404NotFound)]
    [ProducesErrorResponseType(typeof(ErrorResponse))]
    [HttpGet]
    [ResponseCache(NoStore = true, Location = ResponseCacheLocation.None)]
    public ActionResult<FlowerItem> GetFlower(
        [FromQuery][Required] int id,
        [FromQuery(Name = "photo")] bool? includePhoto)
    {
        var (result, user) = CheckPermission();
        if (result != null)
        {
            return result;
        }
        if (user == null)
        {
            return NotFound();
        }

        SaveDatabase();

        var item = database.Flowers.Find(id);
        if (item == null)
        {
            return NotFound($"Flower id {id} not found");
        }

        if (includePhoto == true)
        {
            item.Photos = database.Photos.Where(p => p.FlowerId == item.Id && p.RecordId == null).ToList();
            foreach (var photo in item.Photos)
            {
                photo.Url = $"photo/flower/{item.Id}/{photo.Path}";
            }
        }

        return Ok(item);
    }

    /// <summary>
    /// 移除用户的花草
    /// </summary>
    /// <remarks>
    /// 请求示例：
    /// 
    ///     DELETE /api/flower/remove
    ///     Authorization: authorization id
    /// 
    /// 参数：
    /// 
    ///     id: int
    /// 
    /// </remarks>
    /// <param name="id">花草唯一 id</param>
    /// <returns>会话有效则返回操作影响的数据库行数</returns>
    /// <response code="200">返回操作影响的数据库行数</response>
    /// <response code="401">未找到登录会话或已过期</response>
    /// <response code="403">用户已禁用</response>
    /// <response code="404">未找到关联用户</response>
    [Route("remove", Name = "removeFlower")]
    [ProducesResponseType(StatusCodes.Status200OK)]
    [ProducesResponseType(StatusCodes.Status401Unauthorized)]
    [ProducesResponseType(StatusCodes.Status403Forbidden)]
    [ProducesResponseType(StatusCodes.Status404NotFound)]
    [ProducesErrorResponseType(typeof(ErrorResponse))]
    [HttpDelete]
    public ActionResult<int> RemoveFlower([FromQuery][Required] int id)
    {
        var (result, user) = CheckPermission();
        if (result != null)
        {
            return result;
        }
        if (user == null)
        {
            return NotFound();
        }

        //database.Records.Where(r => r.OwnerId == user.Id && r.FlowerId == id).ExecuteDelete();
        var count = database.Flowers.Where(f => f.OwnerId == user.Id && f.Id == id).ExecuteDelete();

        SaveDatabase();

        return Ok(count);
    }

    /// <summary>
    /// 批量移除用户的花草
    /// </summary>
    /// <remarks>
    /// 请求示例：
    /// 
    ///     POST /api/flower/remove
    ///     Authorization: authorization id
    ///     [
    ///         2, 4, 5, 11
    ///     ]
    /// 
    /// </remarks>
    /// <param name="ids">要移除的花草唯一 id 的数组</param>
    /// <returns>会话有效则返回操作影响的数据库行数</returns>
    /// <response code="200">返回操作影响的数据库行数</response>
    /// <response code="401">未找到登录会话或已过期</response>
    /// <response code="403">用户已禁用</response>
    /// <response code="404">未找到关联用户</response>
    [Route("removeany", Name = "removeFlowers")]
    [ProducesResponseType(StatusCodes.Status200OK)]
    [ProducesResponseType(StatusCodes.Status401Unauthorized)]
    [ProducesResponseType(StatusCodes.Status403Forbidden)]
    [ProducesResponseType(StatusCodes.Status404NotFound)]
    [ProducesErrorResponseType(typeof(ErrorResponse))]
    [HttpPost]
    [Consumes("application/json")]
    public ActionResult<int> RemoveFlowers([FromBody] int[] ids)
    {
        var (result, user) = CheckPermission();
        if (result != null)
        {
            return result;
        }
        if (user == null)
        {
            return NotFound();
        }

        //database.Records.Where(r => r.OwnerId == user.Id && ids.Contains(r.FlowerId)).ExecuteDelete();
        var count = database.Flowers.Where(f => f.OwnerId == user.Id && ids.Contains(f.Id)).ExecuteDelete();

        SaveDatabase();

        return Ok(count);
    }

    /// <summary>
    /// 用户添加花草
    /// </summary>
    /// <remarks>
    /// 请求示例：
    /// 
    ///     POST /api/flower/add
    ///     Authorization: authorization id
    /// 
    /// 参数：
    /// 
    ///     categoryId: 0
    ///     name: "玛格丽特"
    ///     dateBuy: 1684919954743
    ///     cost: 5.00
    ///     purchase: "花鸟市场"
    ///     memo: "备注信息"
    ///     lon: 29.5462794
    ///     lat: 106.5380034
    ///     cid: 1
    ///     cover: &lt;photo&gt;
    /// 
    /// </remarks>
    /// <param name="flower">花草参数</param>
    /// <returns>添加成功则返回已添加的花草对象</returns>
    /// <response code="200">返回已添加的花草对象</response>
    /// <response code="400">提交的内容不合法</response>
    /// <response code="401">未找到登录会话或已过期</response>
    /// <response code="403">用户已禁用</response>
    /// <response code="404">未找到关联用户</response>
    /// <response code="413">提交正文过大</response>
    [Route("add", Name = "addFlower")]
    [ProducesResponseType(StatusCodes.Status200OK)]
    [ProducesResponseType(StatusCodes.Status400BadRequest)]
    [ProducesResponseType(StatusCodes.Status401Unauthorized)]
    [ProducesResponseType(StatusCodes.Status403Forbidden)]
    [ProducesResponseType(StatusCodes.Status404NotFound)]
    [ProducesResponseType(StatusCodes.Status413PayloadTooLarge)]
    [ProducesErrorResponseType(typeof(ErrorResponse))]
    [HttpPost]
    [Consumes("multipart/form-data")]
    [RequestSizeLimit(5 * 1024 * 1024)]
    public async Task<ActionResult<FlowerItem>> AddFlower([FromForm] FlowerParameter flower)
    {
        var (result, user) = CheckPermission();
        if (result != null)
        {
            return result;
        }
        if (user == null)
        {
            return NotFound();
        }

        var item = new FlowerItem
        {
            OwnerId = user.Id,
            CategoryId = flower.CategoryId,
            Name = flower.Name,
            DateBuyUnixTime = flower.DateBuy,
            Cost = flower.Cost,
            Purchase = flower.Purchase,
            Memo = flower.Memo,
            Latitude = flower.Latitude,
            Longitude = flower.Longitude
        };
        database.Flowers.Add(item);
        SaveDatabase();

        var now = user.ActiveDateUnixTime ?? DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
        if (flower.Cover?.Length > 0)
        {
            var file = WrapFormFile(flower.Cover);
            if (file == null)
            {
                return BadRequest();
            }

            try
            {
                await ExecuteTransaction(async token =>
                {
                    var cover = new PhotoItem
                    {
                        OwnerId = user.Id,
                        FlowerId = item.Id,
                        FileType = file.FileType,
                        FileName = file.Filename,
                        Path = file.Path,
                        DateUploadUnixTime = now,
                        Width = file.Width,
                        Height = file.Height
                    };
                    AddPhotoItem(cover);

                    await WriteToFile(item.Id, file, token);
                });
            }
            catch (Exception ex)
            {
                return Problem(ex.ToString(), "api/flower/add#WriteToFile");
                // TODO: Logger
            }
        }
        else if (flower.CoverId is int coverId)
        {
            var photo = database.Photos.SingleOrDefault(p => p.Id == coverId && p.OwnerId == user.Id);
            if (photo != null)
            {
                photo.FlowerId = item.Id;
                SaveDatabase();

                try
                {
                    MoveTempFileToFlower(item.Id, photo.Path);
                }
                catch (Exception ex)
                {
                    return Problem(ex.ToString(), "api/flower/add#MoveTempFileToFlower");
                    // TODO: Logger
                }
            }
        }

        return Ok(item);
    }

    /// <summary>
    /// 用户上传封面
    /// </summary>
    /// <remarks>
    /// 请求示例：
    /// 
    ///     POST /api/flower/cover_upload
    ///     Authorization: authorization id
    /// 
    /// 参数：
    /// 
    ///     cover: &lt;photo&gt;
    /// 
    /// </remarks>
    /// <param name="p">封面参数</param>
    /// <returns>添加成功则返回封面 id</returns>
    /// <response code="200">返回已添加的封面 id</response>
    /// <response code="400">提交的内容不合法</response>
    /// <response code="401">未找到登录会话或已过期</response>
    /// <response code="403">用户已禁用</response>
    /// <response code="404">未找到关联用户</response>
    /// <response code="413">提交正文过大</response>
    [Route("cover_upload", Name = "uploadCover")]
    [ProducesResponseType(StatusCodes.Status200OK)]
    [ProducesResponseType(StatusCodes.Status400BadRequest)]
    [ProducesResponseType(StatusCodes.Status401Unauthorized)]
    [ProducesResponseType(StatusCodes.Status403Forbidden)]
    [ProducesResponseType(StatusCodes.Status404NotFound)]
    [ProducesResponseType(StatusCodes.Status413PayloadTooLarge)]
    [ProducesErrorResponseType(typeof(ErrorResponse))]
    [HttpPost]
    [Consumes("multipart/form-data")]
    [RequestSizeLimit(5 * 1024 * 1024)]
    public async Task<ActionResult<int>> UploadCover([FromForm] CoverParameter p)
    {
        var (result, user) = CheckPermission();
        if (result != null)
        {
            return result;
        }
        if (user == null)
        {
            return NotFound();
        }

        if (p.Cover?.Length > 0)
        {
            var file = WrapFormFile(p.Cover);
            if (file == null)
            {
                return BadRequest();
            }

            var now = user.ActiveDateUnixTime ?? DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();

            try
            {
                await WriteToFile(TempId, file);

                var item = new PhotoItem
                {
                    OwnerId = user.Id,
                    FileType = file.FileType,
                    FileName = file.Filename,
                    Path = file.Path,
                    DateUploadUnixTime = now,
                    Width = file.Width,
                    Height = file.Height
                };
                database.Photos.Add(item);
                SaveDatabase();

                return Ok(item.Id);
            }
            catch (Exception ex)
            {
                return Problem(ex.ToString(), "api/flower/cover_upload");
                // TODO: Logger
            }
        }

        return BadRequest();
    }

    /// <summary>
    /// 修改花草
    /// </summary>
    /// <remarks>
    /// 请求示例：
    /// 
    ///     PUT /api/flower/update
    ///     Authorization: authorization id
    /// 
    /// 参数：
    /// 
    ///     id: 0
    ///     categoryId: 1
    ///     name: "姬小菊"
    ///     dateBuy: 1684935276117
    ///     cost: 15.40
    ///     purchase: null
    ///     memo: "备注"
    ///     lon: 29.5462794
    ///     lat: 106.5380034
    ///     cover: &lt;photo&gt;
    /// 
    /// </remarks>
    /// <param name="update">修改参数</param>
    /// <returns>修改成功则返回已修改的花草对象</returns>
    /// <response code="200">返回已修改的花草对象</response>
    /// <response code="400">提交的内容不合法</response>
    /// <response code="401">未找到登录会话或已过期</response>
    /// <response code="403">用户已禁用</response>
    /// <response code="404">未找到关联用户或者未找到将修改的花草对象</response>
    /// <response code="413">提交正文过大</response>
    [Route("update", Name = "updateFlower")]
    [ProducesResponseType(StatusCodes.Status200OK)]
    [ProducesResponseType(StatusCodes.Status400BadRequest)]
    [ProducesResponseType(StatusCodes.Status401Unauthorized)]
    [ProducesResponseType(StatusCodes.Status403Forbidden)]
    [ProducesResponseType(StatusCodes.Status404NotFound)]
    [ProducesResponseType(StatusCodes.Status413PayloadTooLarge)]
    [ProducesErrorResponseType(typeof(ErrorResponse))]
    [HttpPut]
    [Consumes("multipart/form-data")]
    [RequestSizeLimit(5 * 1024 * 1024)]
    public async Task<ActionResult<FlowerItem>> Update([FromForm] FlowerUpdateParameter update)
    {
        var (result, user) = CheckPermission();
        if (result != null)
        {
            return result;
        }
        if (user == null)
        {
            return NotFound();
        }

        var flower = database.Flowers.SingleOrDefault(f => f.Id == update.Id && f.OwnerId == user.Id);
        if (flower == null)
        {
            return NotFound($"Flower id {update.Id} not found");
        }
        flower.CategoryId = update.CategoryId;
        flower.Name = update.Name;
        flower.DateBuyUnixTime = update.DateBuy;
        flower.Cost = update.Cost;
        flower.Purchase = update.Purchase;
        flower.Memo = update.Memo;
        flower.Latitude = update.Latitude;
        flower.Longitude = update.Longitude;

        if (update.Cover?.Length > 0)
        {
            var file = WrapFormFile(update.Cover);
            if (file == null)
            {
                return BadRequest();
            }

            var now = user.ActiveDateUnixTime ?? DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
            var photos = database.Photos.Where(p => p.FlowerId == update.Id && p.RecordId == null).ToList();
            if (photos.Count > 0)
            {
                database.Photos.Where(p => p.RecordId == null).ExecuteDelete();
                foreach (var photo in photos)
                {
                    DeleteFile(update.Id, photo.Path);
                }
            }
            SaveDatabase();

            try
            {
                await ExecuteTransaction(async token =>
                {
                    var cover = new PhotoItem
                    {
                        OwnerId = user.Id,
                        FlowerId = update.Id,
                        FileType = file.FileType,
                        FileName = file.Filename,
                        Path = file.Path,
                        DateUploadUnixTime = now,
                        Width = file.Width,
                        Height = file.Height
                    };
                    AddPhotoItem(cover);

                    await WriteToFile(update.Id, file, token);
                });
            }
            catch (Exception ex)
            {
                return Problem(ex.ToString(), "api/flower/update");
                // TODO: Logger
            }
        }
        else
        {
            SaveDatabase();
        }

        return Ok(flower);
    }

    /// <summary>
    /// 添加花草封面
    /// </summary>
    /// <remarks>
    /// 请求示例：
    /// 
    ///     POST /api/flower/add_cover
    ///     Authorization: authorization id
    /// 
    /// 参数：
    /// 
    ///     id: int
    ///     lon: double?
    ///     lat: double?
    ///     photo: IFormFile
    /// 
    /// </remarks>
    /// <param name="param">封面修改参数</param>
    /// <returns>修改成功则返回 HTTP 204</returns>
    /// <response code="204">修改成功</response>
    /// <response code="400">照片格式非法</response>
    /// <response code="401">未找到登录会话或已过期</response>
    /// <response code="403">用户已禁用</response>
    /// <response code="404">未找到关联用户或者关联的花草</response>
    /// <response code="413">提交正文过大</response>
    [Route("add_cover", Name = "addFlowerCover")]
    [ProducesResponseType(StatusCodes.Status204NoContent)]
    [ProducesResponseType(StatusCodes.Status400BadRequest)]
    [ProducesResponseType(StatusCodes.Status401Unauthorized)]
    [ProducesResponseType(StatusCodes.Status403Forbidden)]
    [ProducesResponseType(StatusCodes.Status404NotFound)]
    [ProducesResponseType(StatusCodes.Status413PayloadTooLarge)]
    [ProducesErrorResponseType(typeof(ErrorResponse))]
    [HttpPost]
    [Consumes("multipart/form-data")]
    [RequestSizeLimit(5 * 1024 * 1024)]
    public async Task<ActionResult> UploadCovers([FromForm] FlowerCoverParameter param)
    {
        var (result, user) = CheckPermission();
        if (result != null)
        {
            return result;
        }
        if (user == null)
        {
            return NotFound();
        }

        var flower = database.Flowers.SingleOrDefault(f => f.Id == param.Id && f.OwnerId == user.Id);
        if (flower == null)
        {
            return NotFound($"Flower id {param.Id} not found");
        }
        if (param.Cover?.Length > 0)
        {
            var file = WrapFormFile(param.Cover);
            if (file == null)
            {
                return BadRequest();
            }

            var now = user.ActiveDateUnixTime ?? DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();

            flower.Latitude = param.Latitude;
            flower.Longitude = param.Longitude;
            SaveDatabase();

            try
            {
                await ExecuteTransaction(async token =>
                {
                    var cover = new PhotoItem
                    {
                        OwnerId = user.Id,
                        FlowerId = param.Id,
                        FileType = file.FileType,
                        FileName = file.Filename,
                        Path = file.Path,
                        DateUploadUnixTime = now,
                        Width = file.Width,
                        Height = file.Height
                    };
                    AddPhotoItem(cover);

                    await WriteToFile(param.Id, file, token);
                });
            }
            catch (Exception ex)
            {
                return Problem(ex.ToString(), "api/flower/add_cover");
                // TODO: Logger
            }
        }

        return NoContent();
    }

    /// <summary>
    /// 获取花草特定类型事件的照片列表
    /// </summary>
    /// <remarks>
    /// 请求示例：
    /// 
    ///     GET /api/flower/photos
    ///     Authorization: authorization id
    /// 
    /// 参数：
    /// 
    ///     id: int
    ///     eid: int?
    /// 
    /// </remarks>
    /// <param name="id">花草唯一 id</param>
    /// <param name="eventId">事件类型 id</param>
    /// <returns>验证通过则返回花草特定类型事件的照片列表</returns>
    /// <response code="200">返回花草特定类型事件的照片列表</response>
    /// <response code="401">未找到登录会话或已过期</response>
    /// <response code="403">用户已禁用</response>
    /// <response code="404">未找到关联用户或者未找到花草对象</response>
    [Route("photos", Name = "getFlowerPhotos")]
    [ProducesResponseType(StatusCodes.Status200OK)]
    [ProducesResponseType(StatusCodes.Status401Unauthorized)]
    [ProducesResponseType(StatusCodes.Status403Forbidden)]
    [ProducesResponseType(StatusCodes.Status404NotFound)]
    [ProducesErrorResponseType(typeof(ErrorResponse))]
    [HttpGet]
    [ResponseCache(NoStore = true, Location = ResponseCacheLocation.None)]
    public ActionResult<PhotoItem[]> GetPhotos([Required][FromQuery] int id, [FromQuery(Name = "eid")] int? eventId = 0)
    {
        var (result, user) = CheckPermission();
        if (result != null)
        {
            return result;
        }
        if (user == null)
        {
            return NotFound();
        }
        SaveDatabase();

        var photos = database.Photos.Where(p => database.Records.Any(r => r.FlowerId == id && r.EventId == eventId && r.OwnerId == user.Id));

        return Ok(photos);
    }
}
