#nullable enable
using Baya.Application.Contracts.Persistence;
using Baya.Application.Models.Catalog;
using Baya.Domain.Entities.Catalog;
using Microsoft.EntityFrameworkCore;

namespace Baya.Infrastructure.Persistence.Repositories;

internal sealed class CatalogRepository : ICatalogRepository
{
    private readonly ApplicationDbContext _db;

    public CatalogRepository(ApplicationDbContext db) => _db = db;

    public async Task<IReadOnlyList<ServiceCategoryDto>> ListActiveCategoriesAsync(CancellationToken cancellationToken)
        => await _db.Set<ServiceCategory>()
            .AsNoTracking()
            .Where(c => c.IsActive)
            .OrderBy(c => c.SortOrder)
            .ThenBy(c => c.Id)
            .Select(c => new ServiceCategoryDto(
                c.Id, c.NameFa, c.NameEn, c.DescriptionFa, c.DescriptionEn, c.IconKey, c.SortOrder, c.IsActive))
            .ToListAsync(cancellationToken);

    public async Task<IReadOnlyList<OptionGroupDto>> GetApplicableGroupsAsync(long categoryId, CancellationToken cancellationToken)
        => await _db.Set<ServiceOptionGroup>()
            .AsNoTracking()
            // The category's own active groups PLUS every cross-category (NULL) active group.
            .Where(g => g.IsActive && (g.ServiceCategoryId == categoryId || g.ServiceCategoryId == null))
            .OrderBy(g => g.SortOrder)
            .ThenBy(g => g.Id)
            .Select(g => new OptionGroupDto(
                g.Id,
                g.ServiceCategoryId,
                g.NameFa,
                g.NameEn,
                g.IsRequired,
                g.SortOrder,
                g.IsActive,
                g.Values
                    .Where(v => v.IsActive)
                    .OrderBy(v => v.SortOrder)
                    .ThenBy(v => v.Id)
                    .Select(v => new OptionValueDto(v.Id, v.NameFa, v.NameEn, v.SortOrder, v.IsActive))
                    .ToList()))
            .ToListAsync(cancellationToken);

    public Task<ServiceCategoryDto?> GetActiveCategoryAsync(long id, CancellationToken cancellationToken)
        => _db.Set<ServiceCategory>()
            .AsNoTracking()
            .Where(c => c.Id == id && c.IsActive)
            .Select(c => new ServiceCategoryDto(
                c.Id, c.NameFa, c.NameEn, c.DescriptionFa, c.DescriptionEn, c.IconKey, c.SortOrder, c.IsActive))
            .FirstOrDefaultAsync(cancellationToken);

    public Task<OptionGroupDto?> GetGroupDtoAsync(long id, CancellationToken cancellationToken)
        => _db.Set<ServiceOptionGroup>()
            .AsNoTracking()
            .Where(g => g.Id == id)
            .Select(g => new OptionGroupDto(
                g.Id,
                g.ServiceCategoryId,
                g.NameFa,
                g.NameEn,
                g.IsRequired,
                g.SortOrder,
                g.IsActive,
                g.Values
                    .Where(v => v.IsActive)
                    .OrderBy(v => v.SortOrder)
                    .ThenBy(v => v.Id)
                    .Select(v => new OptionValueDto(v.Id, v.NameFa, v.NameEn, v.SortOrder, v.IsActive))
                    .ToList()))
            .FirstOrDefaultAsync(cancellationToken);

    public Task<ServiceCategory?> GetCategoryAsync(long id, CancellationToken cancellationToken)
        => _db.Set<ServiceCategory>().FirstOrDefaultAsync(c => c.Id == id, cancellationToken);

    public Task<ServiceOptionGroup?> GetGroupAsync(long id, CancellationToken cancellationToken)
        => _db.Set<ServiceOptionGroup>().FirstOrDefaultAsync(g => g.Id == id, cancellationToken);

    public Task<ServiceOptionValue?> GetValueAsync(long id, CancellationToken cancellationToken)
        => _db.Set<ServiceOptionValue>().FirstOrDefaultAsync(v => v.Id == id, cancellationToken);

    public Task<bool> CategoryExistsAsync(long id, CancellationToken cancellationToken)
        => _db.Set<ServiceCategory>().AsNoTracking().AnyAsync(c => c.Id == id, cancellationToken);

    public Task<bool> GroupExistsAsync(long id, CancellationToken cancellationToken)
        => _db.Set<ServiceOptionGroup>().AsNoTracking().AnyAsync(g => g.Id == id, cancellationToken);

    public async Task AddCategoryAsync(ServiceCategory category, CancellationToken cancellationToken)
        => await _db.Set<ServiceCategory>().AddAsync(category, cancellationToken);

    public async Task AddGroupAsync(ServiceOptionGroup group, CancellationToken cancellationToken)
        => await _db.Set<ServiceOptionGroup>().AddAsync(group, cancellationToken);

    public async Task AddValueAsync(ServiceOptionValue value, CancellationToken cancellationToken)
        => await _db.Set<ServiceOptionValue>().AddAsync(value, cancellationToken);
}
