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

namespace PowderCoating.Infrastructure.Services;

/// <summary>
/// Implements <see cref="ICompanyListService"/> using <see cref="ApplicationDbContext"/> directly.
/// Queries require <c>IgnoreQueryFilters()</c> (to bypass the tenant filter and see all companies),
/// dynamic sort expressions, and cross-entity GROUP BY aggregations — all of which are beyond the
/// generic <see cref="PowderCoating.Core.Interfaces.IRepository{T}"/>.
/// </summary>
public class CompanyListService : ICompanyListService
{
    private readonly ApplicationDbContext _context;

    public CompanyListService(ApplicationDbContext context)
    {
        _context = context;
    }

    /// <inheritdoc/>
    public async Task<(List<Company> Companies, int TotalCount, int ChurnedCount)> GetPagedAsync(
        string? searchTerm, string sortColumn, string sortDirection, int page, int pageSize,
        bool hideChurned = true)
    {
        var cutoff = DateTime.UtcNow.AddDays(-14);

        // Always count churned regardless of hideChurned so the banner can show a number.
        var churnedCount = await _context.Companies
            .AsNoTracking()
            .IgnoreQueryFilters()
            .Where(c => !c.IsDeleted
                && (c.SubscriptionStatus == SubscriptionStatus.Expired || c.SubscriptionStatus == SubscriptionStatus.Canceled)
                && c.SubscriptionEndDate != null
                && c.SubscriptionEndDate < cutoff)
            .CountAsync();

        var query = _context.Companies
            .AsNoTracking()
            .IgnoreQueryFilters()
            .Where(c => !c.IsDeleted)
            .AsQueryable();

        if (hideChurned)
            query = query.Where(c =>
                !((c.SubscriptionStatus == SubscriptionStatus.Expired || c.SubscriptionStatus == SubscriptionStatus.Canceled)
                  && c.SubscriptionEndDate != null
                  && c.SubscriptionEndDate < cutoff));

        if (!string.IsNullOrWhiteSpace(searchTerm))
        {
            var s = searchTerm.ToLower();
            query = query.Where(c =>
                c.CompanyName.ToLower().Contains(s) ||
                (c.CompanyCode != null && c.CompanyCode.ToLower().Contains(s)) ||
                (c.PrimaryContactEmail != null && c.PrimaryContactEmail.ToLower().Contains(s)) ||
                (c.Phone != null && c.Phone.ToLower().Contains(s)));
        }

        query = (sortColumn, sortDirection == "asc") switch
        {
            ("CompanyName", true)  => query.OrderBy(c => c.CompanyName),
            ("CompanyName", false) => query.OrderByDescending(c => c.CompanyName),
            ("Plan", true)         => query.OrderBy(c => c.SubscriptionPlan),
            ("Plan", false)        => query.OrderByDescending(c => c.SubscriptionPlan),
            ("Status", true)       => query.OrderBy(c => c.IsActive),
            ("Status", false)      => query.OrderByDescending(c => c.IsActive),
            ("Created", true)      => query.OrderBy(c => c.CreatedAt),
            ("Created", false)     => query.OrderByDescending(c => c.CreatedAt),
            _                      => query.OrderBy(c => c.CompanyName)
        };

        var totalCount = await query.CountAsync();

        var companies = await query
            .Include(c => c.Users)
            .Skip((page - 1) * pageSize)
            .Take(pageSize)
            .ToListAsync();

        return (companies, totalCount, churnedCount);
    }

    /// <inheritdoc/>
    public async Task<CompanyCountSummary> GetCountSummaryAsync(IReadOnlyList<int> companyIds)
    {
        var now = DateTime.UtcNow;
        var d30 = now.AddDays(-30);
        var d90 = now.AddDays(-90);

        var jobCounts = await _context.Jobs
            .IgnoreQueryFilters()
            .Where(j => companyIds.Contains(j.CompanyId) && !j.IsDeleted)
            .GroupBy(j => j.CompanyId)
            .Select(g => new { g.Key, Count = g.Count() })
            .ToDictionaryAsync(x => x.Key, x => x.Count);

        var quoteCounts = await _context.Quotes
            .IgnoreQueryFilters()
            .Where(q => companyIds.Contains(q.CompanyId) && !q.IsDeleted)
            .GroupBy(q => q.CompanyId)
            .Select(g => new { g.Key, Count = g.Count() })
            .ToDictionaryAsync(x => x.Key, x => x.Count);

        var customerCounts = await _context.Customers
            .IgnoreQueryFilters()
            .Where(c => companyIds.Contains(c.CompanyId) && !c.IsDeleted)
            .GroupBy(c => c.CompanyId)
            .Select(g => new { g.Key, Count = g.Count() })
            .ToDictionaryAsync(x => x.Key, x => x.Count);

        var wizardRaw = await _context.CompanyPreferences
            .IgnoreQueryFilters()
            .Where(p => companyIds.Contains(p.CompanyId) && p.SetupWizardCompleted)
            .Select(p => new { p.CompanyId, p.SetupWizardCompletedAt, p.SetupWizardCompletedByName })
            .ToListAsync();

        var wizardInfo = wizardRaw.ToDictionary(
            x => x.CompanyId,
            x => new CompanyWizardInfo(true, x.SetupWizardCompletedAt, x.SetupWizardCompletedByName));

        var jobs30 = await _context.Jobs
            .IgnoreQueryFilters()
            .Where(j => companyIds.Contains(j.CompanyId) && !j.IsDeleted && j.CreatedAt >= d30)
            .GroupBy(j => j.CompanyId)
            .Select(g => new { g.Key, Count = g.Count() })
            .ToDictionaryAsync(x => x.Key, x => x.Count);

        var jobs90 = await _context.Jobs
            .IgnoreQueryFilters()
            .Where(j => companyIds.Contains(j.CompanyId) && !j.IsDeleted && j.CreatedAt >= d90)
            .GroupBy(j => j.CompanyId)
            .Select(g => new { g.Key, Count = g.Count() })
            .ToDictionaryAsync(x => x.Key, x => x.Count);

        var lastLoginRaw = await _context.Users
            .IgnoreQueryFilters()
            .Where(u => companyIds.Contains(u.CompanyId) && u.LastLoginDate != null)
            .GroupBy(u => u.CompanyId)
            .Select(g => new { CompanyId = g.Key, Last = g.Max(u => u.LastLoginDate) })
            .ToListAsync();

        var lastLogins = lastLoginRaw.ToDictionary(
            x => x.CompanyId,
            x => x.Last);

        return new CompanyCountSummary(jobCounts, quoteCounts, customerCounts, wizardInfo,
            jobs30, jobs90, lastLogins);
    }
}
