using CashLineERP.Application.Procurement;
using CashLineERP.Domain.Procurement;
using CashLineERP.SharedKernel.Workflow;
using Microsoft.EntityFrameworkCore;

namespace CashLineERP.Infrastructure.Persistence.Repositories;

public sealed class SupplierQuotationRepository : ISupplierQuotationRepository
{
    private readonly CashLineErpDbContext _dbContext;

    public SupplierQuotationRepository(CashLineErpDbContext dbContext)
    {
        _dbContext = dbContext;
    }

    public IReadOnlyCollection<SupplierQuotationDetailView> GetQuotations(long? rfqId = null)
    {
        var query = _dbContext.SupplierQuotations
            .AsNoTracking()
            .Include(quotation => quotation.Lines)
            .AsQueryable();

        if (rfqId.HasValue)
        {
            query = query.Where(quotation => quotation.RequestForQuotationId == rfqId.Value);
        }

        return query
            .OrderByDescending(quotation => quotation.QuoteDate)
            .ThenByDescending(quotation => quotation.Id)
            .ToArray()
            .Select(Map)
            .ToArray();
    }

    public SupplierQuotationDetailView? GetQuotation(long quotationId)
    {
        var quotation = _dbContext.SupplierQuotations
            .AsNoTracking()
            .Include(existingQuotation => existingQuotation.Lines)
            .SingleOrDefault(existingQuotation => existingQuotation.Id == quotationId);

        return quotation is null ? null : Map(quotation);
    }

    public SupplierQuotationDetailView Create(CreateSupplierQuotationCommand command)
    {
        var nextSequence = (_dbContext.SupplierQuotations.Max(quotation => (long?)quotation.Id) ?? 0L) + 1L;
        var quotation = new SupplierQuotation(
            quotationNumber: $"QTN-{nextSequence:D6}",
            requestForQuotationId: command.RequestForQuotationId,
            supplierId: command.SupplierId,
            quoteDate: command.QuoteDate,
            validUntilDate: command.ValidUntilDate,
            currencyCode: command.CurrencyCode,
            fxRate: command.FxRate,
            notes: command.Notes);

        var lineNumber = 1;
        foreach (var line in command.Lines)
        {
            quotation.AddLine(
                lineNumber++,
                line.RequestForQuotationLineId,
                line.ItemId,
                line.QuotedQuantity,
                line.UnitOfMeasureCode,
                line.UnitPrice,
                line.DiscountPercent,
                line.DiscountAmount,
                line.TaxCode,
                line.LeadTimeDays);
        }

        _dbContext.SupplierQuotations.Add(quotation);
        _dbContext.SaveChanges();

        return GetQuotation(quotation.Id)!;
    }

    public SupplierQuotationDetailView Submit(long quotationId, long actorUserId)
    {
        var quotation = _dbContext.SupplierQuotations.Single(existingQuotation => existingQuotation.Id == quotationId);
        quotation.Submit(actorUserId);
        _dbContext.SaveChanges();
        return GetQuotation(quotation.Id)!;
    }

    private static SupplierQuotationDetailView Map(SupplierQuotation quotation) =>
        new(
            quotation.Id,
            quotation.QuotationNumber,
            quotation.RequestForQuotationId,
            quotation.SupplierId,
            quotation.QuoteDate,
            quotation.ValidUntilDate,
            quotation.CurrencyCode,
            quotation.FxRate,
            WorkflowStatusSeedIds.ToCode(quotation.StatusId),
            quotation.SubtotalAmount,
            quotation.DiscountAmount,
            quotation.TaxAmount,
            quotation.TotalAmount,
            quotation.IsSelected,
            quotation.Notes,
            quotation.Lines.OrderBy(line => line.LineNumber).Select(line => new SupplierQuotationLineView(
                line.Id,
                line.LineNumber,
                line.RequestForQuotationLineId,
                line.ItemId,
                line.QuotedQuantity,
                line.UnitOfMeasureCode,
                line.UnitPrice,
                line.DiscountPercent,
                line.DiscountAmount,
                line.TaxCode,
                line.LineNetAmount,
                line.LineTaxAmount,
                line.LineTotalAmount,
                line.LeadTimeDays)).ToArray());
}
