"""Integration tests for the aap package."""

import warnings
from typing import List, Optional

import pytest

from aap import autoarg


def test_complete_workflow_single_function():
    """Test a complete workflow with a single function."""
    def deploy(
        environment: str,
        version: str,
        dry_run: bool = False,
        replicas: int = 3
    ):
        """Deploy an application to an environment.
        
        Args:
            environment: Target environment (dev, staging, prod)
            version: Version to deploy
            dry_run: Perform a dry run without actual deployment
            replicas: Number of replicas to deploy
        """
        action = "Would deploy" if dry_run else "Deploying"
        return f"{action} version {version} to {environment} with {replicas} replicas"
    
    cli = autoarg(deploy)
    
    # Test normal deployment
    result1 = cli(['--environment', 'prod', '--version', '1.2.3'])
    assert result1 == "Deploying version 1.2.3 to prod with 3 replicas"
    
    # Test dry run
    result2 = cli(['--environment', 'staging', '--version', '1.2.4', '--dry-run'])
    assert result2 == "Would deploy version 1.2.4 to staging with 3 replicas"
    
    # Test custom replicas
    result3 = cli(['--environment', 'dev', '--version', '1.3.0', '--replicas', '5'])
    assert result3 == "Deploying version 1.3.0 to dev with 5 replicas"


def test_complete_workflow_multi_command():
    """Test a complete workflow with multiple commands."""
    def create(name: str, size: int = 10):
        """Create a new resource.
        
        Args:
            name: Resource name
            size: Resource size in GB
        """
        return f"Created {name} with {size}GB"
    
    def delete(name: str, force: bool = False):
        """Delete a resource.
        
        Args:
            name: Resource name
            force: Force deletion without confirmation
        """
        mode = "forcefully" if force else "safely"
        return f"Deleted {name} {mode}"
    
    def list_resources(filter_name: Optional[str] = None):
        """List all resources.
        
        Args:
            filter_name: Optional filter by name
        """
        if filter_name:
            return f"Listing resources matching {filter_name}"
        return "Listing all resources"
    
    # Use set for commands
    cli = autoarg({create, delete, list_resources})
    
    # Test create
    result1 = cli(['create', '--name', 'myresource'])
    assert result1 == "Created myresource with 10GB"
    
    result2 = cli(['create', '--name', 'bigresource', '--size', '100'])
    assert result2 == "Created bigresource with 100GB"
    
    # Test delete
    result3 = cli(['delete', '--name', 'oldresource'])
    assert result3 == "Deleted oldresource safely"
    
    result4 = cli(['delete', '--name', 'badresource', '--force'])
    assert result4 == "Deleted badresource forcefully"
    
    # Test list
    result5 = cli(['list_resources'])
    assert result5 == "Listing all resources"
    
    result6 = cli(['list_resources', '--filter-name', 'test'])
    assert result6 == "Listing resources matching test"


def test_list_type_integration():
    """Test integration with List type parameters."""
    def process_files(files: List[str], output: str):
        """Process multiple files.
        
        Args:
            files: List of file paths to process
            output: Output file path
        """
        return f"Processing {len(files)} files to {output}: {', '.join(files)}"
    
    cli = autoarg(process_files)
    result = cli(['--files', 'a.txt', 'b.txt', 'c.txt', '--output', 'result.txt'])
    
    assert "Processing 3 files" in result
    assert "a.txt" in result
    assert "b.txt" in result
    assert "c.txt" in result


