"""
End-to-End Tests for Async API Workflow

These tests validate the complete async API workflow as documented in the README:
1. Upload Phase - File upload to S3
2. Background Processing - Server-side processing
3. Results Retrieval - Polling until completion

Note: These tests make REAL API calls to the demo server with REAL audio files.
Set VALENCE_E2E_TEST=true and VALENCE_API_KEY to run these tests.
"""

import os
import pytest
import time
from valenceai import ValenceClient
from valenceai.exceptions import PredictionError


# Skip E2E tests unless explicitly enabled
pytestmark = pytest.mark.skipif(
    os.getenv('VALENCE_E2E_TEST') != 'true',
    reason="E2E tests only run when VALENCE_E2E_TEST=true"
)

# Path to real test audio files
TEST_AUDIO_DIR = "/Users/julianolarte/code/valenceai/valence-backend-api/tests/fixtures"
TEST_SHORT_AUDIO = os.path.join(TEST_AUDIO_DIR, "test_file.wav")
TEST_LONG_AUDIO = os.path.join(TEST_AUDIO_DIR, "long_file_sample.wav")


class TestAsyncWorkflowE2E:
    """
    End-to-End tests for the complete async API workflow.
    These tests make REAL API calls and validate the workflow documented in README.md.
    """

    @pytest.fixture(autouse=True)
    def setup(self):
        """Setup test fixtures"""
        # Check for API key
        self.api_key = os.getenv('VALENCE_API_KEY')
        if not self.api_key:
            pytest.skip("VALENCE_API_KEY not set")

        # Use demo base URL by default
        self.base_url = os.getenv('VALENCE_API_BASE_URL', 'https://demo.getvalenceai.com')

        self.client = ValenceClient(
            api_key=self.api_key,
            base_url=self.base_url,
            show_progress=True
        )

        # Verify test files exist
        if not os.path.exists(TEST_SHORT_AUDIO):
            pytest.skip(f"Test audio file not found: {TEST_SHORT_AUDIO}")
        if not os.path.exists(TEST_LONG_AUDIO):
            pytest.skip(f"Test audio file not found: {TEST_LONG_AUDIO}")

    def test_async_workflow_complete_cycle(self):
        """
        Test the complete async workflow with a real audio file:
        1. Upload file to S3
        2. Wait for background processing
        3. Retrieve results

        This validates the workflow documented in README Example 2.
        """
        print("\n" + "="*70)
        print("Testing Complete Async Workflow")
        print("="*70)

        # STEP 1: Upload Phase - File is uploaded to S3 in parts
        print("\nSTEP 1: Upload Phase - File upload to S3")
        print("-" * 70)
        print(f"Using test file: {os.path.basename(TEST_LONG_AUDIO)}")

        start_upload = time.time()
        request_id = self.client.asynch.upload(TEST_LONG_AUDIO)
        upload_time = time.time() - start_upload

        print(f"✓ Upload to S3 complete!")
        print(f"  Request ID: {request_id}")
        print(f"  Upload duration: {upload_time:.2f} seconds")
        print(f"  Note: File is uploaded but NOT processed yet")

        # Verify request_id is returned (tracking ID, NOT completion)
        assert request_id is not None, "request_id should not be None"
        assert isinstance(request_id, str), "request_id should be a string"
        assert len(request_id) > 0, "request_id should not be empty"

        # STEP 2: Background Processing - Server automatically processes the audio
        print("\nSTEP 2: Background Processing Phase")
        print("-" * 70)
        print("  Server automatically:")
        print("  - Detects uploaded file (checks every 10 seconds)")
        print("  - Downloads audio from S3")
        print("  - Splits into 5-second segments")
        print("  - Runs ML model on each chunk")
        print("  - Stores results in database")
        print(f"  Status progression: initiated → upload_completed → processing → completed")

        # STEP 3: Poll for Results - Wait until processing is complete
        print("\nSTEP 3: Results Retrieval Phase - Polling for completion")
        print("-" * 70)

        start_poll = time.time()
        result = self.client.asynch.emotions(
            request_id,
            max_attempts=50,
            interval_seconds=10
        )
        poll_time = time.time() - start_poll

        print(f"✓ Processing complete!")
        print(f"  Status: {result['status']}")
        print(f"  Processing duration: {poll_time:.2f} seconds")
        print(f"  Total emotion data points: {len(result['emotions'])}")

        # Verify results
        assert result['status'] == 'completed', f"Expected status 'completed', got '{result['status']}'"
        assert 'emotions' in result, "Result should contain 'emotions' key"
        assert len(result['emotions']) > 0, "Emotions list should not be empty"

        # Verify emotion data structure
        first_emotion = result['emotions'][0]
        required_fields = ['timestamp', 'start_time', 'end_time', 'emotion', 'confidence']
        for field in required_fields:
            assert field in first_emotion, f"Emotion data should contain '{field}' field"

        print(f"\n  Sample emotion data:")
        for i, emotion in enumerate(result['emotions'][:3]):
            print(f"    [{i+1}] {emotion['start_time']:.1f}s-{emotion['end_time']:.1f}s: "
                  f"{emotion['emotion']} ({emotion['confidence']:.2%})")

        print("\n" + "="*70)
        print("✅ COMPLETE WORKFLOW TEST PASSED")
        print(f"   Total time: {upload_time + poll_time:.2f} seconds")
        print("="*70)

    def test_async_workflow_timeline_methods(self):
        """
        Test timeline analysis methods after processing completes.
        Validates get_timeline(), get_emotion_at_time(), and get_dominant_emotion().
        """
        print("\n" + "="*70)
        print("Testing Timeline Analysis Methods")
        print("="*70)

        # Upload and wait for processing
        print(f"\nUploading: {os.path.basename(TEST_LONG_AUDIO)}")
        request_id = self.client.asynch.upload(TEST_LONG_AUDIO)
        print(f"Request ID: {request_id}")

        print("\nWaiting for processing...")
        result = self.client.asynch.emotions(request_id, max_attempts=50, interval_seconds=10)
        assert result['status'] == 'completed'
        print(f"✓ Processing complete")

        # Test get_timeline()
        print("\n1. Testing get_timeline()")
        print("-" * 70)
        timeline = self.client.asynch.get_timeline(request_id)

        assert timeline is not None, "Timeline should not be None"
        assert len(timeline) > 0, "Timeline should have data points"
        assert all('emotion' in point for point in timeline), "All timeline points should have 'emotion' field"

        print(f"✓ Timeline retrieved: {len(timeline)} data points")
        print(f"  First emotion: {timeline[0]['emotion']}")
        print(f"  Last emotion: {timeline[-1]['emotion']}")

        # Test get_emotion_at_time()
        print("\n2. Testing get_emotion_at_time()")
        print("-" * 70)
        mid_point = timeline[len(timeline)//2]
        mid_time = mid_point['timestamp']

        emotion_at_time = self.client.asynch.get_emotion_at_time(request_id, mid_time)

        assert emotion_at_time is not None, "Emotion at time should not be None"
        assert 'emotion' in emotion_at_time, "Result should contain 'emotion' field"

        print(f"✓ Emotion at {mid_time:.1f}s: {emotion_at_time['emotion']}")
        print(f"  Confidence: {emotion_at_time.get('confidence', 'N/A')}")

        # Test get_dominant_emotion()
        print("\n3. Testing get_dominant_emotion()")
        print("-" * 70)
        dominant = self.client.asynch.get_dominant_emotion(request_id)

        assert dominant is not None, "Dominant emotion should not be None"
        assert isinstance(dominant, str), "Dominant emotion should be a string"

        # Count emotions to verify
        emotion_counts = {}
        for point in timeline:
            emotion = point['emotion']
            emotion_counts[emotion] = emotion_counts.get(emotion, 0) + 1

        print(f"✓ Dominant emotion: {dominant}")
        print(f"  Emotion distribution:")
        for emotion, count in sorted(emotion_counts.items(), key=lambda x: x[1], reverse=True):
            percentage = (count / len(timeline)) * 100
            print(f"    {emotion}: {count} ({percentage:.1f}%)")

        print("\n" + "="*70)
        print("✅ TIMELINE METHODS TEST PASSED")
        print("="*70)

    def test_async_workflow_status_progression(self):
        """
        Test that status progresses through expected states.
        Attempts to capture status changes during processing.
        """
        print("\n" + "="*70)
        print("Testing Status Progression")
        print("="*70)

        # Upload
        print(f"\nUploading: {os.path.basename(TEST_LONG_AUDIO)}")
        request_id = self.client.asynch.upload(TEST_LONG_AUDIO)
        print(f"✓ Upload complete. Request ID: {request_id}")

        # Poll with small intervals to catch status changes
        print("\nPolling status with 5-second intervals...")
        print("Expected progression: initiated → upload_completed → processing → completed")
        print("-" * 70)

        statuses_seen = []
        max_checks = 50

        for i in range(max_checks):
            time.sleep(5)

            # Make manual status check
            import requests
            url = f"{self.base_url}/v1/asynch/emotion/status/{request_id}"
            headers = {"x-api-key": self.api_key}

            try:
                response = requests.get(url, headers=headers)
                if response.status_code == 200:
                    data = response.json()
                    status = data.get('status')

                    if status and status not in statuses_seen:
                        statuses_seen.append(status)
                        print(f"  [{i*5:3d}s] Status changed to: {status}")

                    if status == 'completed':
                        print(f"\n✓ Processing completed after {i*5} seconds")
                        break
            except Exception as e:
                print(f"  Warning: Status check error: {e}")

        print(f"\nStatus progression observed: {' → '.join(statuses_seen)}")

        assert 'completed' in statuses_seen, "Should reach 'completed' status"

        print("\n" + "="*70)
        print("✅ STATUS PROGRESSION TEST PASSED")
        print("="*70)

    def test_request_id_is_tracking_not_completion(self):
        """
        Verify that request_id is returned immediately after upload,
        NOT after processing completes.

        This validates the README statement:
        "Returns a request_id - This is a tracking identifier, NOT a completion signal"
        """
        print("\n" + "="*70)
        print("Testing: request_id is Tracking ID (NOT Completion Signal)")
        print("="*70)

        # Measure upload time
        print(f"\nUploading: {os.path.basename(TEST_LONG_AUDIO)}")
        print("Measuring time to receive request_id...")

        start_time = time.time()
        request_id = self.client.asynch.upload(TEST_LONG_AUDIO)
        upload_duration = time.time() - start_time

        print(f"\n✓ request_id received: {request_id}")
        print(f"  Time to get request_id: {upload_duration:.2f} seconds")
        print(f"  ⚠️  This is ONLY the S3 upload time")

        # Verify request_id is available quickly
        assert request_id is not None
        assert isinstance(request_id, str)
        assert upload_duration < 60, "Upload should be relatively quick (< 60s)"

        # Now measure actual processing time
        print(f"\nNow polling for actual processing completion...")
        print("This will take longer because server needs to process audio...")

        process_start = time.time()
        result = self.client.asynch.emotions(request_id, max_attempts=50, interval_seconds=10)
        process_duration = time.time() - process_start

        print(f"\n✓ Processing complete: {result['status']}")
        print(f"  Time to process: {process_duration:.2f} seconds")

        print("\n" + "-"*70)
        print("PROOF THAT request_id ≠ COMPLETION:")
        print("-"*70)
        print(f"  Upload returned request_id in:  {upload_duration:6.2f} seconds")
        print(f"  But processing actually took:    {process_duration:6.2f} seconds")
        print(f"  Difference:                      {process_duration - upload_duration:6.2f} seconds")
        print(f"\n  ✓ Therefore: request_id is a TRACKING ID, not a completion signal")

        # Processing should take longer than just getting request_id
        assert process_duration > upload_duration, \
            "Processing time should be greater than upload time"

        print("\n" + "="*70)
        print("✅ TRACKING ID VALIDATION TEST PASSED")
        print("="*70)

    def test_async_workflow_short_audio(self):
        """
        Test async workflow with a shorter audio file.
        """
        print("\n" + "="*70)
        print("Testing Async Workflow with Short Audio")
        print("="*70)

        print(f"\nUploading: {os.path.basename(TEST_SHORT_AUDIO)}")

        # Upload
        request_id = self.client.asynch.upload(TEST_SHORT_AUDIO)
        print(f"✓ Request ID: {request_id}")

        # Process
        print("\nProcessing...")
        result = self.client.asynch.emotions(request_id, max_attempts=30, interval_seconds=10)

        print(f"✓ Status: {result['status']}")
        print(f"  Emotion data points: {len(result['emotions'])}")

        assert result['status'] == 'completed'
        assert len(result['emotions']) > 0

        print("\n" + "="*70)
        print("✅ SHORT AUDIO TEST PASSED")
        print("="*70)


if __name__ == "__main__":
    print("\n" + "="*70)
    print("Valence SDK - Async API E2E Tests")
    print("="*70)
    print("\nTo run these tests:")
    print("  export VALENCE_E2E_TEST=true")
    print("  export VALENCE_API_KEY=your_api_key")
    print("  pytest tests/test_e2e_async_workflow.py -v -s")
    print("\n" + "="*70)
