"""
Tests for the pip binary provider plugin.

Tests cover:
1. Hook script execution
2. pip package detection
3. Virtual environment handling
4. JSONL output format
"""

import json
import os
import subprocess
import sys
import tempfile
from pathlib import Path
from unittest.mock import patch, MagicMock

import pytest
from django.test import TestCase


# Get the path to the pip provider hook
PLUGIN_DIR = Path(__file__).parent.parent
INSTALL_HOOK = next(PLUGIN_DIR.glob('on_Binary__*_pip_install.py'), None)


class TestPipProviderHook(TestCase):
    """Test the pip binary provider installation hook."""

    def setUp(self):
        """Set up test environment."""
        self.temp_dir = tempfile.mkdtemp()
        self.output_dir = Path(self.temp_dir) / 'output'
        self.output_dir.mkdir()
        self.lib_dir = Path(self.temp_dir) / 'lib' / 'x86_64-linux'
        self.lib_dir.mkdir(parents=True, exist_ok=True)
        self.lib_dir = Path(self.temp_dir) / 'lib' / 'x86_64-linux'
        self.lib_dir.mkdir(parents=True, exist_ok=True)

    def tearDown(self):
        """Clean up."""
        import shutil
        shutil.rmtree(self.temp_dir, ignore_errors=True)

    def test_hook_script_exists(self):
        """Hook script should exist."""
        self.assertTrue(INSTALL_HOOK and INSTALL_HOOK.exists(), f"Hook not found: {INSTALL_HOOK}")

    def test_hook_help(self):
        """Hook should accept --help without error."""
        result = subprocess.run(
            [sys.executable, str(INSTALL_HOOK), '--help'],
            capture_output=True,
            text=True,
            timeout=30
        )
        # May succeed or fail depending on implementation
        # At minimum should not crash with Python error
        self.assertNotIn('Traceback', result.stderr)

    def test_hook_finds_pip(self):
        """Hook should find pip binary."""
        env = os.environ.copy()
        env['DATA_DIR'] = self.temp_dir
        env['LIB_DIR'] = str(self.lib_dir)

        result = subprocess.run(
            [
                sys.executable, str(INSTALL_HOOK),
                '--name=pip',
                '--binproviders=pip',
                '--binary-id=test-uuid',
                '--machine-id=test-machine',
            ],
            capture_output=True,
            text=True,
            cwd=str(self.output_dir),
            env=env,
            timeout=60
        )

        # Check for JSONL output
        jsonl_found = False
        for line in result.stdout.split('\n'):
            line = line.strip()
            if line.startswith('{'):
                try:
                    record = json.loads(line)
                    if record.get('type') == 'Binary' and record.get('name') == 'pip':
                        jsonl_found = True
                        # Verify structure
                        self.assertIn('abspath', record)
                        self.assertIn('version', record)
                        break
                except json.JSONDecodeError:
                    continue

        # Should not crash
        self.assertNotIn('Traceback', result.stderr)

        # Should find pip via pip provider
        self.assertTrue(jsonl_found, "Expected to find pip binary in JSONL output")

    def test_hook_unknown_package(self):
        """Hook should handle unknown packages gracefully."""
        env = os.environ.copy()
        env['DATA_DIR'] = self.temp_dir
        env['LIB_DIR'] = str(self.lib_dir)

        result = subprocess.run(
            [
                sys.executable, str(INSTALL_HOOK),
                '--name=nonexistent_package_xyz123',
                '--binproviders=pip',
                '--binary-id=test-uuid',
                '--machine-id=test-machine',
            ],
            capture_output=True,
            text=True,
            cwd=str(self.output_dir),
            env=env,
            timeout=60
        )

        # Should not crash
        self.assertNotIn('Traceback', result.stderr)
        # May have non-zero exit code for missing package


class TestPipProviderIntegration(TestCase):
    """Integration tests for pip provider with real packages."""

    def setUp(self):
        """Set up test environment."""
        self.temp_dir = tempfile.mkdtemp()
        self.output_dir = Path(self.temp_dir) / 'output'
        self.output_dir.mkdir()

    def tearDown(self):
        """Clean up."""
        import shutil
        shutil.rmtree(self.temp_dir, ignore_errors=True)

    def test_hook_finds_pip_installed_binary(self):
        """Hook should find binaries installed via pip."""
        pip_check = subprocess.run(
            [sys.executable, '-m', 'pip', '--version'],
            capture_output=True,
            text=True,
        )
        assert pip_check.returncode == 0, "pip not available"
        env = os.environ.copy()
        env['DATA_DIR'] = self.temp_dir

        # Try to find 'pip' itself which should be available
        result = subprocess.run(
            [
                sys.executable, str(INSTALL_HOOK),
                '--name=pip',
                '--binproviders=pip,env',
                '--binary-id=test-uuid',
                '--machine-id=test-machine',
            ],
            capture_output=True,
            text=True,
            cwd=str(self.output_dir),
            env=env,
            timeout=60
        )

        # Look for success in output
        for line in result.stdout.split('\n'):
            line = line.strip()
            if line.startswith('{'):
                try:
                    record = json.loads(line)
                    if record.get('type') == 'Binary' and 'pip' in record.get('name', ''):
                        # Found pip binary
                        self.assertTrue(record.get('abspath'))
                        return
                except json.JSONDecodeError:
                    continue

        # If we get here without finding pip, that's acceptable
        # as long as the hook didn't crash
        self.assertNotIn('Traceback', result.stderr)


if __name__ == '__main__':
    pytest.main([__file__, '-v'])
