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

namespace Models.Repositories
{
    public class PhSQuoteRepository(PhronCareOperationsHubContext context,
                    IPhSFormSeriesRepository formSeriesRepository) : IQuoteRepository
    {
        private readonly PhronCareOperationsHubContext _context = context;
        private readonly IPhSFormSeriesRepository _formSeriesRepository = formSeriesRepository;
        #region Busqueda usando el QuoteDto
        public async Task<PagedResult<QuoteDto>> SearchAsync(
            int? customerId,
            string? customerText,
            string? quoteNumber,
            int? professionalId,
            string? professionalText,
            int? institutionId,
            string? institutionText,
            int? patientId,
            string? patientText,
            DateTime? issueDateFrom,
            DateTime? issueDateTo,
            string? status,
            int page = 1,
            int pageSize = 50)
        {
            // 1) Base query with includes
            var query = _context.PhSQuoteHeaders
                .Include(q => q.PhSQuoteDetails)
                .Include(q => q.PhSQuoteRoles)
                .Include(q => q.PhSQuoteAdjustments)
                .Include(q => q.PhSQuoteTaxes)
                .AsQueryable();

            // 2) Apply filters
            if (customerId.HasValue)
                query = query.Where(q => q.CustomerId == customerId.Value);
            else if (!string.IsNullOrWhiteSpace(customerText))
                query = query.Where(q =>
                    _context.PhSCustomers.Any(c =>
                        c.Id == q.CustomerId &&
                        c.Name.Contains(customerText)));

            if (!string.IsNullOrWhiteSpace(quoteNumber))
                query = query.Where(q => q.Quotenumber.Contains(quoteNumber));

            if (professionalId.HasValue)
            {
                query = query.Where(q =>
                    q.PhSQuoteRoles.Any(r =>
                        r.Entitytype == PhSEntityTypes.Professional &&
                        r.EntityId == professionalId.Value));
            }
            else if (!string.IsNullOrWhiteSpace(professionalText))
            {
                query = query.Where(q =>
                    q.PhSQuoteRoles.Any(r =>
                        r.Entitytype == PhSEntityTypes.Professional &&
                        _context.PhSProfessionals.Any(p =>
                            p.Id == r.EntityId &&
                            p.Fullname.Contains(professionalText))));
            }

            if (institutionId.HasValue)
            {
                query = query.Where(q =>
                    q.PhSQuoteRoles.Any(r =>
                        r.Entitytype == PhSEntityTypes.Institution &&
                        r.EntityId == institutionId.Value));
            }
            else if (!string.IsNullOrWhiteSpace(institutionText))
            {
                query = query.Where(q =>
                    q.PhSQuoteRoles.Any(r =>
                        r.Entitytype == PhSEntityTypes.Institution &&
                        _context.PhSInstitutions.Any(i =>
                            i.Id == r.EntityId &&
                            i.Name.Contains(institutionText))));
            }

            if (patientId.HasValue)
            {
                query = query.Where(q =>
                    q.PhSQuoteRoles.Any(r =>
                        r.Entitytype == PhSEntityTypes.Patient &&
                        r.EntityId == patientId.Value));
            }
            else if (!string.IsNullOrWhiteSpace(patientText))
            {
                query = query.Where(q =>
                    q.PhSQuoteRoles.Any(r =>
                        r.Entitytype == PhSEntityTypes.Patient &&
                        (_context.PhSPatients.Any(pt =>
                             pt.Id == r.EntityId &&
                             pt.Firstname.Contains(patientText)) ||
                         _context.PhSPatients.Any(pt =>
                             pt.Id == r.EntityId &&
                             pt.Lastname.Contains(patientText)))));
            }

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

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

            // 3) Order by most recent and execute paged query
            query = query.OrderByDescending(q => q.Issuedate);
            var pagedEntities = await query.ToPagedResultAsync(page, pageSize);

            // 4) Project to DTOs
            var dtos = pagedEntities.Items.Select(header =>
            {
                // Precompute tax sums
                var totalTaxAmount = header.PhSQuoteTaxes.Sum(t => t.Taxamount);
                var netBase = header.Netamount.GetValueOrDefault() != 0m
                              ? header.Netamount.Value
                              : 1m;

                return new QuoteDto
                {
                    Id = header.Id,
                    Quotenumber = header.Quotenumber,
                    IssueDate = header.Issuedate,
                    EstimatedDate = header.Estimateddate,
                    CustomerName = _context.PhSCustomers
                                            .Where(c => c.Id == header.CustomerId)
                                            .Select(c => c.Name)
                                            .FirstOrDefault() ?? "",
                    ProfessionalName = header.PhSQuoteRoles
                                            .Where(r => r.Entitytype == PhSEntityTypes.Professional)
                                            .Select(r => _context.PhSProfessionals
                                                               .Where(p => p.Id == r.EntityId)
                                                               .Select(p => p.Fullname)
                                                               .FirstOrDefault())
                                            .FirstOrDefault() ?? "",
                    InstitutionName = header.PhSQuoteRoles
                                            .Where(r => r.Entitytype == PhSEntityTypes.Institution)
                                            .Select(r => _context.PhSInstitutions
                                                               .Where(i => i.Id == r.EntityId)
                                                               .Select(i => i.Name)
                                                               .FirstOrDefault())
                                            .FirstOrDefault() ?? "",
                    PatientName = header.PhSQuoteRoles
                                            .Where(r => r.Entitytype == PhSEntityTypes.Patient)
                                            .Select(r => _context.PhSPatients
                                                               .Where(pt => pt.Id == r.EntityId)
                                                               .Select(pt => (pt.Firstname + " " + pt.Lastname).Trim())
                                                               .FirstOrDefault())
                                            .FirstOrDefault() ?? "",
                    BusinessUnitName = _context.PhSBusinessUnits
                                            .Where(b => b.Id == header.BusinessunitId)
                                            .Select(b => b.Code)
                                            .FirstOrDefault() ?? "",
                    Currency = header.Currency,
                    Total = header.Total.GetValueOrDefault(0m),
                    Status = header.Status.Trim(),
                    Observations=header.Observations,
                    SalespersonName = _context.PhSPeople
                                            .Where(u => u.Id == header.PeopleId)
                                            .Select(u => u.Name)
                                            .FirstOrDefault() ?? "",

                    Items = header.PhSQuoteDetails.Select(d =>
                    {
                        var itemBase = d.Quantity * d.Unitprice;
                        var itemTax = totalTaxAmount * itemBase / netBase;
                        return new QuoteItemDto
                        {
                            Id = d.Id,
                            Description = d.ProductDescription,
                            Quantity = d.Quantity,
                            UnitPrice = d.Unitprice,
                            Subtotal = itemBase,
                            TaxAmount = itemTax,
                            Total = itemBase + itemTax,
                            Approved = d.Approved,
                            ApprovedQuantity = d.Approvedquantity,
                            ApprovedUnitPrice = d.Approvedunitprice,
                            ApprovedAmount = d.Approvedamount
                        };
                    }).ToList(),

                    Taxes = header.PhSQuoteTaxes.Select(t => new QuoteTaxDto
                    {
                        TaxName = t.Taxname,
                        TaxCode = t.Taxcode,
                        TaxableAmount = t.TaxableAmount,
                        TaxRate = t.Taxrate,
                        TaxAmount = t.Taxamount,
                        IsIncludedInPrice = t.IsIncludedInPrice
                    }).ToList(),

                    Adjustments = header.PhSQuoteAdjustments.Select(a => new QuoteAdjustmentDto
                    {
                        Reason = _context.PhSAdjustmentReasons
                                    .Where(r => r.Code == a.ReasonCode)
                                    .Select(r => r.Description)
                                    .FirstOrDefault() ?? "",
                        Amount = a.Amount.GetValueOrDefault(0m)
                    }).ToList()
                };
            }).ToList();

            // 5) Return paged DTO result
            return new PagedResult<QuoteDto>
            {
                Items = dtos,
                TotalItems = pagedEntities.TotalItems,
                Page = pagedEntities.Page,
                PageSize = pagedEntities.PageSize
            };
        }
        public async Task<QuoteDto?> GetDtoByIdAsync(int id)
        {
            var header = await _context.PhSQuoteHeaders
                .Include(q => q.PhSQuoteDetails)
                .Include(q => q.PhSQuoteRoles)
                .Include(q => q.PhSQuoteAdjustments)
                .Include(q => q.PhSQuoteTaxes)
                .FirstOrDefaultAsync(q => q.Id == id);

            if (header == null)
                return null;

            // Cargar Customer completo con documentos y tipos
            var customer = await _context.PhSCustomers
                .Include(c => c.PhSCustomerDocuments)
                    .ThenInclude(d => d.Documenttypes)
                .Include(c => c.PhSCustomerAddresses)
                .Include(c => c.TaxCondition)
                .FirstOrDefaultAsync(c => c.Id == header.CustomerId);

            var totalTaxAmount = header.PhSQuoteTaxes.Sum(t => t.Taxamount);
            var netBase = header.Netamount.GetValueOrDefault() != 0m
                          ? header.Netamount.Value
                          : 1m;

            var dto = new QuoteDto
            {
                Id = header.Id,
                Quotenumber = header.Quotenumber,
                IssueDate = header.Issuedate,
                EstimatedDate = header.Estimateddate,
                OfferValidityDays = header.Offervaliditydays,
                PaymentTermDescription = _context.PhSPaymentTerms
                    .Where(p => p.Id == header.PaymenttermId)
                    .Select(p => p.Description)
                    .FirstOrDefault() ?? "",
                BusinessUnitName = _context.PhSBusinessUnits
                    .Where(b => b.Id == header.BusinessunitId)
                    .Select(b => b.Code)
                    .FirstOrDefault() ?? "",
                Currency = header.Currency,
                Total = header.Total.GetValueOrDefault(0m),
                Status = header.Status.Trim(),
                Observations = header.Observations ?? "",

                CustomerName = customer?.Name ?? "",

                ProfessionalName = header.PhSQuoteRoles
                    .Where(r => r.Entitytype == PhSEntityTypes.Professional)
                    .Select(r => _context.PhSProfessionals
                        .Where(p => p.Id == r.EntityId)
                        .Select(p => p.Fullname)
                        .FirstOrDefault())
                    .FirstOrDefault() ?? "",

                InstitutionName = header.PhSQuoteRoles
                    .Where(r => r.Entitytype == PhSEntityTypes.Institution)
                    .Select(r => _context.PhSInstitutions
                        .Where(i => i.Id == r.EntityId)
                        .Select(i => i.Name)
                        .FirstOrDefault())
                    .FirstOrDefault() ?? "",

                PatientName = header.PhSQuoteRoles
                    .Where(r => r.Entitytype == PhSEntityTypes.Patient)
                    .Select(r => _context.PhSPatients
                        .Where(pt => pt.Id == r.EntityId)
                        .Select(pt => (pt.Firstname + " " + pt.Lastname).Trim())
                        .FirstOrDefault())
                    .FirstOrDefault() ?? "",

                SalespersonName = _context.PhSPeople
                    .Where(u => u.Id == header.PeopleId)
                    .Select(u => u.Name)
                    .FirstOrDefault() ?? "",

                Items = header.PhSQuoteDetails.Select(d =>
                {
                    var itemBase = d.Quantity * d.Unitprice;
                    var itemTax = totalTaxAmount * itemBase / netBase;

                    return new QuoteItemDto
                    {
                        Id = d.Id,
                        Description = d.ProductDescription,
                        Quantity = d.Quantity,
                        UnitPrice = d.Unitprice,
                        Subtotal = itemBase,
                        TaxAmount = itemTax,
                        Total = itemBase + itemTax,
                        Approved = d.Approved,
                        ApprovedQuantity = d.Approvedquantity,
                        ApprovedUnitPrice = d.Approvedunitprice,
                        ApprovedAmount = d.Approvedamount
                    };
                }).ToList(),

                Taxes = header.PhSQuoteTaxes.Select(t => new QuoteTaxDto
                {
                    TaxName = t.Taxname,
                    TaxCode = t.Taxcode,
                    TaxableAmount = t.TaxableAmount,
                    TaxRate = t.Taxrate,
                    TaxAmount = t.Taxamount,
                    IsIncludedInPrice = t.IsIncludedInPrice
                }).ToList(),

                Adjustments = header.PhSQuoteAdjustments.Select(a => new QuoteAdjustmentDto
                {
                    Reason = _context.PhSAdjustmentReasons
                        .Where(r => r.Code == a.ReasonCode)
                        .Select(r => r.Description)
                        .FirstOrDefault() ?? "",
                    Amount = a.Amount.GetValueOrDefault(0m)
                }).ToList(),

                Customer = new QuoteCustomerDto
                {
                    Name = customer?.Name ?? "",
                    Address = customer?.PhSCustomerAddresses
                        .Select(a => a.Streetaddress1)
                        .FirstOrDefault() ?? "",
                    IvaCondition = customer?.TaxCondition?.Description ?? "",
                    Documents = customer?.PhSCustomerDocuments
                        .Select(d => new QuoteCustomerDocumentDto
                        {
                            DocumentType = d.Documenttypes.Name,
                            Number = d.DocumentNumber
                        }).ToList() ?? new()
                }
            };

            return dto;
        }
        public async Task<QuoteDto?> GetDtoByQuoteNumberAsync(string quoteNumber)
        {
            var header = await _context.PhSQuoteHeaders
                .Include(q => q.PhSQuoteRoles)
                .FirstOrDefaultAsync(q => q.Quotenumber == quoteNumber);

            if (header == null)
                return null;

            var customer = await _context.PhSCustomers
                .FirstOrDefaultAsync(c => c.Id == header.CustomerId);

            return new QuoteDto
            {
                Id = header.Id,
                Quotenumber = header.Quotenumber,
                EstimatedDate = header.Estimateddate,
                Observations = header.DispatchInstruction ?? "",
                CustomerName = customer?.Name ?? "",

                ProfessionalName = header.PhSQuoteRoles
                    .Where(r => r.Entitytype == PhSEntityTypes.Professional)
                    .Select(r => _context.PhSProfessionals
                        .Where(p => p.Id == r.EntityId)
                        .Select(p => p.Fullname)
                        .FirstOrDefault())
                    .FirstOrDefault() ?? "",

                InstitutionName = header.PhSQuoteRoles
                    .Where(r => r.Entitytype == PhSEntityTypes.Institution)
                    .Select(r => _context.PhSInstitutions
                        .Where(i => i.Id == r.EntityId)
                        .Select(i => i.Name)
                        .FirstOrDefault())
                    .FirstOrDefault() ?? "",

                PatientName = header.PhSQuoteRoles
                    .Where(r => r.Entitytype == PhSEntityTypes.Patient)
                    .Select(r => _context.PhSPatients
                        .Where(pt => pt.Id == r.EntityId)
                        .Select(pt => (pt.Firstname + " " + pt.Lastname).Trim())
                        .FirstOrDefault())
                    .FirstOrDefault() ?? ""
            };
        }
        #endregion
        #region Guardado completo de presupuesto (encabezado + detalles + roles + ajustes + impuestos)
        /// <summary>
        /// Crea un nuevo presupuesto, incluyendo encabezado, detalles, roles, ajustes e impuestos asociados.
        /// Genera automáticamente el número de presupuesto en base a la serie indicada.
        /// <param name="quote">Presupuesto a registrar, incluyendo entidades relacionadas.</param>
        /// <param name="formSeriesId">Identificador de la serie de numeración a utilizar.</param>
        /// <returns>Cadena con el número generado del presupuesto.</returns>
        /// </summary>
        public async Task<(int Id, string Quotenumber)> CreateFullQuoteAsync(EQuoteHeader quote, int formSeriesId)
        {
            using var transaction = await _context.Database.BeginTransactionAsync();
            try
            {
                var nextNumber = await _formSeriesRepository.GetNextInternalNumberAsync(formSeriesId);
                var series = await _formSeriesRepository.GetByIdAsync(formSeriesId)
                                   ?? throw new InvalidOperationException("Serie no encontrada");
                int padding = 8; /* format "00000000" */
                quote.Status = "Emitido";
                quote.Quotenumber = $"{series.Letter}-{nextNumber.ToString($"D{padding}")}";

                var headerEntity = EntityMapper.MapEntity<EQuoteHeader, PhSQuoteHeader>(quote);
                _context.PhSQuoteHeaders.Add(headerEntity);

                await _context.SaveChangesAsync();
                await transaction.CommitAsync();
                return (headerEntity.Id, headerEntity.Quotenumber); ;
           }
            catch
            {
                await transaction.RollbackAsync();
                throw;
            }
        }
        #endregion
        #region Autorización de presupuesto (aprobar/rechazar ítems)
        public async Task<bool> AuthorizeQuoteAsync(int quoteId, List<EQuoteDetail> approvedItems)
        {
            var header = await _context.PhSQuoteHeaders
                .Include(h => h.PhSQuoteDetails)
                .FirstOrDefaultAsync(h => h.Id == quoteId);

            if (header == null)
                return false;

            bool anyApproved = false;

            foreach (var item in approvedItems)
            {
                var detail = header.PhSQuoteDetails.FirstOrDefault(d => d.Id == item.Id);
                if (detail == null)
                    continue;

                detail.Approved = item.Approved;
                detail.Approvedquantity = item.Approved ? item.Approvedquantity : null;
                detail.Approvedunitprice = item.Approved ? item.Approvedunitprice : null;
                detail.Approvedamount = item.Approved && item.Approvedquantity.HasValue && item.Approvedunitprice.HasValue
                    ? item.Approvedquantity.Value * item.Approvedunitprice.Value
                    : null;

                if (item.Approved)
                    anyApproved = true;

                detail.Modifiedat = DateTime.Now;
            }

            if (anyApproved)
            {
                header.Status = "Aprobado";
                header.Approvaldate = DateTime.Now;
                header.Approvedamount = header.PhSQuoteDetails
                    .Where(d => d.Approved && d.Approvedamount.HasValue)
                    .Sum(d => d.Approvedamount.Value);
            }
            else
            {
                header.Status = "Anulado";
                header.Approvaldate = DateTime.Now;
                header.Approvedamount = 0;
            }
            header.Modifiedat = DateTime.Now;

            await _context.SaveChangesAsync();
            return true;
        }
        #endregion
    }
}