import unittest
import json
import os
import tempfile
from datetime import datetime, timedelta

from app import app, db, User, Printer, PrintJob

class MYPBackendTestCase(unittest.TestCase):
    def setUp(self):
        # Temporäre Datenbank für Tests
        self.db_fd, app.config['DATABASE'] = tempfile.mkstemp()
        app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///' + app.config['DATABASE']
        app.config['TESTING'] = True
        self.app = app.test_client()

        # Datenbank-Tabellen erstellen und Test-Daten einfügen
        with app.app_context():
            db.create_all()
            
            # Admin-Benutzer erstellen
            admin = User(username='admin_test', email='admin@test.com', role='admin')
            admin.set_password('admin')
            db.session.add(admin)
            
            # Normaler Benutzer erstellen
            user = User(username='user_test', email='user@test.com', role='user')
            user.set_password('user')
            db.session.add(user)
            
            # Drucker erstellen
            printer1 = Printer(name='Printer 1', location='Room A', type='3D', 
                              status='available', description='Test printer 1')
            printer2 = Printer(name='Printer 2', location='Room B', type='3D', 
                              status='busy', description='Test printer 2')
            db.session.add(printer1)
            db.session.add(printer2)
            
            # Job erstellen
            start_time = datetime.utcnow()
            end_time = start_time + timedelta(minutes=60)
            job = PrintJob(title='Test Job', start_time=start_time, end_time=end_time,
                          duration=60, status='active', comments='Test job',
                          user_id=2, printer_id=2)
            db.session.add(job)
            
            db.session.commit()
    
    def tearDown(self):
        # Aufräumen nach dem Test
        os.close(self.db_fd)
        os.unlink(app.config['DATABASE'])
    
    def get_token(self, username, password):
        response = self.app.post('/api/auth/login',
                      data=json.dumps({'username': username, 'password': password}),
                      content_type='application/json')
        data = json.loads(response.data)
        return data.get('token')
    
    def test_login(self):
        # Test: Erfolgreicher Login
        response = self.app.post('/api/auth/login',
                      data=json.dumps({'username': 'admin_test', 'password': 'admin'}),
                      content_type='application/json')
        self.assertEqual(response.status_code, 200)
        data = json.loads(response.data)
        self.assertIn('token', data)
        self.assertIn('user', data)
        
        # Test: Fehlgeschlagener Login (falsches Passwort)
        response = self.app.post('/api/auth/login',
                      data=json.dumps({'username': 'admin_test', 'password': 'wrong'}),
                      content_type='application/json')
        self.assertEqual(response.status_code, 401)
    
    def test_register(self):
        # Test: Erfolgreiche Registrierung
        response = self.app.post('/api/auth/register',
                      data=json.dumps({
                          'username': 'new_user',
                          'email': 'new@test.com',
                          'password': 'password'
                      }),
                      content_type='application/json')
        self.assertEqual(response.status_code, 201)
        
        # Test: Doppelte Registrierung
        response = self.app.post('/api/auth/register',
                      data=json.dumps({
                          'username': 'new_user',
                          'email': 'another@test.com',
                          'password': 'password'
                      }),
                      content_type='application/json')
        self.assertEqual(response.status_code, 400)
    
    def test_get_printers(self):
        # Test: Drucker abrufen
        response = self.app.get('/api/printers')
        self.assertEqual(response.status_code, 200)
        data = json.loads(response.data)
        self.assertEqual(len(data), 2)
    
    def test_get_single_printer(self):
        # Test: Einzelnen Drucker abrufen
        response = self.app.get('/api/printers/1')
        self.assertEqual(response.status_code, 200)
        data = json.loads(response.data)
        self.assertEqual(data['name'], 'Printer 1')
    
    def test_create_printer(self):
        # Als Admin einen Drucker erstellen
        token = self.get_token('admin_test', 'admin')
        response = self.app.post('/api/printers',
                      headers={'Authorization': f'Bearer {token}'},
                      data=json.dumps({
                          'name': 'New Printer',
                          'location': 'Room C',
                          'type': '3D',
                          'description': 'New test printer'
                      }),
                      content_type='application/json')
        self.assertEqual(response.status_code, 201)
        data = json.loads(response.data)
        self.assertEqual(data['name'], 'New Printer')
    
    def test_update_printer(self):
        # Als Admin einen Drucker aktualisieren
        token = self.get_token('admin_test', 'admin')
        response = self.app.put('/api/printers/1',
                     headers={'Authorization': f'Bearer {token}'},
                     data=json.dumps({
                         'name': 'Updated Printer',
                         'location': 'Room D'
                     }),
                     content_type='application/json')
        self.assertEqual(response.status_code, 200)
        data = json.loads(response.data)
        self.assertEqual(data['name'], 'Updated Printer')
        self.assertEqual(data['location'], 'Room D')
    
    def test_delete_printer(self):
        # Als Admin einen Drucker löschen
        token = self.get_token('admin_test', 'admin')
        response = self.app.delete('/api/printers/1',
                       headers={'Authorization': f'Bearer {token}'})
        self.assertEqual(response.status_code, 200)
        
        # Überprüfen, ob der Drucker wirklich gelöscht wurde
        response = self.app.get('/api/printers/1')
        self.assertEqual(response.status_code, 404)
    
    def test_get_jobs_as_admin(self):
        # Als Admin alle Jobs abrufen
        token = self.get_token('admin_test', 'admin')
        response = self.app.get('/api/jobs',
                     headers={'Authorization': f'Bearer {token}'})
        self.assertEqual(response.status_code, 200)
        data = json.loads(response.data)
        self.assertEqual(len(data), 1)
    
    def test_get_jobs_as_user(self):
        # Als normaler Benutzer nur eigene Jobs abrufen
        token = self.get_token('user_test', 'user')
        response = self.app.get('/api/jobs',
                     headers={'Authorization': f'Bearer {token}'})
        self.assertEqual(response.status_code, 200)
        data = json.loads(response.data)
        self.assertEqual(len(data), 1)  # Der Benutzer hat einen Job
    
    def test_create_job(self):
        # Als Benutzer einen Job erstellen
        token = self.get_token('user_test', 'user')
        response = self.app.post('/api/jobs',
                      headers={'Authorization': f'Bearer {token}'},
                      data=json.dumps({
                          'title': 'New Job',
                          'printer_id': 1,
                          'duration': 30,
                          'comments': 'Test job creation'
                      }),
                      content_type='application/json')
        self.assertEqual(response.status_code, 201)
        data = json.loads(response.data)
        self.assertEqual(data['title'], 'New Job')
        self.assertEqual(data['duration'], 30)
    
    def test_update_job(self):
        # Als Benutzer den eigenen Job aktualisieren
        token = self.get_token('user_test', 'user')
        response = self.app.put('/api/jobs/1',
                     headers={'Authorization': f'Bearer {token}'},
                     data=json.dumps({
                         'comments': 'Updated comments',
                         'duration': 15  # Verlängerung
                     }),
                     content_type='application/json')
        self.assertEqual(response.status_code, 200)
        data = json.loads(response.data)
        self.assertEqual(data['comments'], 'Updated comments')
        self.assertEqual(data['duration'], 75)  # 60 + 15
    
    def test_complete_job(self):
        # Als Benutzer einen Job als abgeschlossen markieren
        token = self.get_token('user_test', 'user')
        response = self.app.put('/api/jobs/1',
                     headers={'Authorization': f'Bearer {token}'},
                     data=json.dumps({
                         'status': 'completed'
                     }),
                     content_type='application/json')
        self.assertEqual(response.status_code, 200)
        data = json.loads(response.data)
        self.assertEqual(data['status'], 'completed')
        
        # Überprüfen, ob der Drucker wieder verfügbar ist
        response = self.app.get('/api/printers/2')
        self.assertEqual(response.status_code, 200)
        data = json.loads(response.data)
        self.assertEqual(data['status'], 'available')
    
    def test_get_remaining_time(self):
        # Test: Verbleibende Zeit für einen aktiven Job abrufen
        response = self.app.get('/api/job/1/remaining-time')
        self.assertEqual(response.status_code, 200)
        data = json.loads(response.data)
        self.assertIn('remaining_minutes', data)
        # Der genaue Wert kann nicht überprüft werden, da er von der Zeit abhängt
    
    def test_stats(self):
        # Als Admin Statistiken abrufen
        token = self.get_token('admin_test', 'admin')
        response = self.app.get('/api/stats',
                     headers={'Authorization': f'Bearer {token}'})
        self.assertEqual(response.status_code, 200)
        data = json.loads(response.data)
        self.assertIn('printers', data)
        self.assertIn('jobs', data)
        self.assertIn('users', data)
        self.assertEqual(data['printers']['total'], 2)
        self.assertEqual(data['jobs']['total'], 1)
        self.assertEqual(data['users']['total'], 2)
    
    def test_test_endpoint(self):
        # Test: API-Test-Endpunkt
        response = self.app.get('/api/test')
        self.assertEqual(response.status_code, 200)
        data = json.loads(response.data)
        self.assertEqual(data['message'], 'MYP Backend API funktioniert!')

if __name__ == '__main__':
    unittest.main()