using Microsoft.EntityFrameworkCore;
using PowderCoating.Core.Entities;
using PowderCoating.Core.Enums;
using PowderCoating.Core.Interfaces.Repositories;
using PowderCoating.Infrastructure.Data;

namespace PowderCoating.Infrastructure.Repositories;

/// <summary>
/// Typed repository for <see cref="PurchaseOrder"/> that provides domain-specific queries
/// previously expressed inline in <c>PurchaseOrdersController</c>.
/// </summary>
public class PurchaseOrderRepository : Repository<PurchaseOrder>, IPurchaseOrderRepository
{
    public PurchaseOrderRepository(ApplicationDbContext context) : base(context) { }

    /// <inheritdoc/>
    public async Task<PurchaseOrder?> LoadForViewAsync(int id, int companyId)
    {
        return await _context.Set<PurchaseOrder>()
            .Where(p => p.Id == id && !p.IsDeleted && p.CompanyId == companyId)
            .Include(p => p.Vendor)
            .Include(p => p.Bill)
            .Include(p => p.Items.Where(i => !i.IsDeleted))
                .ThenInclude(i => i.InventoryItem)
            .FirstOrDefaultAsync();
    }

    /// <inheritdoc/>
    public async Task<PurchaseOrderStats> GetStatsAsync(int companyId)
    {
        var today = DateTime.UtcNow.Date;
        var items = await _context.Set<PurchaseOrder>()
            .Where(po => !po.IsDeleted && po.CompanyId == companyId)
            .Select(po => new { po.Status, po.TotalAmount, po.ExpectedDeliveryDate })
            .ToListAsync();

        return new PurchaseOrderStats(
            TotalCount: items.Count,
            OpenCount: items.Count(p =>
                p.Status == PurchaseOrderStatus.Draft ||
                p.Status == PurchaseOrderStatus.Submitted ||
                p.Status == PurchaseOrderStatus.PartiallyReceived),
            CommittedValue: items.Where(p => p.Status != PurchaseOrderStatus.Cancelled).Sum(p => p.TotalAmount),
            OverdueCount: items.Count(p =>
                (p.Status == PurchaseOrderStatus.Draft || p.Status == PurchaseOrderStatus.Submitted || p.Status == PurchaseOrderStatus.PartiallyReceived) &&
                p.ExpectedDeliveryDate.HasValue &&
                p.ExpectedDeliveryDate.Value.Date < today)
        );
    }

    /// <inheritdoc/>
    public async Task<(List<PurchaseOrder> Items, int TotalCount)> GetPagedAsync(
        int companyId,
        int pageNumber,
        int pageSize,
        PurchaseOrderStatus? statusFilter = null,
        int? vendorId = null,
        DateTime? dateFrom = null,
        DateTime? dateTo = null,
        string? searchTerm = null,
        string? sortColumn = null,
        string? sortDirection = null)
    {
        var query = _context.Set<PurchaseOrder>()
            .Include(po => po.Vendor)
            .Include(po => po.Items.Where(i => !i.IsDeleted))
            .Where(po => !po.IsDeleted && po.CompanyId == companyId)
            .AsQueryable();

        if (statusFilter.HasValue)
            query = query.Where(po => po.Status == statusFilter.Value);

        if (vendorId.HasValue)
            query = query.Where(po => po.VendorId == vendorId.Value);

        if (dateFrom.HasValue)
            query = query.Where(po => po.OrderDate >= dateFrom.Value);

        if (dateTo.HasValue)
            query = query.Where(po => po.OrderDate <= dateTo.Value.AddDays(1));

        if (!string.IsNullOrWhiteSpace(searchTerm))
        {
            var term = searchTerm.Trim().ToLower();
            query = query.Where(po =>
                po.PoNumber.ToLower().Contains(term) ||
                po.Vendor.CompanyName.ToLower().Contains(term) ||
                (po.Notes != null && po.Notes.ToLower().Contains(term)));
        }

        query = (sortColumn?.ToLower(), sortDirection?.ToLower()) switch
        {
            ("ponumber", "asc")  => query.OrderBy(po => po.PoNumber),
            ("ponumber", _)      => query.OrderByDescending(po => po.PoNumber),
            ("vendor", "asc")    => query.OrderBy(po => po.Vendor.CompanyName),
            ("vendor", _)        => query.OrderByDescending(po => po.Vendor.CompanyName),
            ("status", "asc")    => query.OrderBy(po => po.Status),
            ("status", _)        => query.OrderByDescending(po => po.Status),
            ("orderdate", "asc") => query.OrderBy(po => po.OrderDate),
            ("orderdate", _)     => query.OrderByDescending(po => po.OrderDate),
            ("expected", "asc")  => query.OrderBy(po => po.ExpectedDeliveryDate),
            ("expected", _)      => query.OrderByDescending(po => po.ExpectedDeliveryDate),
            ("total", "asc")     => query.OrderBy(po => po.TotalAmount),
            ("total", _)         => query.OrderByDescending(po => po.TotalAmount),
            _                    => query.OrderByDescending(po => po.OrderDate)
        };

        var totalCount = await query.CountAsync();
        var items = await query
            .Skip((pageNumber - 1) * pageSize)
            .Take(pageSize)
            .ToListAsync();

        return (items, totalCount);
    }
}
