using PowderCoating.Core.Entities;

namespace PowderCoating.Core.Interfaces.Repositories;

/// <summary>Aggregate counts and totals for the Quotes Index stat cards.</summary>
public record QuoteIndexStats(int OpenCount, int ApprovedConvertedCount, decimal TotalValue);

/// <summary>
/// Typed repository for <see cref="Quote"/> that adds domain-specific queries on top of
/// the generic CRUD interface.
/// </summary>
public interface IQuoteRepository : IRepository<Quote>
{
    /// <summary>
    /// Loads a single quote with the full include chain required by the Details view: Customer,
    /// PreparedBy, QuoteStatus, OvenCost, QuoteItems with Coats (InventoryItem + Vendor),
    /// CatalogItem, and PrepServices; plus QuotePrepServices (quote-level prep).
    /// Returns null if not found or soft-deleted.
    /// </summary>
    Task<Quote?> LoadForDetailsAsync(int id, int companyId);

    /// <summary>
    /// Loads a single quote by its customer-facing approval token. Ignores global query filters
    /// so the unauthenticated approval portal can resolve any tenant's quote by token alone.
    /// Includes Customer navigation. Returns null if the token does not match any live quote.
    /// </summary>
    Task<Quote?> GetByApprovalTokenAsync(string token);

    /// <summary>
    /// Returns the change history for a quote, ordered newest-first, with ChangedBy loaded.
    /// </summary>
    Task<List<QuoteChangeHistory>> GetChangeHistoryAsync(int quoteId, int companyId);

    /// <summary>
    /// Returns aggregate stat counts and total value for the Index view stat cards, scoped to the
    /// given <paramref name="companyId"/> (explicit predicate for defense in depth, in addition to
    /// the global query filter). Pass status ID sets (derived from QuoteStatusLookup) to classify
    /// open vs. approved/converted quotes.
    /// <para>
    /// <paramref name="excludedStatusId"/> drops quotes in that status (Converted) from every card so
    /// the stat strip reflects the same population as the default Index list, which hides Converted
    /// quotes. Without this, a converted quote (which cannot be deleted while a job is linked) inflates
    /// TotalValue and the approved count even though it never appears in the list.
    /// </para>
    /// </summary>
    Task<QuoteIndexStats> GetIndexStatsAsync(int companyId, List<int> openStatusIds, List<int> approvedConvertedStatusIds, int? excludedStatusId = null);

    /// <summary>
    /// Loads quote items with their coat passes (InventoryItem + Vendor) and prep services for
    /// PDF generation and quote→job conversion. Cheaper than <see cref="LoadForDetailsAsync"/>
    /// because it skips the parent-quote navigations that callers already have.
    /// </summary>
    Task<List<QuoteItem>> GetItemsWithCoatsAsync(int quoteId, int companyId);

    /// <summary>
    /// Returns the last quote number that starts with <paramref name="prefix"/> for the given
    /// company (including soft-deleted quotes) for sequential number generation.
    /// </summary>
    Task<string?> GetLastQuoteNumberByPrefixAsync(int companyId, string prefix);
}
