using Microsoft.EntityFrameworkCore;
using ScadaLink.Commons.Entities.Audit;
using ScadaLink.Commons.Entities.Deployment;
using ScadaLink.Commons.Entities.Instances;
using ScadaLink.Commons.Entities.Sites;
using ScadaLink.Commons.Entities.Templates;
using ScadaLink.Commons.Interfaces.Repositories;

namespace ScadaLink.ConfigurationDatabase.Repositories;

public class CentralUiRepository : ICentralUiRepository
{
    private readonly ScadaLinkDbContext _context;

    public CentralUiRepository(ScadaLinkDbContext context)
    {
        _context = context ?? throw new ArgumentNullException(nameof(context));
    }

    public async Task<IReadOnlyList<Site>> GetAllSitesAsync(CancellationToken cancellationToken = default)
    {
        return await _context.Sites
            .AsNoTracking()
            .OrderBy(s => s.Name)
            .ToListAsync(cancellationToken);
    }

    public async Task<IReadOnlyList<DataConnection>> GetDataConnectionsBySiteIdAsync(int siteId, CancellationToken cancellationToken = default)
    {
        return await _context.DataConnections
            .AsNoTracking()
            .Where(d => d.SiteId == siteId)
            .OrderBy(d => d.Name)
            .ToListAsync(cancellationToken);
    }

    public async Task<IReadOnlyList<DataConnection>> GetAllDataConnectionsAsync(CancellationToken cancellationToken = default)
    {
        return await _context.DataConnections
            .AsNoTracking()
            .OrderBy(d => d.Name)
            .ToListAsync(cancellationToken);
    }

    public async Task<IReadOnlyList<Template>> GetTemplateTreeAsync(CancellationToken cancellationToken = default)
    {
        return await _context.Templates
            .AsNoTracking()
            .Include(t => t.Attributes)
            .Include(t => t.Alarms)
            .Include(t => t.Scripts)
            .Include(t => t.Compositions)
            .OrderBy(t => t.Name)
            .ToListAsync(cancellationToken);
    }

    public async Task<IReadOnlyList<Instance>> GetInstancesFilteredAsync(
        int? siteId = null,
        int? templateId = null,
        string? searchTerm = null,
        CancellationToken cancellationToken = default)
    {
        var query = _context.Instances.AsNoTracking().AsQueryable();

        if (siteId.HasValue)
            query = query.Where(i => i.SiteId == siteId.Value);

        if (templateId.HasValue)
            query = query.Where(i => i.TemplateId == templateId.Value);

        if (!string.IsNullOrWhiteSpace(searchTerm))
            query = query.Where(i => i.UniqueName.Contains(searchTerm));

        return await query
            .OrderBy(i => i.UniqueName)
            .ToListAsync(cancellationToken);
    }

    public async Task<IReadOnlyList<DeploymentRecord>> GetRecentDeploymentsAsync(int count, CancellationToken cancellationToken = default)
    {
        return await _context.DeploymentRecords
            .AsNoTracking()
            .OrderByDescending(d => d.DeployedAt)
            .Take(count)
            .ToListAsync(cancellationToken);
    }

    public async Task<IReadOnlyList<Area>> GetAreaTreeBySiteIdAsync(int siteId, CancellationToken cancellationToken = default)
    {
        return await _context.Areas
            .AsNoTracking()
            .Where(a => a.SiteId == siteId)
            .Include(a => a.Children)
            .OrderBy(a => a.Name)
            .ToListAsync(cancellationToken);
    }

    public async Task<(IReadOnlyList<AuditLogEntry> Entries, int TotalCount)> GetAuditLogEntriesAsync(
        string? user = null,
        string? entityType = null,
        string? action = null,
        DateTimeOffset? from = null,
        DateTimeOffset? to = null,
        string? entityId = null,
        string? entityName = null,
        int page = 1,
        int pageSize = 50,
        CancellationToken cancellationToken = default)
    {
        var query = _context.AuditLogEntries.AsNoTracking().AsQueryable();

        if (!string.IsNullOrWhiteSpace(user))
            query = query.Where(a => a.User == user);

        if (!string.IsNullOrWhiteSpace(entityType))
            query = query.Where(a => a.EntityType == entityType);

        if (!string.IsNullOrWhiteSpace(action))
            query = query.Where(a => a.Action == action);

        if (from.HasValue)
            query = query.Where(a => a.Timestamp >= from.Value);

        if (to.HasValue)
            query = query.Where(a => a.Timestamp <= to.Value);

        if (!string.IsNullOrWhiteSpace(entityId))
            query = query.Where(a => a.EntityId == entityId);

        if (!string.IsNullOrWhiteSpace(entityName))
            query = query.Where(a => a.EntityName.Contains(entityName));

        var totalCount = await query.CountAsync(cancellationToken);

        var entries = await query
            .OrderByDescending(a => a.Timestamp)
            .Skip((page - 1) * pageSize)
            .Take(pageSize)
            .ToListAsync(cancellationToken);

        return (entries, totalCount);
    }

    public async Task<int> SaveChangesAsync(CancellationToken cancellationToken = default)
    {
        return await _context.SaveChangesAsync(cancellationToken);
    }
}
