using GreenHome.Application; using GreenHome.Domain; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; namespace GreenHome.Infrastructure; public sealed class AIQueryService : IAIQueryService { private readonly GreenHomeDbContext dbContext; private readonly ILogger logger; public AIQueryService( GreenHomeDbContext dbContext, ILogger logger) { this.dbContext = dbContext; this.logger = logger; } public async Task SaveQueryAsync(AIQuery query, CancellationToken cancellationToken = default) { try { dbContext.AIQueries.Add(query); await dbContext.SaveChangesAsync(cancellationToken); logger.LogInformation("AI query saved: {QueryId}, Tokens: {TotalTokens}", query.Id, query.TotalTokens); return query; } catch (Exception ex) { logger.LogError(ex, "Error saving AI query"); throw; } } public async Task> GetDeviceQueriesAsync( int deviceId, int take = 50, CancellationToken cancellationToken = default) { return await dbContext.AIQueries .Where(q => q.DeviceId == deviceId) .OrderByDescending(q => q.CreatedAt) .Take(take) .ToListAsync(cancellationToken); } public async Task> GetUserQueriesAsync( int userId, int take = 50, CancellationToken cancellationToken = default) { return await dbContext.AIQueries .Where(q => q.UserId == userId) .OrderByDescending(q => q.CreatedAt) .Take(take) .ToListAsync(cancellationToken); } public async Task GetDeviceTotalTokensAsync( int deviceId, CancellationToken cancellationToken = default) { return await dbContext.AIQueries .Where(q => q.DeviceId == deviceId) .SumAsync(q => q.TotalTokens, cancellationToken); } public async Task GetUserTotalTokensAsync( int userId, CancellationToken cancellationToken = default) { return await dbContext.AIQueries .Where(q => q.UserId == userId) .SumAsync(q => q.TotalTokens, cancellationToken); } public async Task> GetRecentQueriesAsync( int take = 20, CancellationToken cancellationToken = default) { return await dbContext.AIQueries .Include(q => q.Device) .Include(q => q.User) .OrderByDescending(q => q.CreatedAt) .Take(take) .ToListAsync(cancellationToken); } public async Task GetStatsAsync(CancellationToken cancellationToken = default) { var today = DateTime.UtcNow.Date; var allQueries = await dbContext.AIQueries .Select(q => new { q.TotalTokens, q.PromptTokens, q.CompletionTokens, q.ResponseTimeMs, q.CreatedAt }) .ToListAsync(cancellationToken); var todayQueries = allQueries.Where(q => q.CreatedAt >= today).ToList(); return new AIQueryStats { TotalQueries = allQueries.Count, TotalTokensUsed = allQueries.Sum(q => q.TotalTokens), TotalPromptTokens = allQueries.Sum(q => q.PromptTokens), TotalCompletionTokens = allQueries.Sum(q => q.CompletionTokens), AverageResponseTimeMs = allQueries.Any() ? allQueries.Where(q => q.ResponseTimeMs.HasValue) .Average(q => q.ResponseTimeMs ?? 0) : 0, TodayQueries = todayQueries.Count, TodayTokens = todayQueries.Sum(q => q.TotalTokens) }; } }