using System.Net;
using System.Net.Mime;
using Iceshrimp.Backend.Controllers.Shared.Attributes;
using Iceshrimp.Backend.Core.Database;
using Iceshrimp.Backend.Core.Middleware;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.RateLimiting;
using Microsoft.EntityFrameworkCore;
using static Iceshrimp.Shared.Schemas.Web.SessionSchemas;

namespace Iceshrimp.Backend.Controllers.Web;

[ApiController]
[Authenticate]
[Authorize]
[Tags("Session")]
[EnableRateLimiting("sliding")]
[Produces(MediaTypeNames.Application.Json)]
[Route("/api/iceshrimp/sessions")]
public class SessionController(DatabaseContext db) : ControllerBase
{
	[HttpGet]
	[ProducesResults(HttpStatusCode.OK)]
	public async Task<List<SessionResponse>> GetSessions(int page = 0)
	{
		const int pageSize  = 20;
		var       currentId = HttpContext.GetSessionOrFail().Id;

		return await db.Sessions
		               .Where(p => p.User == HttpContext.GetUserOrFail())
		               .OrderByDescending(p => p.LastActiveDate ?? p.CreatedAt)
		               .Skip(page * pageSize)
		               .Take(pageSize)
		               .Select(p => new SessionResponse
		               {
			               Id         = p.Id,
			               Current    = p.Id == currentId,
			               Active     = p.Active,
			               CreatedAt  = p.CreatedAt,
			               LastActive = p.LastActiveDate
		               })
		               .ToListAsync();
	}

	[HttpDelete("{id}")]
	[ProducesResults(HttpStatusCode.OK)]
	[ProducesErrors(HttpStatusCode.BadRequest, HttpStatusCode.NotFound)]
	public async Task TerminateSession(string id)
	{
		var user = HttpContext.GetUserOrFail();
		var session = await db.Sessions.FirstOrDefaultAsync(p => p.Id == id && p.User == user)
		              ?? throw GracefulException.NotFound("Session not found");

		if (session.Id == HttpContext.GetSessionOrFail().Id)
			throw GracefulException.BadRequest("Refusing to terminate current session");

		db.Remove(session);
		await db.SaveChangesAsync();
	}

	[HttpGet("mastodon")]
	[ProducesResults(HttpStatusCode.OK)]
	public async Task<List<MastodonSessionResponse>> GetMastodonSessions(int page = 0)
	{
		const int pageSize = 20;

		return await db.OauthTokens
		               .Include(p => p.App)
		               .Where(p => p.User == HttpContext.GetUserOrFail())
		               .OrderByDescending(p => p.LastActiveDate ?? p.CreatedAt)
		               .Skip(page * pageSize)
		               .Take(pageSize)
		               .Select(p => new MastodonSessionResponse
		               {
			               Id         = p.Id,
			               Active     = p.Active,
			               CreatedAt  = p.CreatedAt,
			               LastActive = p.LastActiveDate,
			               App        = p.App.Name,
			               Scopes     = p.Scopes,
			               Flags = new MastodonSessionFlags
			               {
				               SupportsHtmlFormatting = p.SupportsHtmlFormatting,
				               AutoDetectQuotes       = p.AutoDetectQuotes,
				               IsPleroma              = p.IsPleroma,
				               SupportsInlineMedia    = p.SupportsInlineMedia
			               }
		               })
		               .ToListAsync();
	}

	[HttpPatch("mastodon/{id}")]
	[ProducesResults(HttpStatusCode.OK)]
	[ProducesErrors(HttpStatusCode.BadRequest, HttpStatusCode.NotFound)]
	public async Task UpdateMastodonSession(string id, [FromBody] MastodonSessionFlags flags)
	{
		var user = HttpContext.GetUserOrFail();
		var token = await db.OauthTokens.FirstOrDefaultAsync(p => p.Id == id && p.User == user)
		            ?? throw GracefulException.NotFound("Session not found");

		token.SupportsHtmlFormatting = flags.SupportsHtmlFormatting;
		token.AutoDetectQuotes       = flags.AutoDetectQuotes;
		token.IsPleroma              = flags.IsPleroma;
		token.SupportsInlineMedia    = flags.SupportsInlineMedia;

		await db.SaveChangesAsync();
	}

	[HttpDelete("mastodon/{id}")]
	[ProducesResults(HttpStatusCode.OK)]
	[ProducesErrors(HttpStatusCode.BadRequest, HttpStatusCode.NotFound)]
	public async Task TerminateMastodonSession(string id)
	{
		var user = HttpContext.GetUserOrFail();
		var token = await db.OauthTokens.FirstOrDefaultAsync(p => p.Id == id && p.User == user)
		            ?? throw GracefulException.NotFound("Session not found");

		db.Remove(token);
		await db.SaveChangesAsync();
	}
}
