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

namespace PowderCoating.Infrastructure.Repositories;

/// <summary>
/// Typed repository for <see cref="Job"/> that provides domain-specific multi-level
/// include queries that the generic <see cref="Repository{T}"/> cannot express.
/// The base class handles all standard CRUD operations; this class adds read queries
/// that were previously scattered as inline EF expressions inside controllers.
/// </summary>
public class JobRepository : Repository<Job>, IJobRepository
{
    public JobRepository(ApplicationDbContext context) : base(context) { }

    /// <inheritdoc/>
    public async Task<List<Job>> GetBoardJobsAsync()
    {
        return await _context.Jobs
            .AsNoTracking()
            .Where(j => !j.IsDeleted)
            .Include(j => j.Customer)
            .Include(j => j.JobStatus)
            .Include(j => j.JobPriority)
            .Include(j => j.AssignedUser)
            .OrderBy(j => j.DueDate.HasValue ? 0 : 1)
            .ThenBy(j => j.DueDate)
            .ThenBy(j => j.JobPriority!.DisplayOrder)
            .ToListAsync();
    }

    /// <inheritdoc/>
    public async Task<Job?> LoadForDetailsAsync(int id, int companyId)
    {
        // Single query replaces the per-item N+1 loop that was in JobsController.Details.
        // EF Core splits the multi-level ThenIncludes across two SQL queries automatically
        // (split query behavior), keeping result set size manageable.
        return await _context.Jobs
            .Where(j => j.Id == id && j.CompanyId == companyId && !j.IsDeleted)
            .Include(j => j.Customer)
            .Include(j => j.JobStatus)
            .Include(j => j.JobPriority)
            .Include(j => j.AssignedUser)
            .Include(j => j.Quote)
            .Include(j => j.OvenCost)
            .Include(j => j.OriginalJob)
            .Include(j => j.IntakeCheckedBy)
            .Include(j => j.JobItems.Where(ji => !ji.IsDeleted))
                .ThenInclude(ji => ji.Coats)
                    .ThenInclude(c => c.InventoryItem)
            .Include(j => j.JobItems.Where(ji => !ji.IsDeleted))
                .ThenInclude(ji => ji.Coats)
                    .ThenInclude(c => c.Vendor)
            .Include(j => j.JobItems.Where(ji => !ji.IsDeleted))
                .ThenInclude(ji => ji.PrepServices)
                    .ThenInclude(ps => ps.PrepService)
            .Include(j => j.JobPrepServices.Where(jps => !jps.IsDeleted))
                .ThenInclude(jps => jps.PrepService)
            .FirstOrDefaultAsync();
    }

    /// <inheritdoc/>
    public async Task<Job?> LoadForEditAsync(int id, int companyId)
    {
        return await _context.Jobs
            .Where(j => j.Id == id && j.CompanyId == companyId && !j.IsDeleted)
            .Include(j => j.Customer)
            .Include(j => j.JobStatus)
            .Include(j => j.JobPriority)
            .Include(j => j.AssignedUser)
            .Include(j => j.OvenCost)
            .Include(j => j.JobItems.Where(ji => !ji.IsDeleted))
                .ThenInclude(ji => ji.Coats)
                    .ThenInclude(c => c.InventoryItem)
            .Include(j => j.JobItems.Where(ji => !ji.IsDeleted))
                .ThenInclude(ji => ji.Coats)
                    .ThenInclude(c => c.Vendor)
            .Include(j => j.JobItems.Where(ji => !ji.IsDeleted))
                .ThenInclude(ji => ji.PrepServices)
                    .ThenInclude(ps => ps.PrepService)
            .Include(j => j.JobPrepServices.Where(jps => !jps.IsDeleted))
                .ThenInclude(jps => jps.PrepService)
            .FirstOrDefaultAsync();
    }

    /// <inheritdoc/>
    public async Task<Job?> LoadForStatusChangeAsync(int id, int companyId)
    {
        return await _context.Jobs
            .Include(j => j.JobStatus)
            .FirstOrDefaultAsync(j => j.Id == id && j.CompanyId == companyId && !j.IsDeleted);
    }

    /// <inheritdoc/>
    public async Task<List<JobChangeHistory>> GetChangeHistoryAsync(int jobId, int companyId)
    {
        return await _context.JobChangeHistories
            .Where(h => h.JobId == jobId && h.CompanyId == companyId && !h.IsDeleted)
            .Include(h => h.ChangedBy)
            .OrderByDescending(h => h.ChangedAt)
            .AsNoTracking()
            .ToListAsync();
    }

