import os
import sys
import json
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.main import app, init_database
from src.tests.test_data_setup import create_test_data, get_test_credentials

class TestAPIs(unittest.TestCase):
    """Pruebas de integración para las APIs del sistema"""
    
    @classmethod
    def setUpClass(cls):
        """Configuración inicial para todas las pruebas"""
        cls.app = app
        cls.app.config['TESTING'] = True
        cls.app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///:memory:'
        cls.client = cls.app.test_client()
        cls.credentials = get_test_credentials()
        
        # Crear datos de prueba
        with cls.app.app_context():
            cls.test_data = create_test_data(cls.app)
    
    def setUp(self):
        """Configuración antes de cada prueba"""
        self.app_context = self.app.app_context()
        self.app_context.push()
    
    def tearDown(self):
        """Limpieza después de cada prueba"""
        self.app_context.pop()
    
    def login_user(self, username, password):
        """Helper para hacer login de un usuario"""
        response = self.client.post('/api/auth/login', 
                                  json={'username': username, 'password': password})
        return response
    
    def test_01_auth_login_success(self):
        """Prueba de login exitoso"""
        response = self.login_user('superadmin', 'admin123')
        self.assertEqual(response.status_code, 200)
        
        data = json.loads(response.data)
        self.assertTrue(data.get('user'))
        self.assertEqual(data['user']['role'], 'super_admin')
    
    def test_02_auth_login_invalid_credentials(self):
        """Prueba de login con credenciales inválidas"""
        response = self.login_user('superadmin', 'wrongpassword')
        self.assertEqual(response.status_code, 401)
        
        data = json.loads(response.data)
        self.assertIn('error', data)
    
    def test_03_auth_me_without_login(self):
        """Prueba de obtener usuario actual sin login"""
        response = self.client.get('/api/auth/me')
        self.assertEqual(response.status_code, 401)
    
    def test_04_auth_me_with_login(self):
        """Prueba de obtener usuario actual con login"""
        # Login primero
        self.login_user('contador1', 'contador123')
        
        # Obtener usuario actual
        response = self.client.get('/api/auth/me')
        self.assertEqual(response.status_code, 200)
        
        data = json.loads(response.data)
        self.assertEqual(data['user']['role'], 'accountant')
        self.assertEqual(data['user']['username'], 'contador1')
    
    def test_05_invoices_list_as_accountant(self):
        """Prueba de listar facturas como contador"""
        # Login como contador
        self.login_user('contador1', 'contador123')
        
        # Obtener facturas
        response = self.client.get('/api/invoices')
        self.assertEqual(response.status_code, 200)
        
        data = json.loads(response.data)
        self.assertIn('invoices', data)
        self.assertGreater(len(data['invoices']), 0)
        
        # Verificar que solo ve facturas de su empresa
        for invoice in data['invoices']:
            self.assertEqual(invoice['company_id'], 1)  # Empresa 1
    
    def test_06_invoices_list_as_employee(self):
        """Prueba de listar facturas como empleado"""
        # Login como empleado
        self.login_user('empleado1', 'empleado123')
        
        # Obtener facturas
        response = self.client.get('/api/invoices')
        self.assertEqual(response.status_code, 200)
        
        data = json.loads(response.data)
        self.assertIn('invoices', data)
        
        # Verificar que solo ve sus propias facturas
        for invoice in data['invoices']:
            self.assertEqual(invoice['created_by_user_id'], 3)  # Usuario empleado1
    
    def test_07_create_invoice_manual(self):
        """Prueba de crear factura manualmente"""
        # Login como contador
        self.login_user('contador1', 'contador123')
        
        # Datos de la nueva factura
        invoice_data = {
            'supplier_name': 'Proveedor de Prueba',
            'supplier_rnc': '101-99999-9',
            'ncf': 'B0100000999',
            'invoice_date': '2024-08-25',
            'payment_date': '2024-08-30',
            'invoiced_amount': 10000.00,
            'itbis_invoiced': 1800.00,
            'expense_type': '02'
        }
        
        response = self.client.post('/api/invoices', json=invoice_data)
        self.assertEqual(response.status_code, 201)
        
        data = json.loads(response.data)
        self.assertIn('invoice', data)
        self.assertEqual(data['invoice']['supplier_name'], 'Proveedor de Prueba')
    
    def test_08_approve_invoice(self):
        """Prueba de aprobar una factura"""
        # Login como contador
        self.login_user('contador1', 'contador123')
        
        # Aprobar factura pendiente (ID 3 según datos de prueba)
        response = self.client.post('/api/invoices/3/approve')
        self.assertEqual(response.status_code, 200)
        
        data = json.loads(response.data)
        self.assertEqual(data['invoice']['status'], 'approved')
    
    def test_09_generate_606_report(self):
        """Prueba de generar reporte 606"""
        # Login como contador
        self.login_user('contador1', 'contador123')
        
        # Generar reporte para agosto 2024
        report_data = {
            'year': 2024,
            'month': 8,
            'include_pending': False
        }
        
        response = self.client.post('/api/reports/606', json=report_data)
        self.assertEqual(response.status_code, 200)
        
        data = json.loads(response.data)
        self.assertTrue(data['success'])
        self.assertIn('content', data)
        self.assertIn('filename', data)
        self.assertGreater(data['total_invoices'], 0)
        
        # Verificar formato del contenido
        content_lines = data['content'].split('\n')
        self.assertTrue(content_lines[0].startswith('606|'))  # Línea de encabezado
        self.assertTrue(content_lines[1].startswith('607|'))  # Primera línea de detalle
    
    def test_10_dashboard_data(self):
        """Prueba de obtener datos del dashboard"""
        # Login como contador
        self.login_user('contador1', 'contador123')
        
        response = self.client.get('/api/reports/dashboard')
        self.assertEqual(response.status_code, 200)
        
        data = json.loads(response.data)
        self.assertTrue(data['success'])
        self.assertIn('statistics', data)
        self.assertIn('recent_invoices', data)
        self.assertIn('expense_summary', data)
        
        # Verificar estadísticas
        stats = data['statistics']
        self.assertGreater(stats['total_invoices'], 0)
        self.assertGreaterEqual(stats['approved_invoices'], 0)
        self.assertGreaterEqual(stats['pending_invoices'], 0)
    
    def test_11_expense_types_list(self):
        """Prueba de listar tipos de gastos"""
        # Login como empleado
        self.login_user('empleado1', 'empleado123')
        
        response = self.client.get('/api/expense-types')
        self.assertEqual(response.status_code, 200)
        
        data = json.loads(response.data)
        self.assertIn('expense_types', data)
        self.assertEqual(len(data['expense_types']), 11)  # 11 tipos oficiales
        
        # Verificar que incluye los tipos principales
        types = [et['id'] for et in data['expense_types']]
        self.assertIn('01', types)
        self.assertIn('02', types)
        self.assertIn('11', types)
    
    def test_12_companies_list_as_super_admin(self):
        """Prueba de listar empresas como super admin"""
        # Login como super admin
        self.login_user('superadmin', 'admin123')
        
        response = self.client.get('/api/companies')
        self.assertEqual(response.status_code, 200)
        
        data = json.loads(response.data)
        self.assertIn('companies', data)
        self.assertEqual(len(data['companies']), 2)  # 2 empresas de prueba
    
    def test_13_companies_list_as_accountant_forbidden(self):
        """Prueba de que contador no puede listar todas las empresas"""
        # Login como contador
        self.login_user('contador1', 'contador123')
        
        response = self.client.get('/api/companies')
        self.assertEqual(response.status_code, 403)
    
    def test_14_suppliers_report(self):
        """Prueba de reporte por proveedores"""
        # Login como contador
        self.login_user('contador1', 'contador123')
        
        response = self.client.get('/api/reports/suppliers?year=2024&month=8')
        self.assertEqual(response.status_code, 200)
        
        data = json.loads(response.data)
        self.assertTrue(data['success'])
        self.assertIn('suppliers', data)
        self.assertGreater(len(data['suppliers']), 0)
        
        # Verificar estructura de datos
        supplier = data['suppliers'][0]
        self.assertIn('supplier_name', supplier)
        self.assertIn('supplier_rnc', supplier)
        self.assertIn('total_amount', supplier)
        self.assertIn('invoice_count', supplier)
    
    def test_15_expense_types_report(self):
        """Prueba de reporte por tipos de gasto"""
        # Login como contador
        self.login_user('contador1', 'contador123')
        
        response = self.client.get('/api/reports/expense-types?year=2024&month=8')
        self.assertEqual(response.status_code, 200)
        
        data = json.loads(response.data)
        self.assertTrue(data['success'])
        self.assertIn('expense_types', data)
        self.assertGreater(len(data['expense_types']), 0)
        
        # Verificar estructura de datos
        expense_type = data['expense_types'][0]
        self.assertIn('expense_type', expense_type)
        self.assertIn('expense_description', expense_type)
        self.assertIn('total_amount', expense_type)
        self.assertIn('invoice_count', expense_type)
    
    def test_16_logout(self):
        """Prueba de logout"""
        # Login primero
        self.login_user('contador1', 'contador123')
        
        # Verificar que está logueado
        response = self.client.get('/api/auth/me')
        self.assertEqual(response.status_code, 200)
        
        # Logout
        response = self.client.post('/api/auth/logout')
        self.assertEqual(response.status_code, 200)
        
        # Verificar que ya no está logueado
        response = self.client.get('/api/auth/me')
        self.assertEqual(response.status_code, 401)

def run_tests():
    """Ejecuta todas las pruebas"""
    print("🧪 Iniciando pruebas de integración de APIs...")
    
    # Crear suite de pruebas
    loader = unittest.TestLoader()
    suite = loader.loadTestsFromTestCase(TestAPIs)
    
    # Ejecutar pruebas
    runner = unittest.TextTestRunner(verbosity=2)
    result = runner.run(suite)
    
    # Resumen de resultados
    print(f"\n📊 Resumen de pruebas:")
    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}: {traceback}")
    
    if result.errors:
        print(f"\n💥 Errores en pruebas:")
        for test, traceback in result.errors:
            print(f"   - {test}: {traceback}")
    
    success = len(result.failures) == 0 and len(result.errors) == 0
    if success:
        print(f"\n✅ Todas las pruebas pasaron exitosamente!")
    else:
        print(f"\n❌ Algunas pruebas fallaron. Revisar los errores arriba.")
    
    return success

if __name__ == '__main__':
    run_tests()