def test_optional_type_integration():
    """Test integration with Optional type parameters."""
    def send_email(
        to: str,
        subject: str,
        body: str,
        cc: Optional[str] = None,
        bcc: Optional[str] = None
    ):
        """Send an email.
        
        Args:
            to: Recipient email address
            subject: Email subject
            body: Email body
            cc: CC email address
            bcc: BCC email address
        """
        parts = [f"To: {to}", f"Subject: {subject}", f"Body: {body}"]
        if cc:
            parts.append(f"CC: {cc}")
        if bcc:
            parts.append(f"BCC: {bcc}")
        return " | ".join(parts)
    
    cli = autoarg(send_email)
    
    # Test without optional params
    result1 = cli([
        '--to', 'user@example.com',
        '--subject', 'Hello',
        '--body', 'Test message'
    ])
    assert "To: user@example.com" in result1
    assert "CC:" not in result1
    
    # Test with optional params
    result2 = cli([
        '--to', 'user@example.com',
        '--subject', 'Hello',
        '--body', 'Test message',
        '--cc', 'manager@example.com',
        '--bcc', 'archive@example.com'
    ])
    assert "CC: manager@example.com" in result2
    assert "BCC: archive@example.com" in result2


def test_warning_for_untyped_parameters():
    """Test that warnings are issued for untyped parameters."""
    def untyped_function(name, age: int):
        """A function with mixed typing.
        
        Args:
            name: Person's name
            age: Person's age
        """
        return f"{name} is {age}"
    
    with warnings.catch_warnings(record=True) as w:
        warnings.simplefilter("always")
        cli = autoarg(untyped_function)
        
        # Should have warning for 'name' parameter
        assert len(w) == 1
        assert "name" in str(w[0].message)
        assert "no type annotation" in str(w[0].message).lower()
    
    # Function should still work with default str type
    result = cli(['--name', 'Alice', '--age', '25'])
    assert result == "Alice is 25"


def test_complex_real_world_example():
    """Test a complex real-world-like example."""
    def backup_database(
        host: str,
        database: str,
        output_file: str,
        port: int = 5432,
        username: Optional[str] = None,
        password: Optional[str] = None,
        compress: bool = True,
        tables: Optional[List[str]] = None
    ):
        """Backup a PostgreSQL database.
        
        Args:
            host: Database host
            database: Database name
            output_file: Output backup file path
            port: Database port
            username: Database username
            password: Database password
            compress: Compress the backup file
            tables: Specific tables to backup (all if not specified)
        """
        parts = [f"Backing up {database} from {host}:{port}"]
        
        if username:
            parts.append(f"as user {username}")
        
        parts.append(f"to {output_file}")
        
        if compress:
            parts.append("(compressed)")
        
        if tables:
            parts.append(f"tables: {', '.join(tables)}")
        else:
            parts.append("(all tables)")
        
        return " ".join(parts)
    
    cli = autoarg(backup_database)
    
    # Simple backup
    result1 = cli([
        '--host', 'localhost',
        '--database', 'mydb',
        '--output-file', 'backup.sql'
    ])
    assert "Backing up mydb from localhost:5432" in result1
    assert "(compressed)" in result1
    assert "(all tables)" in result1
    
    # Complex backup with all options
    result2 = cli([
        '--host', 'db.example.com',
        '--database', 'production',
        '--output-file', '/backups/prod.sql',
        '--port', '3306',
        '--username', 'admin',
        '--password', 'secret',
        '--no-compress',
        '--tables', 'users', 'orders', 'products'
    ])
    assert "db.example.com:3306" in result2
    assert "as user admin" in result2
    assert "(compressed)" not in result2
    assert "tables: users, orders, products" in result2


def test_edge_case_empty_string_default():
    """Test edge case with empty string as default."""
    def func(name: str, prefix: str = ""):
        """Function with empty string default.
        
        Args:
            name: The name
            prefix: Optional prefix
        """
        return f"{prefix}{name}" if prefix else name
    
    cli = autoarg(func)
    
    result1 = cli(['--name', 'test'])
    assert result1 == "test"
    
    result2 = cli(['--name', 'test', '--prefix', 'Mr. '])
    assert result2 == "Mr. test"


def test_edge_case_zero_default():
    """Test edge case with zero as default."""
    def func(value: int, offset: int = 0):
        """Function with zero default.
        
        Args:
            value: The value
            offset: Offset to add
        """
        return value + offset
    
    cli = autoarg(func)
    
    result1 = cli(['--value', '10'])
    assert result1 == 10
    
    result2 = cli(['--value', '10', '--offset', '5'])
    assert result2 == 15
