using JobTrackerApi.Data;
using JobTrackerApi.Models;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;

namespace JobTrackerApi.Controllers;

[ApiController]
[Route("api/ai/usage")]
[Authorize(AuthenticationSchemes = "local")]
public sealed class AiUsageController : ControllerBase
{
    private readonly UserManager<ApplicationUser> _users;
    private readonly JobTrackerContext _db;

    public AiUsageController(UserManager<ApplicationUser> users, JobTrackerContext db)
    {
        _users = users;
        _db = db;
    }

    public sealed record UsagePeriodDto(int Calls, long InputCharacters, long OutputCharacters, long EstimatedTokens);
    public sealed record UsageDto(UsagePeriodDto CurrentMonth, UsagePeriodDto AllTime, string Plan, int MonthlyCallLimit, long MonthlyTokenLimit, long StorageUsedBytes, long StorageLimitBytes);

    [HttpGet]
    public async Task<ActionResult<UsageDto>> Get(CancellationToken cancellationToken)
    {
        var user = await _users.GetUserAsync(User);
        if (user is null) return Unauthorized();

        var roles = await _users.GetRolesAsync(user);
        var entitlements = AccountPlans.ForRoles(roles);
        var monthStart = new DateTimeOffset(DateTime.UtcNow.Year, DateTime.UtcNow.Month, 1, 0, 0, 0, TimeSpan.Zero);
        var interactions = _db.AiInteractions.Where(x => x.OwnerUserId == user.Id);
        var currentMonth = _db.Database.IsSqlite()
            ? Sum((await interactions.ToListAsync(cancellationToken)).Where(x => x.CreatedAtUtc >= monthStart))
            : await SumAsync(interactions.Where(x => x.CreatedAtUtc >= monthStart), cancellationToken);
        return Ok(new UsageDto(
            currentMonth,
            await SumAsync(interactions, cancellationToken),
            AccountPlans.Name(entitlements),
            entitlements.MonthlyAiCalls,
            entitlements.MonthlyAiTokens,
            await _db.Attachments.Where(x => x.JobApplication.OwnerUserId == user.Id).SumAsync(x => (long?)x.FileSize, cancellationToken) ?? 0,
            entitlements.StorageBytes));
    }

    private static async Task<UsagePeriodDto> SumAsync(IQueryable<AiInteraction> query, CancellationToken cancellationToken)
    {
        var totals = await query.GroupBy(_ => 1).Select(group => new UsagePeriodDto(
            group.Count(),
            group.Sum(x => (long)x.InputCharacterCount),
            group.Sum(x => (long)x.OutputCharacterCount),
            group.Sum(x => (long)x.EstimatedTokenCount))).FirstOrDefaultAsync(cancellationToken);
        return totals ?? new UsagePeriodDto(0, 0, 0, 0);
    }

    private static UsagePeriodDto Sum(IEnumerable<AiInteraction> interactions)
    {
        var rows = interactions.ToList();
        return new UsagePeriodDto(
            rows.Count,
            rows.Sum(x => (long)x.InputCharacterCount),
            rows.Sum(x => (long)x.OutputCharacterCount),
            rows.Sum(x => (long)x.EstimatedTokenCount));
    }
}
