#!/usr/bin/env python3
"""
Genesis Integration Test Suite
================================
Integration tests for Genesis system components.

PM-050: Integration Test Suite Enhancement
- Tests skills, gates, and API integration
- Provides coverage metrics
- Supports CI/CD integration
"""

import os
import sys
import json
import time
import logging
import unittest
from typing import Dict, Any, List, Optional
from datetime import datetime
from dataclasses import dataclass, asdict
from io import StringIO

# Add project root to path
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))

logger = logging.getLogger("IntegrationTestSuite")
logging.basicConfig(level=logging.INFO)


@dataclass
class TestResult:
    """Result of a test execution."""
    test_name: str
    status: str  # passed, failed, skipped, error
    duration_ms: float
    message: Optional[str]
    timestamp: str

    def to_dict(self) -> Dict[str, Any]:
        return asdict(self)


class IntegrationTestSuite:
    """
    Integration test suite for Genesis system.
    Tests component interactions and end-to-end workflows.
    """

    def __init__(self, verbose: bool = True):
        """
        Initialize test suite.

        Args:
            verbose: Print detailed output
        """
        self.verbose = verbose
        self.results: List[TestResult] = []
        self.start_time: Optional[datetime] = None
        self.end_time: Optional[datetime] = None

        logger.info("Integration Test Suite initialized")

    def _run_test(self, test_name: str, test_fn) -> TestResult:
        """Run a single test and capture result."""
        start = datetime.utcnow()

        try:
            test_fn()
            status = "passed"
            message = None
        except AssertionError as e:
            status = "failed"
            message = str(e)
        except Exception as e:
            status = "error"
            message = f"{type(e).__name__}: {str(e)}"

        duration = (datetime.utcnow() - start).total_seconds() * 1000

        result = TestResult(
            test_name=test_name,
            status=status,
            duration_ms=round(duration, 2),
            message=message,
            timestamp=datetime.utcnow().isoformat()
        )

        self.results.append(result)

        if self.verbose:
            symbol = {"passed": ".", "failed": "F", "error": "E", "skipped": "S"}
            print(symbol.get(status, "?"), end="", flush=True)

        return result

    # ===== SKILL INTEGRATION TESTS =====

    def test_ghl_skill_initialization(self):
        """Test GHL skill can be initialized."""
        from skills.ghl_mastery_skill import GHLMasterySkill
        skill = GHLMasterySkill()
        assert skill is not None
        assert hasattr(skill, 'execute')
        assert hasattr(skill, 'create_contact')

    def test_ghl_skill_execute_interface(self):
        """Test GHL skill execute interface."""
        from skills.ghl_mastery_skill import GHLMasterySkill
        skill = GHLMasterySkill()
        result = skill.execute("unknown_action")
        assert "error" in result
        assert "available" in result

    def test_telnyx_skill_initialization(self):
        """Test Telnyx skill can be initialized."""
        from skills.telnyx_manager_skill import TelnyxManagerSkill
        skill = TelnyxManagerSkill()
        assert skill is not None
        assert hasattr(skill, 'execute')
        assert hasattr(skill, 'dial_number')

    def test_instantly_skill_initialization(self):
        """Test Instantly skill can be initialized."""
        from skills.instantly_skill import InstantlySkill
        skill = InstantlySkill()
        assert skill is not None
        assert hasattr(skill, 'execute')
        assert hasattr(skill, 'create_campaign')

    def test_tradie_funnel_skill(self):
        """Test Tradie Funnel skill end-to-end."""
        from skills.tradie_funnel_architect_skill import TradieFunnelArchitectSkill
        skill = TradieFunnelArchitectSkill()

        # Capture lead
        lead = skill.capture_lead(
            first_name="Test",
            last_name="Lead",
            email="test@example.com",
            phone="+61400000000",
            niche="plumber",
            service_area="Sydney"
        )
        assert lead.lead_id is not None
        assert lead.stage == "capture"
        assert lead.score >= 0

        # Qualify lead
        lead = skill.qualify_lead(
            lead_id=lead.lead_id,
            qualification_data={"urgency": "emergency", "has_budget": True}
        )
        assert lead.stage == "qualify"
        assert lead.score > 50

    def test_voice_wrapper_skill(self):
        """Test Voice Wrapper skill initialization."""
        from skills.voice_wrapper_skill import VoiceWrapperSkill
        skill = VoiceWrapperSkill()
        assert skill is not None
        assert hasattr(skill, 'execute')
        assert hasattr(skill, 'handle_ghl_webhook_trigger')

    # ===== VALIDATION GATE TESTS =====

    def test_gate1_input_validation(self):
        """Test Validation Gate 1 - Input validation."""
        from AIVA.validation_gate1 import InputValidationGate

        gate = InputValidationGate()

        # Valid input
        score, checks = gate.validate(
            {"task_id": "test_123", "description": "Test task"},
            "task"
        )
        assert score > 0
        assert checks["format_valid"] is True

        # Invalid input
        score, checks = gate.validate("not a dict", "task")
        assert checks["format_valid"] is False

    def test_gate1_pii_detection(self):
        """Test Gate 1 PII detection."""
        from AIVA.validation_gate1 import InputValidationGate

        gate = InputValidationGate()

        # Input with PII
        score, checks = gate.validate(
            {"message": "Call me at 123-456-7890"},
            "message"
        )
        assert checks["no_pii_exposure"] is False

    def test_gate2_output_quality(self):
        """Test Validation Gate 2 - Output quality."""
        from AIVA.validation_gate2 import OutputQualityGate

        gate = OutputQualityGate()

        # Good output
        good_output = """
        Analysis complete. Here are the findings:
        1. Total leads: 150
        2. Conversion rate: 23%
        All data validated and synced.
        """
        score, checks = gate.validate(good_output, "Analyze lead data")
        assert score > 0.5

        # Poor output
        poor_output = "I'm not sure about this."
        score, checks = gate.validate(poor_output, "Complete the analysis")
        assert score < 0.8

    def test_gate3_safety_validation(self):
        """Test Validation Gate 3 - Safety."""
        from AIVA.validation_gate3 import SafetyValidationGate

        gate = SafetyValidationGate()

        # Safe action
        score, checks = gate.validate("Read the configuration file")
        assert checks["no_dangerous_ops"] is True

        # Dangerous action
        score, checks = gate.validate("rm -rf /tmp/*")
        assert checks["no_dangerous_ops"] is False

    # ===== REVENUE TESTS =====

    def test_revenue_tracker(self):
        """Test Revenue Tracker functionality."""
        from core.revenue.revenue_tracker import RevenueTracker
        import tempfile

        tracker = RevenueTracker(storage_path=tempfile.mkdtemp())

        # Log revenue
        event = tracker.log(
            source="test_source",
            amount=100.00,
            attribution_id="test_attr",
            attribution_type="direct"
        )
        assert event.event_id is not None
        assert event.amount == 100.00

        # Check totals
        daily = tracker.get_daily_total()
        assert daily >= 100.00

    def test_attribution_engine(self):
        """Test Revenue Attribution Engine."""
        from core.revenue.attribution_engine import AttributionEngine
        import tempfile

        engine = AttributionEngine(storage_path=tempfile.mkdtemp())

        # Direct attribution
        record = engine.attribute(
            revenue_event_id="rev_123",
            aiva_action_id="action_456",
            attribution_type="direct",
            amount=500.00
        )
        assert record.attribution_id is not None
        assert record.attributed_amount == 500.00

        # Check action revenue
        action_rev = engine.get_action_revenue("action_456")
        assert action_rev["total_attributed"] == 500.00

    # ===== API TESTS =====

    def test_analytics_api(self):
        """Test Analytics API endpoints."""
        from api.analytics_endpoint import AnalyticsAPI

        api = AnalyticsAPI()

        # Revenue metrics
        revenue = api.get_revenue_metrics()
        assert "totals" in revenue

        # Task metrics
        tasks = api.get_task_metrics()
        assert "total_tasks" in tasks

        # Agent metrics
        agents = api.get_agent_metrics()
        assert "total_agents" in agents

    def test_webhook_receiver(self):
        """Test Webhook Receiver."""
        from api.webhook_receiver import WebhookReceiver

        receiver = WebhookReceiver()
        receiver.setup_default_handlers()

        # GHL webhook
        result = receiver.receive("ghl", {
            "event": "contact.created",
            "contact": {"id": "test_123", "email": "test@example.com"}
        })
        assert result["status"] == "received"
        assert result["event_type"] == "contact.created"

    def test_health_check_api(self):
        """Test Health Check API."""
        from api.health_check import HealthCheckAPI

        api = HealthCheckAPI()
        api.setup_default_checks()

        # Liveness
        liveness = api.liveness()
        assert liveness["status"] == "healthy"

        # Readiness
        readiness = api.readiness()
        assert "status" in readiness
        assert "components" in readiness

    # ===== NOTIFICATION TESTS =====

    def test_slack_notifier(self):
        """Test Slack Notifier."""
        from tools.slack_notifier import SlackNotifier

        notifier = SlackNotifier()

        # Send (will skip if not configured)
        result = notifier.send("Test message")
        assert result.notification_id is not None

        # Metrics
        metrics = notifier.get_metrics()
        assert "total_notifications" in metrics

    def test_email_notifier(self):
        """Test Email Notifier."""
        from tools.email_notifier import EmailNotifier

        notifier = EmailNotifier()

        # Send (will skip if not configured)
        result = notifier.send(
            to="test@example.com",
            subject="Test",
            body="Test message"
        )
        assert result.email_id is not None

        # Template
        html = notifier.create_html_template("Test", "Content")
        assert "<html>" in html

    # ===== TEST RUNNER =====

    def run_all(self) -> Dict[str, Any]:
        """Run all integration tests."""
        self.start_time = datetime.utcnow()
        self.results = []

        if self.verbose:
            print("\n" + "=" * 60)
            print("Genesis Integration Test Suite")
            print("=" * 60 + "\n")

        # Discover and run tests
        test_methods = [
            m for m in dir(self)
            if m.startswith("test_") and callable(getattr(self, m))
        ]

        for test_name in sorted(test_methods):
            test_fn = getattr(self, test_name)
            self._run_test(test_name, test_fn)

        self.end_time = datetime.utcnow()

        if self.verbose:
            print("\n")
            self._print_summary()

        return self.get_summary()

    def _print_summary(self):
        """Print test summary."""
        passed = sum(1 for r in self.results if r.status == "passed")
        failed = sum(1 for r in self.results if r.status == "failed")
        errors = sum(1 for r in self.results if r.status == "error")
        skipped = sum(1 for r in self.results if r.status == "skipped")
        total = len(self.results)

        duration = (self.end_time - self.start_time).total_seconds()

        print("=" * 60)
        print(f"Tests: {total} | Passed: {passed} | Failed: {failed} | Errors: {errors} | Skipped: {skipped}")
        print(f"Duration: {duration:.2f}s")
        print("=" * 60)

        # Print failures
        failures = [r for r in self.results if r.status in ["failed", "error"]]
        if failures:
            print("\nFailures:")
            for f in failures:
                print(f"  - {f.test_name}: {f.message}")

    def get_summary(self) -> Dict[str, Any]:
        """Get test run summary."""
        passed = sum(1 for r in self.results if r.status == "passed")
        failed = sum(1 for r in self.results if r.status == "failed")
        errors = sum(1 for r in self.results if r.status == "error")
        skipped = sum(1 for r in self.results if r.status == "skipped")

        duration = (self.end_time - self.start_time).total_seconds() if self.end_time else 0

        return {
            "total": len(self.results),
            "passed": passed,
            "failed": failed,
            "errors": errors,
            "skipped": skipped,
            "pass_rate": round(passed / max(1, len(self.results)) * 100, 2),
            "duration_seconds": round(duration, 2),
            "results": [r.to_dict() for r in self.results],
            "timestamp": datetime.utcnow().isoformat()
        }

    def get_coverage_report(self) -> Dict[str, Any]:
        """Get coverage report for tested modules."""
        tested_modules = {
            "skills/ghl_mastery_skill.py": any("ghl_skill" in r.test_name for r in self.results),
            "skills/telnyx_manager_skill.py": any("telnyx" in r.test_name for r in self.results),
            "skills/instantly_skill.py": any("instantly" in r.test_name for r in self.results),
            "skills/tradie_funnel_architect_skill.py": any("tradie_funnel" in r.test_name for r in self.results),
            "skills/voice_wrapper_skill.py": any("voice_wrapper" in r.test_name for r in self.results),
            "AIVA/validation_gate1.py": any("gate1" in r.test_name for r in self.results),
            "AIVA/validation_gate2.py": any("gate2" in r.test_name for r in self.results),
            "AIVA/validation_gate3.py": any("gate3" in r.test_name for r in self.results),
            "core/revenue/revenue_tracker.py": any("revenue_tracker" in r.test_name for r in self.results),
            "core/revenue/attribution_engine.py": any("attribution" in r.test_name for r in self.results),
            "api/analytics_endpoint.py": any("analytics_api" in r.test_name for r in self.results),
            "api/webhook_receiver.py": any("webhook" in r.test_name for r in self.results),
            "api/health_check.py": any("health_check" in r.test_name for r in self.results),
            "tools/slack_notifier.py": any("slack" in r.test_name for r in self.results),
            "tools/email_notifier.py": any("email" in r.test_name for r in self.results),
        }

        covered = sum(1 for v in tested_modules.values() if v)
        total = len(tested_modules)

        return {
            "modules": tested_modules,
            "covered": covered,
            "total": total,
            "coverage_percent": round(covered / total * 100, 2)
        }


def run_tests(verbose: bool = True) -> Dict[str, Any]:
    """Convenience function to run all tests."""
    suite = IntegrationTestSuite(verbose=verbose)
    return suite.run_all()


if __name__ == "__main__":
    # Run tests
    results = run_tests(verbose=True)

    # Print JSON summary
    print("\n" + "=" * 60)
    print("JSON Summary:")
    print("=" * 60)
    print(json.dumps(results, indent=2))

    # Exit with appropriate code
    sys.exit(0 if results["failed"] == 0 and results["errors"] == 0 else 1)
