using Meezi.API.Models.Taxes;
using Meezi.Core.Entities;
using Meezi.Infrastructure.Data;
using Microsoft.EntityFrameworkCore;

namespace Meezi.API.Services;

public interface ITaxService
{
    Task<IReadOnlyList<TaxDto>> GetAllAsync(string cafeId, CancellationToken cancellationToken = default);
    Task<TaxDto?> CreateAsync(string cafeId, CreateTaxRequest request, CancellationToken cancellationToken = default);
    Task<TaxDto?> UpdateAsync(string cafeId, string id, UpdateTaxRequest request, CancellationToken cancellationToken = default);
    Task<bool> DeleteAsync(string cafeId, string id, CancellationToken cancellationToken = default);
}

public class TaxService : ITaxService
{
    private readonly AppDbContext _db;

    public TaxService(AppDbContext db) => _db = db;

    public async Task<IReadOnlyList<TaxDto>> GetAllAsync(string cafeId, CancellationToken cancellationToken = default)
    {
        return await _db.Taxes
            .Where(t => t.CafeId == cafeId)
            .OrderBy(t => t.Name)
            .Select(t => ToDto(t))
            .ToListAsync(cancellationToken);
    }

    public async Task<TaxDto?> CreateAsync(string cafeId, CreateTaxRequest request, CancellationToken cancellationToken = default)
    {
        if (request.IsDefault)
        {
            var existing = await _db.Taxes.Where(t => t.CafeId == cafeId && t.IsDefault).ToListAsync(cancellationToken);
            foreach (var t in existing) t.IsDefault = false;
        }

        var entity = new Tax
        {
            CafeId = cafeId,
            Name = request.Name,
            Rate = request.Rate,
            IsDefault = request.IsDefault,
            IsRequired = request.IsRequired,
            IsCompound = request.IsCompound
        };
        _db.Taxes.Add(entity);
        await _db.SaveChangesAsync(cancellationToken);
        return ToDto(entity);
    }

    public async Task<TaxDto?> UpdateAsync(
        string cafeId,
        string id,
        UpdateTaxRequest request,
        CancellationToken cancellationToken = default)
    {
        var entity = await _db.Taxes.FirstOrDefaultAsync(t => t.Id == id && t.CafeId == cafeId, cancellationToken);
        if (entity is null) return null;

        if (request.Name is not null) entity.Name = request.Name;
        if (request.Rate.HasValue) entity.Rate = request.Rate.Value;
        if (request.IsDefault == true)
        {
            var others = await _db.Taxes.Where(t => t.CafeId == cafeId && t.Id != id).ToListAsync(cancellationToken);
            foreach (var t in others) t.IsDefault = false;
            entity.IsDefault = true;
        }
        else if (request.IsDefault.HasValue) entity.IsDefault = request.IsDefault.Value;

        if (request.IsRequired.HasValue) entity.IsRequired = request.IsRequired.Value;
        if (request.IsCompound.HasValue) entity.IsCompound = request.IsCompound.Value;

        await _db.SaveChangesAsync(cancellationToken);
        return ToDto(entity);
    }

    public async Task<bool> DeleteAsync(string cafeId, string id, CancellationToken cancellationToken = default)
    {
        var entity = await _db.Taxes.FirstOrDefaultAsync(t => t.Id == id && t.CafeId == cafeId, cancellationToken);
        if (entity is null) return false;

        var wasDefault = entity.IsDefault;
        entity.DeletedAt = DateTime.UtcNow;
        entity.IsDefault = false;

        var categories = await _db.MenuCategories
            .Where(c => c.CafeId == cafeId && c.TaxId == id)
            .ToListAsync(cancellationToken);

        Tax? replacementDefault = null;
        if (wasDefault)
        {
            replacementDefault = await _db.Taxes
                .Where(t => t.CafeId == cafeId && t.Id != id)
                .OrderBy(t => t.Name)
                .FirstOrDefaultAsync(cancellationToken);
            if (replacementDefault is not null)
                replacementDefault.IsDefault = true;
        }

        foreach (var category in categories)
            category.TaxId = replacementDefault?.Id;

        await _db.SaveChangesAsync(cancellationToken);
        return true;
    }

    private static TaxDto ToDto(Tax t) => new(t.Id, t.Name, t.Rate, t.IsDefault, t.IsRequired, t.IsCompound);
}
