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 _users; private readonly JobTrackerContext _db; public AiUsageController(UserManager 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> 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); return Ok(new UsageDto( await SumAsync(_db.AiInteractions.Where(x => x.OwnerUserId == user.Id && x.CreatedAtUtc >= monthStart), cancellationToken), await SumAsync(_db.AiInteractions.Where(x => x.OwnerUserId == user.Id), cancellationToken), entitlements.AdvancedAi ? "premium" : "free", 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 SumAsync(IQueryable 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); } }