using Microsoft.EntityFrameworkCore;
using PowderCoating.Application.DTOs.Health;
using PowderCoating.Application.Interfaces;
using PowderCoating.Infrastructure.Data;

namespace PowderCoating.Infrastructure.Services;

/// <summary>
/// Runs configuration health checks for tenant companies. Each check type is a single
/// batch DB query grouped by CompanyId — O(checks) round-trips regardless of company count.
/// </summary>
public class CompanyConfigHealthService : ICompanyConfigHealthService
{
    private readonly ApplicationDbContext _db;

    public CompanyConfigHealthService(ApplicationDbContext db)
    {
        _db = db;
    }

    public async Task<CompanyConfigHealth> CheckAsync(int companyId)
    {
        var batch = await CheckBatchAsync([companyId]);
        return batch.TryGetValue(companyId, out var result)
            ? result
            : new CompanyConfigHealth { CompanyId = companyId };
    }

    public async Task<Dictionary<int, CompanyConfigHealth>> CheckBatchAsync(IEnumerable<int> companyIds)
    {
        var ids = companyIds.ToList();
        if (ids.Count == 0) return new Dictionary<int, CompanyConfigHealth>();

        // --- Batch queries (one per check type) ---

        var hasAccounts = new HashSet<int>(await _db.Accounts
            .AsNoTracking().IgnoreQueryFilters()
            .Where(a => ids.Contains(a.CompanyId) && a.IsActive && !a.IsDeleted)
            .Select(a => a.CompanyId).Distinct().ToListAsync());

        var hasPrepServices = new HashSet<int>(await _db.PrepServices
            .AsNoTracking().IgnoreQueryFilters()
            .Where(p => ids.Contains(p.CompanyId) && p.IsActive && !p.IsDeleted)
            .Select(p => p.CompanyId).Distinct().ToListAsync());

        var hasOperatingCosts = new HashSet<int>(await _db.CompanyOperatingCosts
            .AsNoTracking().IgnoreQueryFilters()
            .Where(o => ids.Contains(o.CompanyId) && !o.IsDeleted)
            .Select(o => o.CompanyId).Distinct().ToListAsync());

        // Calibrated = has named blast setups OR (operating costs with CompressorCfm > 0 OR BlastRateSqFtPerHourOverride set)
        var hasNamedBlastSetups = new HashSet<int>(await _db.CompanyBlastSetups
            .AsNoTracking().IgnoreQueryFilters()
            .Where(b => ids.Contains(b.CompanyId) && !b.IsDeleted && b.IsActive)
            .Select(b => b.CompanyId).Distinct().ToListAsync());

        var hasQuotingCalibration = new HashSet<int>(hasNamedBlastSetups.Concat(
            await _db.CompanyOperatingCosts
                .AsNoTracking().IgnoreQueryFilters()
                .Where(o => ids.Contains(o.CompanyId) && !o.IsDeleted
                         && (o.CompressorCfm > 0 || o.BlastRateSqFtPerHourOverride != null))
                .Select(o => o.CompanyId).Distinct().ToListAsync()));

        var wizardDone = new HashSet<int>(await _db.CompanyPreferences
            .AsNoTracking().IgnoreQueryFilters()
            .Where(p => ids.Contains(p.CompanyId) && !p.IsDeleted && p.SetupWizardCompleted)
            .Select(p => p.CompanyId).Distinct().ToListAsync());

        var hasInventory = new HashSet<int>(await _db.InventoryItems
            .AsNoTracking().IgnoreQueryFilters()
            .Where(i => ids.Contains(i.CompanyId) && i.IsActive && !i.IsDeleted)
            .Select(i => i.CompanyId).Distinct().ToListAsync());

        var hasCatalogItems = new HashSet<int>(await _db.CatalogItems
            .AsNoTracking().IgnoreQueryFilters()
            .Where(c => ids.Contains(c.CompanyId) && c.IsActive && !c.IsDeleted)
            .Select(c => c.CompanyId).Distinct().ToListAsync());

        var hasOvenCosts = new HashSet<int>(await _db.OvenCosts
            .AsNoTracking().IgnoreQueryFilters()
            .Where(o => ids.Contains(o.CompanyId) && o.IsActive && !o.IsDeleted)
            .Select(o => o.CompanyId).Distinct().ToListAsync());

        // --- Build result per company ---

        var results = new Dictionary<int, CompanyConfigHealth>();

        foreach (var id in ids)
        {
            var health = new CompanyConfigHealth { CompanyId = id };

            if (!hasAccounts.Contains(id))
                health.Issues.Add(new ConfigHealthIssue
                {
                    Code     = "NO_ACCOUNTS",
                    Title    = "No Chart of Accounts",
                    Detail   = "Invoicing, expense tracking, and financial reports require at least a basic chart of accounts.",
                    Severity = ConfigIssueSeverity.Critical,
                    FixPath  = "/Accounts",
                    FixLabel = "Set Up Accounts"
                });

            if (!hasOperatingCosts.Contains(id))
                health.Issues.Add(new ConfigHealthIssue
                {
                    Code     = "NO_OPERATING_COSTS",
                    Title    = "Operating Costs Not Configured",
                    Detail   = "Labor, equipment, and overhead rates are missing — job pricing calculations will produce $0 estimates.",
                    Severity = ConfigIssueSeverity.Critical,
                    FixPath  = "/CompanySettings#operating-costs",
                    FixLabel = "Configure Costs"
                });

            // Only prompt for calibration if operating costs are already set up
            if (hasOperatingCosts.Contains(id) && !hasQuotingCalibration.Contains(id))
                health.Issues.Add(new ConfigHealthIssue
                {
                    Code     = "QUOTING_NOT_CALIBRATED",
                    Title    = "No Sandblasting Setups Found",
                    Detail   = "Your shop's blast equipment hasn't been configured. AI photo quotes and calculated item time estimates will use generic industry averages instead of your actual throughput.",
                    Severity = ConfigIssueSeverity.Warning,
                    FixPath  = "/CompanySettings#quoting-calibration",
                    FixLabel = "Configure Blast Setups"
                });

            if (!hasPrepServices.Contains(id))
                health.Issues.Add(new ConfigHealthIssue
                {
                    Code     = "NO_PREP_SERVICES",
                    Title    = "No Prep Services Defined",
                    Detail   = "The prep services step is skipped in the quote/job wizard. Workers won't see sandblasting, masking, or other prep options.",
                    Severity = ConfigIssueSeverity.Warning,
                    FixPath  = "/CompanySettings#prep-services-lookup",
                    FixLabel = "Add Prep Services"
                });

            if (!wizardDone.Contains(id))
                health.Issues.Add(new ConfigHealthIssue
                {
                    Code     = "WIZARD_INCOMPLETE",
                    Title    = "Setup Wizard Not Completed",
                    Detail   = "The initial company configuration wizard was never finished. Some rates and defaults may be missing.",
                    Severity = ConfigIssueSeverity.Warning,
                    FixPath  = "/SetupWizard/Step?step=1",
                    FixLabel = "Resume Wizard"
                });

            if (!hasInventory.Contains(id))
                health.Issues.Add(new ConfigHealthIssue
                {
                    Code     = "NO_INVENTORY",
                    Title    = "No Inventory Items",
                    Detail   = "No powder or material items are in inventory. The coating layer step will have no powder options to select.",
                    Severity = ConfigIssueSeverity.Warning,
                    FixPath  = "/Inventory",
                    FixLabel = "Add Inventory"
                });

            if (!hasCatalogItems.Contains(id))
                health.Issues.Add(new ConfigHealthIssue
                {
                    Code     = "NO_CATALOG",
                    Title    = "No Catalog Items",
                    Detail   = "No pre-defined products or services exist. Quotes can still be created with custom items.",
                    Severity = ConfigIssueSeverity.Info,
                    FixPath  = "/CatalogItems",
                    FixLabel = "Add Catalog Items"
                });

            if (!hasOvenCosts.Contains(id))
                health.Issues.Add(new ConfigHealthIssue
                {
                    Code     = "NO_OVENS",
                    Title    = "No Named Ovens Configured",
                    Detail   = "The oven scheduler will use the default rate only. Capacity planning and batch suggestions require named ovens.",
                    Severity = ConfigIssueSeverity.Info,
                    FixPath  = "/CompanySettings#operating-costs",
                    FixLabel = "Configure Ovens"
                });

            results[id] = health;
        }

        return results;
    }
}
