using Microsoft.EntityFrameworkCore;
using ROLAC.API.Data.Logging;
using ROLAC.API.DTOs.Logging;
using ROLAC.API.DTOs.Shared;
using ROLAC.API.Entities.Logging;

namespace ROLAC.API.Services.Logging;

public interface IAuditLogQueryService
{
    Task<PagedResult<AuditLogListItemDto>> GetPagedAsync(AuditLogQuery query);
    Task<AuditLogDetailDto?> GetByIdAsync(long id);
    AuditCatalogDto GetCatalog();
}

/// <summary>Read-only, paged access to the AuditLogs table via the dedicated LogDbContext.</summary>
public sealed class AuditLogQueryService : IAuditLogQueryService
{
    private readonly LogDbContext _db;

    public AuditLogQueryService(LogDbContext db) => _db = db;

    public async Task<PagedResult<AuditLogListItemDto>> GetPagedAsync(AuditLogQuery query)
    {
        var page     = Math.Max(1, query.Page);
        var pageSize = Math.Clamp(query.PageSize, 1, 200);

        var rows = _db.AuditLogs.AsNoTracking().AsQueryable();

        if (query.From is not null)     rows = rows.Where(l => l.Timestamp >= query.From);
        if (query.To   is not null)     rows = rows.Where(l => l.Timestamp <= query.To);
        if (query.MinLevel is not null) rows = rows.Where(l => l.Level >= query.MinLevel);
        if (!string.IsNullOrWhiteSpace(query.Category))   rows = rows.Where(l => l.Category == query.Category);
        if (!string.IsNullOrWhiteSpace(query.Action))     rows = rows.Where(l => l.Action == query.Action);
        if (!string.IsNullOrWhiteSpace(query.EntityName)) rows = rows.Where(l => l.EntityName == query.EntityName);
        if (!string.IsNullOrWhiteSpace(query.EntityId))   rows = rows.Where(l => l.EntityId == query.EntityId);
        if (!string.IsNullOrWhiteSpace(query.UserId))     rows = rows.Where(l => l.UserId == query.UserId);
        if (!string.IsNullOrWhiteSpace(query.Search))
        {
            var term = query.Search.Trim().ToLower();
            rows = rows.Where(l =>
                (l.Summary != null && l.Summary.ToLower().Contains(term)) ||
                (l.EntityName != null && l.EntityName.ToLower().Contains(term)) ||
                (l.UserEmail != null && l.UserEmail.ToLower().Contains(term)));
        }

        var total = await rows.CountAsync();
        var items = await rows
            .OrderByDescending(l => l.Timestamp)
            .Skip((page - 1) * pageSize).Take(pageSize)
            .Select(l => new AuditLogListItemDto
            {
                Id         = l.Id,
                Timestamp  = l.Timestamp,
                Level      = l.Level.ToString(),
                Action     = l.Action,
                Category   = l.Category,
                EntityName = l.EntityName,
                EntityId   = l.EntityId,
                Summary    = l.Summary,
                UserId     = l.UserId,
                UserEmail  = l.UserEmail,
            })
            .ToListAsync();

        return new PagedResult<AuditLogListItemDto>
        {
            Items = items, TotalCount = total, Page = page, PageSize = pageSize,
        };
    }

    public async Task<AuditLogDetailDto?> GetByIdAsync(long id)
    {
        return await _db.AuditLogs.AsNoTracking()
            .Where(l => l.Id == id)
            .Select(l => new AuditLogDetailDto
            {
                Id            = l.Id,
                Timestamp     = l.Timestamp,
                Level         = l.Level.ToString(),
                Action        = l.Action,
                Category      = l.Category,
                EntityName    = l.EntityName,
                EntityId      = l.EntityId,
                Summary       = l.Summary,
                UserId        = l.UserId,
                UserEmail     = l.UserEmail,
                Changes       = l.Changes,
                IpAddress     = l.IpAddress,
                CorrelationId = l.CorrelationId,
            })
            .FirstOrDefaultAsync();
    }

    public AuditCatalogDto GetCatalog() => new()
    {
        Categories = AuditCategories.All,
        Actions    = AuditActions.All,
        Levels     = Enum.GetNames<LogLevelEnum>(),
    };
}
