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/event")]
public class EventApiController : BaseController
{
    /// <inheritdoc/>
    public EventApiController(FlowerDatabase database, ILogger<BaseController>? logger = null) : base(database, logger)
    {
    }

    /// <summary>
    /// 获取用户相关所有符合条件的事件
    /// </summary>
    /// <remarks>
    /// 请求示例：
    /// 
    ///     GET /api/event/query
    ///     Authorization: authorization id
    /// 
    /// 参数：
    /// 
    ///     eid: int?
    ///     key: string?
    ///     from: long?
    ///     to: long?
    ///     p: bool?
    /// 
    /// </remarks>
    /// <param name="eventId">事件类型 id</param>
    /// <param name="key">查询关键字</param>
    /// <param name="from">起始日期</param>
    /// <param name="to">结束日期</param>
    /// <param name="includePhoto">是否包含图片</param>
    /// <returns>会话有效则返回符合条件的花草集</returns>
    /// <response code="200">返回符合条件的花草集</response>
    /// <response code="401">未找到登录会话或已过期</response>
    /// <response code="403">用户已禁用</response>
    /// <response code="404">未找到关联用户</response>
    [Route("query", Name = "queryEvents")]
    [ProducesResponseType(StatusCodes.Status200OK)]
    [ProducesResponseType(StatusCodes.Status401Unauthorized)]
    [ProducesResponseType(StatusCodes.Status403Forbidden)]
    [ProducesResponseType(StatusCodes.Status404NotFound)]
    [ProducesErrorResponseType(typeof(ErrorResponse))]
    [HttpGet]
    [ResponseCache(NoStore = true, Location = ResponseCacheLocation.None)]
    public ActionResult<RecordItem[]> GetRecords(
        [FromQuery(Name = "eid")] int? eventId,
        [FromQuery] string? key,
        [FromQuery] long? from,
        [FromQuery] long? to,
        [FromQuery(Name = "p")] bool? includePhoto)
    {
        var (result, user) = CheckPermission();
        if (result != null)
        {
            return result;
        }
        if (user == null)
        {
            return NotFound();
        }

        SaveDatabase();

        var records = database.Records.Where(r => r.OwnerId == user.Id);
        if (eventId != null)
        {
            records = records.Where(r => r.EventId == eventId);
        }
        if (key != null)
        {
            records = records.Where(r =>
                r.ByUserName != null && r.ByUserName.ToLower().Contains(key.ToLower()) ||
                r.Memo != null && r.Memo.ToLower().Contains(key.ToLower()));
        }
        if (from != null)
        {
            records = records.Where(r => r.DateUnixTime >= from);
        }
        if (to != null)
        {
            records = records.Where(r => r.DateUnixTime <= to);
        }

        if (includePhoto == true)
        {
            records = records.Include(r => r.Photos);
        }

        return Ok(records.ToArray());
    }

    /// <summary>
    /// 移除用户的事件
    /// </summary>
    /// <remarks>
    /// 请求示例：
    /// 
    ///     DELETE /api/event/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 = "removeEvent")]
    [ProducesResponseType(StatusCodes.Status200OK)]
    [ProducesResponseType(StatusCodes.Status401Unauthorized)]
    [ProducesResponseType(StatusCodes.Status403Forbidden)]
    [ProducesResponseType(StatusCodes.Status404NotFound)]
    [ProducesErrorResponseType(typeof(ErrorResponse))]
    [HttpDelete]
    public ActionResult<int> RemoveEvent([FromQuery][Required] int id)
    {
        var (result, user) = CheckPermission();
        if (result != null)
        {
            return result;
        }
        if (user == null)
        {
            return NotFound();
        }

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

        SaveDatabase();

        return Ok(count);
    }

    /// <summary>
    /// 批量移除用户的事件
    /// </summary>
    /// <remarks>
    /// 请求示例：
    /// 
    ///     POST /api/event/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 = "removeEvents")]
    [ProducesResponseType(StatusCodes.Status200OK)]
    [ProducesResponseType(StatusCodes.Status401Unauthorized)]
    [ProducesResponseType(StatusCodes.Status403Forbidden)]
    [ProducesResponseType(StatusCodes.Status404NotFound)]
    [ProducesErrorResponseType(typeof(ErrorResponse))]
    [HttpPost]
    [Consumes("application/json")]
    public ActionResult<int> RemoveEvents([FromBody] int[] ids)
    {
        var (result, user) = CheckPermission();
        if (result != null)
        {
            return result;
        }
        if (user == null)
        {
            return NotFound();
        }

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

        SaveDatabase();

        return Ok(count);
    }

