import os
import sys
import time
import unittest
from datetime import datetime, date

# Agregar el directorio raíz al path
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.dirname(__file__))))

from src.services.report_606_service import Report606Service
from src.models.invoice import Invoice
from src.models.company import Company

class TestPerformance(unittest.TestCase):
    """Pruebas de rendimiento para el sistema"""
    
    def setUp(self):
        """Configuración antes de cada prueba"""
        self.service = Report606Service()
        
        # Crear empresa de prueba
        self.test_company = Company(
            id=1,
            name="Empresa de Prueba S.R.L.",
            rnc="101-12345-6",
            address="Calle de Prueba #123",
            phone="809-555-0123",
            email="test@empresa.com",
            status="active"
        )
    
    def create_test_invoices(self, count: int):
        """Crea una cantidad específica de facturas de prueba"""
        invoices = []
        for i in range(count):
            invoice = Invoice(
                id=i + 1,
                invoice_number=f"INV-2024-{i+1:06d}",
                ncf=f"B01{i+1:08d}",
                supplier_name=f"Proveedor {i+1}",
                supplier_rnc=f"101-{i+1:05d}-{(i+1) % 10}",
                supplier_id_type="RNC",
                invoice_date=date(2024, 8, (i % 28) + 1),
                payment_date=date(2024, 8, (i % 28) + 1),
                invoiced_amount=1000.00 + (i * 100),
                itbis_invoiced=180.00 + (i * 18),
                itbis_retained=0.00,
                expense_type=f"{(i % 11) + 1:02d}",
                status="approved",
                company_id=1,
                created_by_user_id=1
            )
            invoices.append(invoice)
        return invoices
    
    def test_performance_small_dataset(self):
        """Prueba de rendimiento con dataset pequeño (10 facturas)"""
        invoices = self.create_test_invoices(10)
        
        start_time = time.time()
        content = self.service._generate_606_content(self.test_company, invoices, 2024, 8)
        end_time = time.time()
        
        execution_time = end_time - start_time
        
        # Verificar que se genere correctamente
        lines = content.split("\n")
        self.assertEqual(len(lines), 11)  # 1 encabezado + 10 detalles
        
        # Verificar que sea rápido (menos de 0.1 segundos)
        self.assertLess(execution_time, 0.1)
        
        print(f"   ⏱️  10 facturas procesadas en {execution_time:.4f} segundos")
    
    def test_performance_medium_dataset(self):
        """Prueba de rendimiento con dataset mediano (100 facturas)"""
        invoices = self.create_test_invoices(100)
        
        start_time = time.time()
        content = self.service._generate_606_content(self.test_company, invoices, 2024, 8)
        end_time = time.time()
        
        execution_time = end_time - start_time
        
        # Verificar que se genere correctamente
        lines = content.split("\n")
        self.assertEqual(len(lines), 101)  # 1 encabezado + 100 detalles
        
        # Verificar que sea razonablemente rápido (menos de 0.5 segundos)
        self.assertLess(execution_time, 0.5)
        
        print(f"   ⏱️  100 facturas procesadas en {execution_time:.4f} segundos")
    
    def test_performance_large_dataset(self):
        """Prueba de rendimiento con dataset grande (1000 facturas)"""
        invoices = self.create_test_invoices(1000)
        
        start_time = time.time()
        content = self.service._generate_606_content(self.test_company, invoices, 2024, 8)
        end_time = time.time()
        
        execution_time = end_time - start_time
        
        # Verificar que se genere correctamente
        lines = content.split("\n")
        self.assertEqual(len(lines), 1001)  # 1 encabezado + 1000 detalles
        
        # Verificar que sea aceptable (menos de 2 segundos)
        self.assertLess(execution_time, 2.0)
        
        print(f"   ⏱️  1000 facturas procesadas en {execution_time:.4f} segundos")
    
    def test_performance_totals_calculation(self):
        """Prueba de rendimiento del cálculo de totales"""
        invoices = self.create_test_invoices(1000)
        
        start_time = time.time()
        totals = self.service._calculate_totals(invoices)
        end_time = time.time()
        
        execution_time = end_time - start_time
        
        # Verificar cálculos
        expected_total = sum(inv.invoiced_amount for inv in invoices)
        expected_itbis = sum(inv.itbis_invoiced for inv in invoices)
        
        self.assertEqual(totals['total_amount'], expected_total)
        self.assertEqual(totals['total_itbis'], expected_itbis)
        
        # Verificar que sea rápido (menos de 0.1 segundos)
        self.assertLess(execution_time, 0.1)
        
        print(f"   ⏱️  Totales de 1000 facturas calculados en {execution_time:.4f} segundos")
    
    def test_performance_expense_summary(self):
        """Prueba de rendimiento del resumen por tipo de gasto"""
        invoices = self.create_test_invoices(1000)
        
        start_time = time.time()
        summary = self.service._get_expense_type_summary(invoices)
        end_time = time.time()
        
        execution_time = end_time - start_time
        
        # Verificar que se generen los 11 tipos de gasto
        self.assertEqual(len(summary), 11)
        
        # Verificar que esté ordenado por monto
        amounts = [item['amount'] for item in summary]
        self.assertEqual(amounts, sorted(amounts, reverse=True))
        
        # Verificar que sea rápido (menos de 0.1 segundos)
        self.assertLess(execution_time, 0.1)
        
        print(f"   ⏱️  Resumen por tipos de gasto de 1000 facturas en {execution_time:.4f} segundos")
    
    def test_performance_supplier_summary(self):
        """Prueba de rendimiento del resumen por proveedor"""
        invoices = self.create_test_invoices(1000)
        
        start_time = time.time()
        summary = self.service._get_supplier_summary(invoices)
        end_time = time.time()
        
        execution_time = end_time - start_time
        
        # Verificar que se generen 1000 proveedores únicos
        self.assertEqual(len(summary), 1000)
        
        # Verificar que esté ordenado por monto
        amounts = [item['amount'] for item in summary]
        self.assertEqual(amounts, sorted(amounts, reverse=True))
        
        # Verificar que sea razonablemente rápido (menos de 0.2 segundos)
        self.assertLess(execution_time, 0.2)
        
        print(f"   ⏱️  Resumen por proveedores de 1000 facturas en {execution_time:.4f} segundos")
    
    def test_memory_usage_large_dataset(self):
        """Prueba de uso de memoria con dataset grande"""
        import psutil
        import os
        
        # Obtener uso de memoria inicial
        process = psutil.Process(os.getpid())
        initial_memory = process.memory_info().rss / 1024 / 1024  # MB
        
        # Crear dataset grande
        invoices = self.create_test_invoices(5000)
        
        # Generar reporte
        content = self.service._generate_606_content(self.test_company, invoices, 2024, 8)
        
        # Obtener uso de memoria final
        final_memory = process.memory_info().rss / 1024 / 1024  # MB
        memory_increase = final_memory - initial_memory
        
        # Verificar que el aumento de memoria sea razonable (menos de 50MB)
        self.assertLess(memory_increase, 50)
        
        print(f"   💾 Aumento de memoria con 5000 facturas: {memory_increase:.2f} MB")
        
        # Verificar que el contenido se genere correctamente
        lines = content.split("\n")
        self.assertEqual(len(lines), 5001)  # 1 encabezado + 5000 detalles

