import unittest
from unittest.mock import patch, MagicMock
import time
import os
import logging
from datetime import datetime, timedelta

# Importiere unsere Module
from trading_twins.orchestrator import TradingTwinsOrchestrator
from trading_twins.manager import TradingTwinsManager
from trading_twins.models import init_db, ProposalJob, ProposedSlot

# Logging reduzieren
logging.basicConfig(level=logging.INFO)

class TestTradingTwinsDryRun(unittest.TestCase):
    @classmethod
    def setUpClass(cls):
        # Wir nutzen eine temporäre Test-Datenbank
        cls.test_db_path = 'sqlite:///trading_twins/test_dry_run.db'
        if os.path.exists("trading_twins/test_dry_run.db"):
            os.remove("trading_twins/test_dry_run.db")
        
        # Manager mit Test-DB initialisieren
        cls.manager = TradingTwinsManager(db_path=cls.test_db_path)

    def setUp(self):
        # Orchestrator neu erstellen
        self.orchestrator = TradingTwinsOrchestrator()
        self.orchestrator.manager = self.manager # Inject Test Manager
        
        # Timer drastisch verkürzen für Tests
        self.orchestrator_timeout_patch = patch('trading_twins.orchestrator.TIMEOUT_SECONDS', 2)
        self.orchestrator_timeout_patch.start()

    def tearDown(self):
        self.orchestrator_timeout_patch.stop()

    @patch('trading_twins.orchestrator.send_email_via_graph')
    @patch('trading_twins.orchestrator.send_approval_card')
    def test_1_happy_path_timeout(self, mock_teams, mock_email):
        """Testet den automatischen Versand nach Timeout."""
        print("\n--- TEST 1: Happy Path (Timeout -> Auto-Send) ---")
        mock_teams.return_value = True
        mock_email.return_value = True

        # Lead verarbeiten
        job_uuid = self.orchestrator.process_lead("test1@example.com", "Kunde Eins", "Firma A")
        
        print(f"Job {job_uuid} gestartet. Warte auf Timeout (2s).")
        time.sleep(3) # Warte länger als Timeout

        # Prüfungen
        mock_teams.assert_called_once()
        mock_email.assert_called_once() # E-Mail muss versendet worden sein
        
        status = self.manager.get_job_status(job_uuid)
        self.assertEqual(status, 'sent')
        print("✅ E-Mail wurde automatisch versendet.")

    @patch('trading_twins.orchestrator.send_email_via_graph')
    @patch('trading_twins.orchestrator.send_approval_card')
    def test_2_manual_cancel(self, mock_teams, mock_email):
        """Testet den manuellen Abbruch."""
        print("\n--- TEST 2: Manueller Abbruch (STOP) ---")
        mock_teams.return_value = True

        # Lead verarbeiten
        job_uuid = self.orchestrator.process_lead("test2@example.com", "Kunde Zwei", "Firma B")
        
        # Simuliere Klick auf "STOP" (direkter DB-Update wie API es tun würde)
        print("Simuliere Klick auf 'STOP'...")
        self.manager.update_job_status(job_uuid, 'cancelled')
        
        print("Warte auf Timeout (2s).")
        time.sleep(3)

        # Prüfungen
        mock_teams.assert_called_once()
        mock_email.assert_not_called() # E-Mail darf NICHT versendet werden
        
        status = self.manager.get_job_status(job_uuid)
        self.assertEqual(status, 'cancelled')
        print("✅ Abbruch erfolgreich, keine E-Mail gesendet.")

    @patch('trading_twins.manager.TradingTwinsManager._mock_calendar_availability')
    def test_3_overbooking_factor_3(self, mock_calendar):
        """
        Testet die Faktor-3 Logik.
        Wir stellen 4 Slots zur Verfügung.
        Wir erzeugen 4 Leads.
        Die ersten 3 sollten Slot A bekommen.
        Der 4. Lead sollte Slot A NICHT mehr bekommen, sondern Slot B (oder andere).
        """
        print("\n--- TEST 3: Überbuchungs-Logik (Faktor 3) ---")
        
        # Setup Mock Calendar: Gibt immer dieselben 4 Slots zurück
        tomorrow = datetime.now().date() + timedelta(days=1)
        slot_a = {'start': datetime.combine(tomorrow, datetime.min.time().replace(hour=10)), 'end': datetime.combine(tomorrow, datetime.min.time().replace(hour=10, minute=45))}
        slot_b = {'start': datetime.combine(tomorrow, datetime.min.time().replace(hour=11)), 'end': datetime.combine(tomorrow, datetime.min.time().replace(hour=11, minute=45))}
        slot_c = {'start': datetime.combine(tomorrow, datetime.min.time().replace(hour=14)), 'end': datetime.combine(tomorrow, datetime.min.time().replace(hour=14, minute=45))}
        
        # Mock gibt diese Liste zurück
        mock_calendar.return_value = [slot_a, slot_b, slot_c]

        # Wir feuern 4 Leads ab
        uuids = []
        for i in range(1, 5):
            uuid, slots = self.manager.create_proposal_job(f"bulk{i}@test.com", f"Bulk {i}", "Bulk Corp")
            uuids.append((uuid, slots))
            # print(f"Lead {i} bekam Slots: {[s['start'].strftime('%H:%M') for s in slots]}")

        # Analyse
        # Lead 1: Bekommt A, B (A hat Count 0 -> 1)
        # Lead 2: Bekommt A, B (A hat Count 1 -> 2)
        # Lead 3: Bekommt A, B (A hat Count 2 -> 3 -> VOLL)
        # Lead 4: Sollte A NICHT bekommen, sondern B, C
        
        slots_lead_1 = uuids[0][1]
        slots_lead_4 = uuids[3][1]
        
        start_time_lead_1_first_slot = slots_lead_1[0]['start']
        start_time_lead_4_first_slot = slots_lead_4[0]['start']

        print(f"Lead 1 Slot 1: {start_time_lead_1_first_slot}")
        print(f"Lead 4 Slot 1: {start_time_lead_4_first_slot}")

        if start_time_lead_1_first_slot != start_time_lead_4_first_slot:
            print("✅ Faktor-3 Logik greift: Lead 4 hat einen anderen Start-Slot bekommen!")
        else:
            print("❌ Faktor-3 Logik fehlgeschlagen: Lead 4 hat denselben Slot bekommen.")
            # Debug
            session = self.manager.Session()
            count = session.query(ProposedSlot).filter(ProposedSlot.start_time == start_time_lead_1_first_slot).count()
            print(f"Total entries for Slot A: {count}")
            session.close()

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