    /// <summary>
    /// 用户添加事件
    /// </summary>
    /// <remarks>
    /// 请求示例：
    /// 
    ///     POST /api/event/add
    ///     Authorization: authorization id
    /// 
    /// 参数：
    /// 
    ///     flowerId: 1
    ///     eventId": 4   // 浇水
    ///     byUser: "朋友"
    ///     memo: "快干死了"
    ///     lon: 29.5462794
    ///     lat: 106.5380034
    ///     photos: &lt;photo&gt;[]
    /// 
    /// </remarks>
    /// <param name="event">事件参数</param>
    /// <returns>添加成功则返回已添加的事件对象</returns>
    /// <response code="200">返回已添加的事件对象</response>
    /// <response code="401">未找到登录会话或已过期</response>
    /// <response code="403">用户已禁用</response>
    /// <response code="404">未找到关联用户</response>
    [Route("add", Name = "addEvent")]
    [ProducesResponseType(StatusCodes.Status200OK)]
    [ProducesResponseType(StatusCodes.Status401Unauthorized)]
    [ProducesResponseType(StatusCodes.Status403Forbidden)]
    [ProducesResponseType(StatusCodes.Status404NotFound)]
    [ProducesErrorResponseType(typeof(ErrorResponse))]
    [HttpPost]
    [Consumes("multipart/form-data")]
    // 5 photos
    [RequestSizeLimit(75 * 1024 * 1024)]
    public async Task<ActionResult<RecordItem>> AddEvent([FromForm] EventParameter @event)
    {
        var (result, user) = CheckPermission();
        if (result != null)
        {
            return result;
        }
        if (user == null)
        {
            return NotFound();
        }

        var now = user.ActiveDateUnixTime ?? DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
        var item = new RecordItem
        {
            OwnerId = user.Id,
            FlowerId = @event.FlowerId,
            EventId = @event.CategoryId,
            DateUnixTime = now,
            ByUserId = @event.ByUser == null ? user.Id : null,
            ByUserName = @event.ByUser,
            Memo = @event.Memo,
            Latitude = @event.Latitude,
            Longitude = @event.Longitude
        };
        database.Records.Add(item);
        SaveDatabase();

        if (@event.Photos?.Length > 0)
        {
            try
            {
                await ExecuteTransaction(async token =>
                {
                    foreach (var photo in @event.Photos)
                    {
                        var file = WrapFormFile(photo);
                        if (file == null)
                        {
                            continue;
                        }
                        var p = new PhotoItem
                        {
                            OwnerId = user.Id,
                            FlowerId = @event.FlowerId,
                            RecordId = item.Id,
                            FileType = file.FileType,
                            FileName = file.Filename,
                            Path = file.Path,
                            DateUploadUnixTime = now,
                            Width = file.Width,
                            Height = file.Height
                        };
                        AddPhotoItem(p);

                        await WriteToFile(@event.FlowerId, file, token);
                    }
                });
            }
            catch (Exception ex)
            {
                return Problem(ex.ToString(), "api/event/add");
                // TODO: Logger
            }
        }

        return Ok(item);
    }