def run_performance_tests():
    """Ejecuta las pruebas de rendimiento"""
    print("🚀 Iniciando pruebas de rendimiento...")
    
    # Crear suite de pruebas
    loader = unittest.TestLoader()
    suite = loader.loadTestsFromTestCase(TestPerformance)
    
    # Ejecutar pruebas
    runner = unittest.TextTestRunner(verbosity=2)
    result = runner.run(suite)
    
    # Resumen de resultados
    print(f"\n📊 Resumen de pruebas de rendimiento:")
    print(f"   - Ejecutadas: {result.testsRun}")
    print(f"   - Exitosas: {result.testsRun - len(result.failures) - len(result.errors)}")
    print(f"   - Fallidas: {len(result.failures)}")
    print(f"   - Errores: {len(result.errors)}")
    
    if result.failures:
        print(f"\n❌ Pruebas fallidas:")
        for test, traceback in result.failures:
            print(f"   - {test}")
    
    if result.errors:
        print(f"\n💥 Errores en pruebas:")
        for test, traceback in result.errors:
            print(f"   - {test}")
    
    success = len(result.failures) == 0 and len(result.errors) == 0
    if success:
        print(f"\n✅ Todas las pruebas de rendimiento pasaron!")
        print(f"🎯 El sistema está optimizado para manejar grandes volúmenes de datos")
    else:
        print(f"\n❌ Algunas pruebas de rendimiento fallaron.")
    
    return success

if __name__ == '__main__':
    run_performance_tests()

