using System.ComponentModel.DataAnnotations;
using DysonNetwork.Shared.Proto;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using NodaTime;

namespace DysonNetwork.Drive.Storage;

[ApiController]
[Route("/api/bundles")]
public class BundleController(AppDatabase db) : ControllerBase
{
    public class BundleRequest
    {
        [MaxLength(1024)] public string? Slug { get; set; }
        [MaxLength(1024)] public string? Name { get; set; }
        [MaxLength(8192)] public string? Description { get; set; }
        [MaxLength(256)] public string? Passcode { get; set; }

        public Instant? ExpiredAt { get; set; }
    }

    [HttpGet("{id:guid}")]
    public async Task<ActionResult<FileBundle>> GetBundle([FromRoute] Guid id, [FromQuery] string? passcode)
    {
        var bundle = await db.Bundles
            .Where(e => e.Id == id)
            .Include(e => e.Files)
            .FirstOrDefaultAsync();
        if (bundle is null) return NotFound();
        if (!bundle.VerifyPasscode(passcode)) return Forbid();

        return Ok(bundle);
    }

    [HttpGet("me")]
    [Authorize]
    public async Task<ActionResult<List<FileBundle>>> ListBundles(
        [FromQuery] string? term,
        [FromQuery] int offset = 0,
        [FromQuery] int take = 20
    )
    {
        if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized();
        var accountId = Guid.Parse(currentUser.Id);

        var query = db.Bundles
            .Where(e => e.AccountId == accountId)
            .OrderByDescending(e => e.CreatedAt)
            .AsQueryable();
        if (!string.IsNullOrEmpty(term))
            query = query.Where(e => EF.Functions.ILike(e.Name, $"%{term}%"));

        var total = await query.CountAsync();
        Response.Headers.Append("X-Total", total.ToString());

        var bundles = await query
            .Skip(offset)
            .Take(take)
            .ToListAsync();

        return Ok(bundles);
    }

    [HttpPost]
    [Authorize]
    public async Task<ActionResult<FileBundle>> CreateBundle([FromBody] BundleRequest request)
    {
        if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized();
        var accountId = Guid.Parse(currentUser.Id);

        if (currentUser.PerkSubscription is null && !string.IsNullOrEmpty(request.Slug))
            return StatusCode(403, "You must have a subscription to create a bundle with a custom slug");
        if (string.IsNullOrEmpty(request.Slug))
            request.Slug = Guid.NewGuid().ToString("N")[..6];
        if (string.IsNullOrEmpty(request.Name))
            request.Name = "Unnamed Bundle";

        var bundle = new FileBundle
        {
            Slug = request.Slug,
            Name = request.Name,
            Description = request.Description,
            Passcode = request.Passcode,
            ExpiredAt = request.ExpiredAt,
            AccountId = accountId
        }.HashPasscode();

        db.Bundles.Add(bundle);
        await db.SaveChangesAsync();

        return Ok(bundle);
    }

    [HttpPut("{id:guid}")]
    [Authorize]
    public async Task<ActionResult<FileBundle>> UpdateBundle([FromRoute] Guid id, [FromBody] BundleRequest request)
    {
        if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized();
        var accountId = Guid.Parse(currentUser.Id);

        var bundle = await db.Bundles
            .Where(e => e.Id == id)
            .Where(e => e.AccountId == accountId)
            .FirstOrDefaultAsync();
        if (bundle is null) return NotFound();

        if (request.Slug != null && request.Slug != bundle.Slug)
        {
            if (currentUser.PerkSubscription is null)
                return StatusCode(403, "You must have a subscription to change the slug of a bundle");
            bundle.Slug = request.Slug;
        }

        if (request.Name != null) bundle.Name = request.Name;
        if (request.Description != null) bundle.Description = request.Description;
        if (request.ExpiredAt != null) bundle.ExpiredAt = request.ExpiredAt;

        if (request.Passcode != null)
        {
            bundle.Passcode = request.Passcode;
            bundle = bundle.HashPasscode();
        }

        await db.SaveChangesAsync();

        return Ok(bundle);
    }

    [HttpDelete("{id:guid}")]
    [Authorize]
    public async Task<ActionResult> DeleteBundle([FromRoute] Guid id)
    {
        if (HttpContext.Items["CurrentUser"] is not Account currentUser) return Unauthorized();
        var accountId = Guid.Parse(currentUser.Id);

        var bundle = await db.Bundles
            .Where(e => e.Id == id)
            .Where(e => e.AccountId == accountId)
            .FirstOrDefaultAsync();
        if (bundle is null) return NotFound();

        db.Bundles.Remove(bundle);
        await db.SaveChangesAsync();

        await db.Files
            .Where(e => e.BundleId == id)
            .ExecuteUpdateAsync(s => s.SetProperty(e => e.IsMarkedRecycle, true));

        return NoContent();
    }
}