    /// <summary>
    /// 修改事件
    /// </summary>
    /// <remarks>
    /// 请求示例：
    /// 
    ///     PUT /api/event/update
    ///     Authorization: authorization id
    /// 
    /// 参数：
    /// 
    ///     id: 1
    ///     flowerId: 1
    ///     eventId": 5   // 施肥
    ///     byUser: null
    ///     memo: "花多多1号"
    ///     lon: 29.5462794
    ///     lat: 106.5380034
    ///     photos: &lt;photo&gt;[]
    /// 
    /// </remarks>
    /// <param name="update">修改参数</param>
    /// <returns>修改成功则返回已修改的事件对象</returns>
    /// <response code="200">返回已修改的事件对象</response>
    /// <response code="401">未找到登录会话或已过期</response>
    /// <response code="403">用户已禁用</response>
    /// <response code="404">未找到关联用户或者未找到将修改的事件对象</response>
    [Route("update", Name = "updateEvent")]
    [ProducesResponseType(StatusCodes.Status200OK)]
    [ProducesResponseType(StatusCodes.Status401Unauthorized)]
    [ProducesResponseType(StatusCodes.Status403Forbidden)]
    [ProducesResponseType(StatusCodes.Status404NotFound)]
    [ProducesErrorResponseType(typeof(ErrorResponse))]
    [HttpPut]
    [Consumes("multipart/form-data")]
    // 5 photos
    [RequestSizeLimit(75 * 1024 * 1024)]
    public async Task<ActionResult<RecordItem>> Update([FromForm] EventUpdateParameter update)
    {
        var (result, user) = CheckPermission();
        if (result != null)
        {
            return result;
        }
        if (user == null)
        {
            return NotFound();
        }

        var record = database.Records.SingleOrDefault(r => r.Id == update.Id && r.OwnerId == user.Id);
        if (record == null)
        {
            return NotFound($"Event id {update.Id} not found");
        }
        var now = user.ActiveDateUnixTime ?? DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
        record.FlowerId = update.FlowerId;
        record.EventId = update.CategoryId;
        record.DateUnixTime = now;
        if (update.ByUser == null)
        {
            record.ByUserId = user.Id;
            record.ByUserName = null;
        }
        else
        {
            record.ByUserId = null;
            record.ByUserName = update.ByUser;
        }
        record.Memo = update.Memo;
        record.Latitude = update.Latitude;
        record.Longitude = update.Longitude;

        if (update.Photos?.Length > 0)
        {
            try
            {
                await ExecuteTransaction(async token =>
                {
                    foreach (var photo in update.Photos)
                    {
                        var file = WrapFormFile(photo);
                        if (file == null)
                        {
                            continue;
                        }

                        var photos = database.Photos.Where(p => p.RecordId == record.Id).ToList();
                        if (photos.Count > 0)
                        {
                            database.Photos.Where(p => p.RecordId == record.Id).ExecuteDelete();
                            foreach (var p in photos)
                            {
                                DeleteFile(update.FlowerId, p.Path);
                            }
                        }
                        SaveDatabase();

                        var cover = new PhotoItem
                        {
                            OwnerId = user.Id,
                            FlowerId = update.FlowerId,
                            RecordId = record.Id,
                            FileType = file.FileType,
                            FileName = file.Filename,
                            Path = file.Path,
                            DateUploadUnixTime = now,
                            Width = file.Width,
                            Height = file.Height
                        };
                        AddPhotoItem(cover);

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

        return Ok(user);
    }

    /// <summary>
    /// 添加事件关联照片
    /// </summary>
    /// <remarks>
    /// 请求示例：
    /// 
    ///     POST /api/event/add_photo
    ///     Authorization: authorization id
    /// 
    /// 参数：
    /// 
    ///     id: int
    ///     photo: IFormFile
    /// 
    /// </remarks>
    /// <param name="id">事件唯一 id</param>
    /// <param name="photo">图片</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_photo", Name = "addEventPhoto")]
    [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(15 * 1024 * 1024)]
    public async Task<ActionResult> UploadPhoto([Required][FromQuery] int id, [Required] IFormFile photo)
    {
        var (result, user) = CheckPermission();
        if (result != null)
        {
            return result;
        }
        if (user == null)
        {
            return NotFound();
        }

        SaveDatabase();

        var record = database.Records.SingleOrDefault(r => r.Id == id && r.OwnerId == user.Id);
        if (record == null)
        {
            return NotFound($"Record id {id} not found");
        }
        if (photo.Length > 0)
        {
            var file = WrapFormFile(photo);
            if (file == null)
            {
                return BadRequest();
            }

            try
            {
                await ExecuteTransaction(async token =>
                {
                    var p = new PhotoItem
                    {
                        OwnerId = user.Id,
                        FlowerId = record.FlowerId,
                        RecordId = id,
                        FileType = file.FileType,
                        FileName = file.Filename,
                        Path = file.Path,
                        DateUploadUnixTime = user.ActiveDateUnixTime ?? DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(),
                        Width = file.Width,
                        Height = file.Height
                    };
                    AddPhotoItem(p);

                    await WriteToFile(record.FlowerId, file, token);
                });
            }
            catch (Exception ex)
            {
                return Problem(ex.ToString(), "api/event/add_photo");
                // TODO: Logger
            }
        }

        return NoContent();
    }

    /// <summary>
    /// 批量添加事件关联照片，总大小限制 75MB
    /// </summary>
    /// <remarks>
    /// 请求示例：
    /// 
    ///     POST /api/event/add_photos
    ///     Authorization: authorization id
    /// 
    /// 参数：
    /// 
    ///     id: int
    ///     photos: IFormFile[]
    /// 
    /// </remarks>
    /// <param name="id">事件唯一 id</param>
    /// <param name="photos">图片集</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_photos", Name = "addEventPhotos")]
    [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")]
    // 5 photos
    [RequestSizeLimit(75 * 1024 * 1024)]
    public async Task<ActionResult> UploadPhotos([Required][FromQuery] int id, [Required] IFormFile[] photos)
    {
        var (result, user) = CheckPermission();
        if (result != null)
        {
            return result;
        }
        if (user == null)
        {
            return NotFound();
        }

        if (photos == null || photos.Length == 0)
        {
            return BadRequest();
        }

        var record = database.Records.SingleOrDefault(r => r.Id == id && r.OwnerId == user.Id);
        if (record == null)
        {
            return NotFound($"Record id {id} not found");
        }

        SaveDatabase();

        try
        {
            await ExecuteTransaction(async token =>
            {
                foreach (var photo in photos)
                {
                    if (photo.Length > 0)
                    {
                        var file = WrapFormFile(photo) ?? throw new BadHttpRequestException(photo?.FileName ?? string.Empty);

                        var p = new PhotoItem
                        {
                            OwnerId = user.Id,
                            FlowerId = record.FlowerId,
                            RecordId = id,
                            FileType = file.FileType,
                            FileName = file.Filename,
                            Path = file.Path,
                            DateUploadUnixTime = user.ActiveDateUnixTime ?? DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(),
                            Width = file.Width,
                            Height = file.Height
                        };
                        AddPhotoItem(p);

                        await WriteToFile(record.FlowerId, file, token);
                    }
                }
            });
        }
        catch (BadHttpRequestException bex)
        {
            return BadRequest(bex.Message);
        }
        catch (Exception ex)
        {
            return Problem(ex.ToString(), "api/event/add_photos");
            // TODO: Logger
        }

        return NoContent();
    }

    /// <summary>
    /// 移除事件关联照片
    /// </summary>
    /// <remarks>
    /// 请求示例：
    /// 
    ///     DELETE /api/event/remove_photo
    ///     Authorization: authorization id
    /// 
    /// 参数：
    /// 
    ///     id: int
    /// 
    /// </remarks>
    /// <param name="id">图片唯一 id</param>
    /// <returns>移除成功则返回 HTTP 204</returns>
    /// <response code="204">移除成功</response>
    /// <response code="401">未找到登录会话或已过期或图片所有者不符</response>
    /// <response code="403">用户已禁用</response>
    /// <response code="404">未找到关联用户或者照片</response>
    [Route("remove_photo", Name = "removeEventPhoto")]
    [ProducesResponseType(StatusCodes.Status204NoContent)]
    [ProducesResponseType(StatusCodes.Status401Unauthorized)]
    [ProducesResponseType(StatusCodes.Status403Forbidden)]
    [ProducesResponseType(StatusCodes.Status404NotFound)]
    [ProducesErrorResponseType(typeof(ErrorResponse))]
    [HttpDelete]
    public ActionResult<int> RemoveEventPhoto([FromQuery][Required] int id)
    {
        var (result, user) = CheckPermission();
        if (result != null)
        {
            return result;
        }
        if (user == null)
        {
            return NotFound();
        }

        var photo = database.Photos.Find(id);
        if (photo == null)
        {
            return NotFound();
        }
        var item = database.Flowers.Find(photo.FlowerId);
        if (item == null || item.OwnerId != user.Id)
        {
            return Unauthorized();
        }

        database.Photos.Remove(photo);

        SaveDatabase();

        DeleteFile(photo.FlowerId ?? TempId, photo.Path);

        return NoContent();
    }

    /// <summary>
    /// 批量移除事件关联的照片
    /// </summary>
    /// <remarks>
    /// 请求示例：
    /// 
    ///     POST /api/event/remove_photos
    ///     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("remove_photos", Name = "removeEventPhotos")]
    [ProducesResponseType(StatusCodes.Status200OK)]
    [ProducesResponseType(StatusCodes.Status401Unauthorized)]
    [ProducesResponseType(StatusCodes.Status403Forbidden)]
    [ProducesResponseType(StatusCodes.Status404NotFound)]
    [ProducesErrorResponseType(typeof(ErrorResponse))]
    [HttpPost]
    [Consumes("application/json")]
    public ActionResult<int> RemoveEventPhotos([FromBody] int[] ids)
    {
        var (result, user) = CheckPermission();
        if (result != null)
        {
            return result;
        }
        if (user == null)
        {
            return NotFound();
        }

        if (database.Photos.Any(p => ids.Contains(p.Id) && database.Records.Any(r => r.Id == p.RecordId && r.OwnerId != user.Id)))
        {
            return Unauthorized();
        }

        var photos = database.Photos.Where(p => ids.Contains(p.Id)).ToList();
        var count = database.Photos.Where(p => ids.Contains(p.Id)).ExecuteDelete();

        SaveDatabase();

        foreach (var photo in photos)
        {
            DeleteFile(photo.FlowerId ?? TempId, photo.Path);
        }

        return Ok(count);
    }

    /// <summary>
    /// 获取事件关联的照片列表
    /// </summary>
    /// <remarks>
    /// 请求示例：
    /// 
    ///     GET /api/event/photos
    ///     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("photos", Name = "getEventPhotos")]
    [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)
    {
        var (result, user) = CheckPermission();
        if (result != null)
        {
            return result;
        }
        if (user == null)
        {
            return NotFound();
        }
        SaveDatabase();

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

        return Ok(photos);
    }
}