    /// <inheritdoc/>
    public async Task<string?> GetLastJobNumberByPrefixAsync(int companyId, string prefix)
    {
        return await _context.Jobs
            .IgnoreQueryFilters()
            .Where(j => j.CompanyId == companyId && j.JobNumber.StartsWith(prefix))
            .OrderByDescending(j => j.JobNumber)
            .Select(j => j.JobNumber)
            .FirstOrDefaultAsync();
    }

    /// <inheritdoc/>
    public async Task<Job?> GetOrphanedConversionJobAsync(int quoteId, int companyId)
    {
        return await _context.Jobs
            .IgnoreQueryFilters()
            .FirstOrDefaultAsync(j => j.QuoteId == quoteId && j.CompanyId == companyId);
    }

    /// <inheritdoc/>
    public async Task<List<Job>> GetScheduledJobsForDateAsync(DateTime date, string? userId = null)
    {
        var query = _context.Jobs
            .Include(j => j.Customer)
            .Include(j => j.JobStatus)
            .Include(j => j.JobPriority)
            .Include(j => j.AssignedUser)
            .Include(j => j.JobItems.Where(i => !i.IsDeleted))
                .ThenInclude(i => i.Coats)
            .Where(j => j.ScheduledDate.HasValue && j.ScheduledDate.Value.Date == date.Date
                        && !j.IsDeleted && !j.JobStatus.IsTerminalStatus);

        if (!string.IsNullOrEmpty(userId))
            query = query.Where(j => j.AssignedUserId == userId);

        return await query.ToListAsync();
    }

    /// <inheritdoc/>
    public async Task<List<Job>> GetActiveJobsForMobileAsync(int companyId, string? workerId = null)
    {
        var query = _context.Jobs
            .Include(j => j.Customer)
            .Include(j => j.JobStatus)
            .Include(j => j.JobPriority)
            .Include(j => j.AssignedUser)
            .Include(j => j.JobItems.Where(i => !i.IsDeleted))
                .ThenInclude(i => i.Coats)
            .Where(j => j.CompanyId == companyId && !j.IsDeleted && !j.JobStatus.IsTerminalStatus
                        && j.JobStatus.StatusCode != "ON_HOLD" && j.JobStatus.StatusCode != "CANCELLED");

        if (!string.IsNullOrEmpty(workerId))
            query = query.Where(j => j.AssignedUserId == workerId);

        return await query.ToListAsync();
    }

    /// <inheritdoc/>
    public async Task<Job?> LoadForCostingAsync(int jobId, int companyId)
    {
        return await _context.Jobs
            .Where(j => j.Id == jobId && j.CompanyId == companyId && !j.IsDeleted)
            .Include(j => j.OvenCost)
            .Include(j => j.Invoice)
            .Include(j => j.JobItems.Where(i => !i.IsDeleted))
                .ThenInclude(i => i.Coats.Where(c => !c.IsDeleted))
            .Include(j => j.TimeEntries.Where(t => !t.IsDeleted))
            .AsNoTracking()
            .FirstOrDefaultAsync();
    }

    /// <inheritdoc/>
    public async Task<Job?> LoadForTemplateSnapshotAsync(int jobId, int companyId)
    {
        return await _context.Jobs
            .Where(j => j.Id == jobId && j.CompanyId == companyId && !j.IsDeleted)
            .Include(j => j.JobItems.Where(i => !i.IsDeleted))
                .ThenInclude(i => i.Coats.Where(c => !c.IsDeleted))
            .Include(j => j.JobItems.Where(i => !i.IsDeleted))
                .ThenInclude(i => i.PrepServices.Where(p => !p.IsDeleted))
            .FirstOrDefaultAsync();
    }

    /// <inheritdoc/>
    public async Task<int> GetReworkJobCountAsync(int originalJobId, int companyId)
    {
        return await _context.Jobs
            .IgnoreQueryFilters()
            .CountAsync(j => j.OriginalJobId == originalJobId && j.CompanyId == companyId);
    }

    /// <inheritdoc/>
    public async Task<List<Job>> GetOverdueScheduledJobsAsync()
    {
        var today = DateTime.Today;
        return await _context.Jobs
            .Include(j => j.Customer)
            .Include(j => j.JobStatus)
            .Include(j => j.JobPriority)
            .Include(j => j.AssignedUser)
            .Where(j => j.ScheduledDate.HasValue
                        && j.ScheduledDate.Value.Date < today
                        && !j.IsDeleted
                        && !j.JobStatus.IsTerminalStatus)
            .OrderBy(j => j.ScheduledDate)
            .ThenBy(j => j.JobNumber)
            .ToListAsync();
    }
}
