using Domain.Dtos.Sales;
using Domain.Entities;
using Domain.Generics;
using Microsoft.EntityFrameworkCore;
using Models.Helpers;
using Models.Interfaces;
using Models.Models;

namespace Models.Repositories
{
    public class PhSDeliveryNoteRepository(PhronCareOperationsHubContext context) : IPhSDeliveryNoteRepository
    {
        private readonly PhronCareOperationsHubContext _context = context;

        public async Task<PagedResult<DeliveryNoteSummaryDto>> SearchAsync(
            int? customerId,
            string? customerText,
            string? deliveryNoteNumber,
            int? quoteId,
            string? quoteNumber,
            DateTime? issueDateFrom,
            DateTime? issueDateTo,
            string? status,
            int page = 1,
            int pageSize = 50)
        {
            var query = _context.PhSDeliveryNotes
                .AsNoTracking()
                .Include(x => x.Customer)
                .Include(x => x.Quote)
                .AsQueryable();

            if (customerId.HasValue)
                query = query.Where(x => x.CustomerId == customerId.Value);
            else if (!string.IsNullOrWhiteSpace(customerText))
                query = query.Where(x => x.Customer.Name.Contains(customerText));

            if (!string.IsNullOrWhiteSpace(deliveryNoteNumber))
                query = query.Where(x => x.Deliverynotenumber.Contains(deliveryNoteNumber));

            if (quoteId.HasValue)
                query = query.Where(x => x.QuoteId == quoteId.Value);
            else if (!string.IsNullOrWhiteSpace(quoteNumber))
                query = query.Where(x => x.Quote != null && x.Quote.Quotenumber.Contains(quoteNumber));

            if (issueDateFrom.HasValue)
                query = query.Where(x => x.Issuedate >= issueDateFrom.Value);

            if (issueDateTo.HasValue)
                query = query.Where(x => x.Issuedate <= issueDateTo.Value);

            if (!string.IsNullOrWhiteSpace(status))
                query = query.Where(x => x.Status == status);

            query = query
                .OrderByDescending(x => x.Issuedate)
                .ThenByDescending(x => x.Id);

            var pagedEntities = await query.ToPagedResultAsync(page, pageSize);

            var dtos = pagedEntities.Items.Select(x => new DeliveryNoteSummaryDto
            {
                Id = x.Id,
                DeliveryNoteNumber = x.Deliverynotenumber,
                QuoteId = x.QuoteId,
                QuoteNumber = x.Quote?.Quotenumber,
                IssueDate = x.Issuedate,
                CustomerId = x.CustomerId,
                CustomerName = x.Customer?.Name ?? string.Empty,
                Status = x.Status,
                Observations = x.Observations,
                PrintCount = x.Printcount,
                CreatedAt = x.Createdat,
                ModifiedAt = x.Modifiedat
            }).ToList();

            return new PagedResult<DeliveryNoteSummaryDto>
            {
                Items = dtos,
                TotalItems = pagedEntities.TotalItems,
                Page = pagedEntities.Page,
                PageSize = pagedEntities.PageSize
            };
        }

        public async Task<DeliveryNoteDto?> GetDtoByIdAsync(int id)
        {
            var entity = await _context.PhSDeliveryNotes
                .Include(x => x.Customer)
                .Include(x => x.Quote)
                .Include(x => x.PhSDeliveryNoteDetails)
                    .ThenInclude(d => d.QuoteDetail)
                .AsNoTracking()
                .FirstOrDefaultAsync(x => x.Id == id);

            return entity == null ? null : MapDeliveryNoteDto(entity);
        }

        public async Task<DeliveryNoteDto?> GetDtoByDeliveryNoteNumberAsync(string deliveryNoteNumber)
        {
            var entity = await _context.PhSDeliveryNotes
                .Include(x => x.Customer)
                .Include(x => x.Quote)
                .Include(x => x.PhSDeliveryNoteDetails)
                    .ThenInclude(d => d.QuoteDetail)
                .AsNoTracking()
                .FirstOrDefaultAsync(x => x.Deliverynotenumber == deliveryNoteNumber);

            return entity == null ? null : MapDeliveryNoteDto(entity);
        }

        public async Task<IEnumerable<DeliveryNoteDto>> GetDtosByQuoteIdAsync(int quoteId)
        {
            var entities = await _context.PhSDeliveryNotes
                .Include(x => x.Customer)
                .Include(x => x.Quote)
                .Include(x => x.PhSDeliveryNoteDetails)
                    .ThenInclude(d => d.QuoteDetail)
                .AsNoTracking()
                .Where(x => x.QuoteId == quoteId)
                .OrderByDescending(x => x.Issuedate)
                .ThenByDescending(x => x.Id)
                .ToListAsync();

            return entities.Select(MapDeliveryNoteDto);
        }

        public async Task<bool> ExistsByDeliveryNoteNumberAsync(string deliveryNoteNumber)
        {
            return await _context.PhSDeliveryNotes
                .AsNoTracking()
                .AnyAsync(x => x.Deliverynotenumber == deliveryNoteNumber);
        }

        public async Task<EDeliveryNote> CreateAsync(EDeliveryNote entity)
        {
            var mapped = EntityMapper.MapEntity<EDeliveryNote, PhSDeliveryNote>(entity);

            await _context.PhSDeliveryNotes.AddAsync(mapped);
            await _context.SaveChangesAsync();

            return EntityMapper.MapEntity<PhSDeliveryNote, EDeliveryNote>(mapped);
        }

        private static DeliveryNoteDto MapDeliveryNoteDto(PhSDeliveryNote source)
        {
            return new DeliveryNoteDto
            {
                Id = source.Id,
                DeliveryNoteNumber = source.Deliverynotenumber,
                CustomerName = source.Customer?.Name ?? string.Empty,
                QuoteId = source.QuoteId,
                QuoteNumber = source.Quote?.Quotenumber,
                SalesInvoiceId = source.SalesinvoiceId,
                IssueDate = source.Issuedate,
                CustomerId = source.CustomerId,
                Status = source.Status,
                Observations = source.Observations,
                ExtraInfoJson = source.ExtrainfoJson,
                PrintCount = source.Printcount,
                LogoBase64 = null,
                CreatedAt = source.Createdat,
                ModifiedAt = source.Modifiedat,
                Items = source.PhSDeliveryNoteDetails
                    .OrderBy(d => d.LineNumber)
                    .ThenBy(d => d.Id)
                    .Select(MapDeliveryNoteItemDto)
                    .ToList()
            };
        }

        private static DeliveryNoteItemDto MapDeliveryNoteItemDto(PhSDeliveryNoteDetail source)
        {
            return new DeliveryNoteItemDto
            {
                Id = source.Id,
                DeliverynoteId = source.DeliverynoteId,
                LineNumber = source.LineNumber,
                OriginType = source.OriginType,
                OriginId = source.OriginId,
                QuoteDetailId = source.QuoteDetailId,
                Description = source.Description??string.Empty,
                Quantity = source.Quantity,
                ProductId = source.QuoteDetail?.ProductId,
                ApprovedUnitPrice = source.QuoteDetail?.Approvedunitprice,
                ApprovedAmount = source.QuoteDetail?.Approvedamount,
                OriginalUnitPrice = source.QuoteDetail?.Unitprice,
                Notes = source.Notes,
                Createdat = source.Createdat,
                Modifiedat = source.Modifiedat
            };
        }
    }
}
