using Core.Interfaces;
using Domain.Constants;
using Domain.Dtos;
using Domain.Entities;
using Domain.Generics;
using Models.Interfaces;
using System.Drawing.Printing;
using System.Reflection;
using Transversal.Services;

namespace Core.Services
{
    public class QuoteService(IQuoteRepository quoteRepository):IQuoteDom
    {
        #region Declaraciones
        private readonly IQuoteRepository _quoteRepository = quoteRepository;
        //private readonly IPhSQuoteRepository _quoteRepository = quoteRepository;
        #endregion

        #region Presupuestos
        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, int pageSize)
        {
            return await _quoteRepository.SearchAsync(
                customerId,
                customerText,
                quoteNumber,
                professionalId,
                professionalText,
                institutionId,
                institutionText,
                patientId,
                patientText,
                issueDateFrom,
                issueDateTo,
                status,
                page,
                pageSize);
        }

        public async Task<QuoteDto?> GetDtoByIdAsync(int id)
        {
            return await _quoteRepository.GetDtoByIdAsync(id);
        }
        public async Task<byte[]> ExportFilteredToExcelAsync(QuoteSearchParams searchParams)
        {
            try
            {
                // Realiza la búsqueda de clientes con los parámetros proporcionados
                var searchResult = await _quoteRepository.SearchAsync(
                    searchParams.CustomerId,
                    searchParams.CustomerText,
                    searchParams.QuoteNumber,
                    searchParams.ProfessionalId,
                    searchParams.ProfessionalText,
                    searchParams.InstitutionId,
                    searchParams.InstitutionText,
                    searchParams.PatientId,
                    searchParams.PatientText,
                    searchParams.IssueDateFrom,
                    searchParams.IssueDateTo,
                    searchParams.Status,
                    searchParams.Page,
                    searchParams.PageSize
                );
                // Verifica que se hayan encontrado resultados
                if (searchResult?.Items is null || !searchResult.Items.Any())
                {
                    throw new Exception("No se encontraron clientes para exportar.");
                }
                // Llamamos a un método que exporta los datos a Excel
                var stream = new XLSXExportBase();
                // Convertimos los resultados de la búsqueda a un formato adecuado para el exportador
                var items = searchResult.Items.Select(c => new
                {
                    c.Quotenumber,
                    Issuedate = c.IssueDate.ToString("dd/MM/yyyy"),          // ← string
                    EstimatedDate = c.EstimatedDate?.ToString("dd/MM/yyyy HH:mm"),    // ← string
                    c.Status,
                    c.CustomerName,
                    c.ProfessionalName,
                    c.InstitutionName,
                    c.PatientName,
                    c.BusinessUnitName,
                    c.SalespersonName,
                    c.Observations,
                    c.Total
                }).ToList();
                // Genera el archivo Excel
                var excelFile = stream.ExportExcel(items);
                // Devuelve el archivo Excel como un array de bytes
                return excelFile;
            }
            catch (Exception ex)
            {
                var methodName = MethodBase.GetCurrentMethod()?.Name ?? "UnknownMethod";
                throw new Exception($"{ex.Message}", ex);
            }
        }

        //public async Task<QuoteDto?> GetDtoByQuoteNumberAsync(string quoteNumber)
        //{
        //    return await _quoteRepository.GetDtoByIdAsync(quoteNumber);
        //}
        #endregion

        #region Guardado completo de presupuesto (encabezado + detalles + roles + ajustes + impuestos)
        public async Task<(int Id, string Quotenumber)> CreateFullQuoteAsync(EQuoteHeader quote, int formSeriesId)
        {
            // 1. Validaciones antes de iniciar transacción
            ValidateQuote(quote);
            return await _quoteRepository.CreateFullQuoteAsync(quote, formSeriesId);
        }
        #endregion

        public async Task<bool> AuthorizeQuoteAsync(int quoteId, List<QuoteAuthorizationDto> items)
        {
            if (items == null)
                throw new InvalidOperationException("No se recibieron ítems para autorizar.");

            // Si no hay ítems aprobados, consideramos que es una anulación
            var approvedDetails = items
                .Where(i => i.Approved)
                .Select(i =>
                {
                    if (!i.ApprovedQuantity.HasValue || !i.ApprovedUnitPrice.HasValue)
                        throw new InvalidOperationException("Los ítems aprobados deben tener cantidad y precio válidos.");

                    return new EQuoteDetail
                    {
                        Id = i.Id,
                        Approved = true,
                        Approvedquantity = i.ApprovedQuantity,
                        Approvedunitprice = i.ApprovedUnitPrice
                    };
                }).ToList();

            // Este llamado puede interpretar lista vacía como anulación completa
            return await _quoteRepository.AuthorizeQuoteAsync(quoteId, approvedDetails);
        }

        #region Validaciones QuoteCreate
        private void ValidateQuote(EQuoteHeader quote)
        {
            if (quote == null)
                throw new ArgumentNullException(nameof(quote), "El presupuesto no puede ser nulo.");

            if (quote.CustomerId <= 0)
                throw new ArgumentException("Debe seleccionar un cliente.");

            if (quote.PeopleId <= 0)
                throw new ArgumentException("Debe seleccionar un vendedor.");

            if (string.IsNullOrWhiteSpace(quote.Currency))
                throw new ArgumentException("La moneda es obligatoria.");

            if (quote.PhSQuoteDetails == null || !quote.PhSQuoteDetails.Any())
                throw new InvalidOperationException("Debe incluir al menos un producto.");

            foreach (var detail in quote.PhSQuoteDetails)
            {
                if (detail.Quantity <= 0)
                    throw new ArgumentException($"La cantidad para el producto {detail.ProductId} debe ser mayor a cero.");
                if (detail.Unitprice < 0)
                    throw new ArgumentException($"El precio unitario del producto {detail.ProductId} no puede ser negativo.");
            }

            if (quote.PhSQuoteRoles == null || !quote.PhSQuoteRoles.Any())
                throw new InvalidOperationException("Debe asignar al menos un rol (profesional, paciente o institución).");

            var hasProfessional = quote.PhSQuoteRoles.Any(r => r.Entitytype == EntityTypes.Professional);
            var hasPatient = quote.PhSQuoteRoles.Any(r => r.Entitytype == EntityTypes.Patient);
            var hasInstitution = quote.PhSQuoteRoles.Any(r => r.Entitytype == EntityTypes.Institution);
            if (!hasProfessional)
                throw new InvalidOperationException("Debe asignar un profesional.");
            if (!hasInstitution)
                throw new InvalidOperationException("Debe asignar un paciente.");
            if (!hasPatient)
                throw new InvalidOperationException("Debe asignar un paciente.");
            if (quote.PhSQuoteTaxes != null)
            {
                foreach (var tax in quote.PhSQuoteTaxes)
                {
                    if (tax.Taxrate < 0 || tax.Taxrate > 100)
                        throw new ArgumentException($"La alícuota del impuesto '{tax.Taxname}' no es válida.");
                }
            }
            if (quote.Total < 0)
                throw new ArgumentException("El total del presupuesto no puede ser negativo.");
        }

        public async Task<QuoteDto?> GetDtoByQuoteNumberAsync(string quoteNumber)
        {
            return await _quoteRepository.GetDtoByQuoteNumberAsync(quoteNumber);
        }
        #endregion
    }
}