"""Tests for the parser module."""

import warnings
from typing import List, Optional

import pytest

from aap.parser import (
    create_argument_name,
    extract_parameter_info,
    get_argparse_type,
    parse_docstring,
)


def test_extract_parameter_info_with_types():
    """Test extracting parameter info from a typed function."""
    def sample_func(name: str, age: int, score: float = 3.5):
        pass
    
    info = extract_parameter_info(sample_func)
    
    assert 'name' in info
    assert info['name']['type'] == str
    assert info['name']['required'] is True
    assert info['name']['has_default'] is False
    
    assert 'age' in info
    assert info['age']['type'] == int
    assert info['age']['required'] is True
    
    assert 'score' in info
    assert info['score']['type'] == float
    assert info['score']['default'] == 3.5
    assert info['score']['has_default'] is True
    assert info['score']['required'] is False


def test_extract_parameter_info_without_types():
    """Test that missing type annotations trigger warnings."""
    def untyped_func(name, age):
        pass
    
    with warnings.catch_warnings(record=True) as w:
        warnings.simplefilter("always")
        info = extract_parameter_info(untyped_func)
        
        # Should have 2 warnings (one for each parameter)
        assert len(w) == 2
        assert "no type annotation" in str(w[0].message).lower()
        
        # Should default to str
        assert info['name']['type'] == str
        assert info['age']['type'] == str


def test_extract_parameter_info_skips_self_and_cls():
    """Test that self and cls parameters are skipped."""
    class SampleClass:
        def instance_method(self, value: int):
            pass
        
        @classmethod
        def class_method(cls, value: int):
            pass
    
    instance_info = extract_parameter_info(SampleClass.instance_method)
    assert 'self' not in instance_info
    assert 'value' in instance_info
    
    class_info = extract_parameter_info(SampleClass.class_method)
    assert 'cls' not in class_info
    assert 'value' in class_info


def test_parse_docstring_google_style():
    """Test parsing Google-style docstrings."""
    def google_func(name: str, age: int):
        """
        A sample function with Google-style docstring.
        
        This function does something useful.
        
        Args:
            name: The person's name
            age (int): The person's age in years
        
        Returns:
            Nothing important
        """
        pass
    
    description, param_help = parse_docstring(google_func)
    
    assert "sample function" in description.lower()
    assert "useful" in description.lower()
    assert param_help['name'] == "The person's name"
    assert param_help['age'] == "The person's age in years"


def test_parse_docstring_numpy_style():
    """Test parsing NumPy-style docstrings."""
    def numpy_func(name: str, age: int):
        """
        A sample function with NumPy-style docstring.
        
        Parameters
        ----------
        name : str
            The person's name
        age : int
            The person's age
        """
        pass
    
    description, param_help = parse_docstring(numpy_func)
    
    assert "sample function" in description.lower()
    assert "name" in param_help
    assert "person's name" in param_help['name']


def test_parse_docstring_rst_style():
    """Test parsing reStructuredText-style docstrings."""
    def rst_func(name: str, age: int):
        """
        A sample function with RST-style docstring.
        
        :param name: The person's name
        :param age: The person's age
        """
        pass
    
    description, param_help = parse_docstring(rst_func)
    
    assert "sample function" in description.lower()
    assert param_help['name'] == "The person's name"
    assert param_help['age'] == "The person's age"


def test_parse_docstring_no_docstring():
    """Test parsing when there's no docstring."""
    def no_doc_func(name: str):
        pass
    
    description, param_help = parse_docstring(no_doc_func)
    
    assert description is None
    assert param_help == {}


def test_get_argparse_type_basic_types():
    """Test conversion of basic Python types to argparse types."""
    str_type, str_kwargs = get_argparse_type(str)
    assert str_type == str
    assert str_kwargs == {}
    
    int_type, int_kwargs = get_argparse_type(int)
    assert int_type == int
    assert int_kwargs == {}
    
    float_type, float_kwargs = get_argparse_type(float)
    assert float_type == float
    assert float_kwargs == {}
    
    bool_type, bool_kwargs = get_argparse_type(bool)
    assert bool_type == bool
    assert bool_kwargs == {}


def test_get_argparse_type_optional():
    """Test conversion of Optional types."""
    opt_type, opt_kwargs = get_argparse_type(Optional[int])
    assert opt_type == int
    assert opt_kwargs == {}
    
    opt_str_type, opt_str_kwargs = get_argparse_type(Optional[str])
    assert opt_str_type == str


def test_get_argparse_type_list():
    """Test conversion of List types."""
    list_type, list_kwargs = get_argparse_type(List[int])
    assert list_type == int
    assert list_kwargs == {'nargs': '+'}
    
    list_str_type, list_str_kwargs = get_argparse_type(List[str])
    assert list_str_type == str
    assert list_str_kwargs == {'nargs': '+'}


def test_get_argparse_type_none():
    """Test handling of None type."""
    none_type, none_kwargs = get_argparse_type(None)
    assert none_type == str
    assert none_kwargs == {}


def test_create_argument_name():
    """Test conversion of parameter names to CLI argument names."""
    assert create_argument_name('name') == '--name'
    assert create_argument_name('first_name') == '--first-name'
    assert create_argument_name('my_long_param_name') == '--my-long-param-name'
