from typing import overload, SupportsIndex

from typediter.classes import Item_T
from typediter.classes.light import TypedList_lt
from typediter._helpers import check_object_compatibility

class TypedList( TypedList_lt[Item_T] ):
    """ Class based on list, insuring the type safety of its items, and the type safety on operations returning a new list

    It can be used exactly like the built-in list but its items, and some operations, are type-restricted,
    if the type restriction is broken, it fails with a TypeRestrictionError

    Inherits from list, but overriding methods:
        - That could insert an incompatible item (overridden in parent 'light' version class)
        - That used to return a list, to make it return a TypedList
        (for example, list addition returns a new list instance, so TypedList addition
        returns a new TypedList instance)

    Attributes
    ----------
    - **i_type**
        The type restriction for the items

    Raises
    ------
    - **typediter.exceptions.TypeRestrictionError**
        Raised by a typed-checked operation to prevent
        it from inserting incompatible items in a type-safe iterable.
        (Can be raised at instance creation
        or by every operation performing type checks)
    
    - **typediter.exceptions.InvalidTypeRestrictionError**
        Raised at instance creation if the provided 'i_type':
            - is not a type
    
    - **typediter.exceptions.IterableExpectedError**
        Raised by a type-checked operation if it expected an iterable
        but received something else
        (Can be raised at instance creation
        or by every operation performing type checks)
    
    - **other exceptions**
        Any exception that can be raised by a built-in 'list'
        for a given operation

    Usage Example
    -------------
    >>> # importing the class
    >>> from typediter import TypedList
    >>>
    >>> # creating an instance
    >>> string_list = TypedList( ('A', 'B', 'C'), i_type=str )
    >>>
    >>> # supports all list operations
    >>> string_list.append( 'D' )
    >>> new_list = string_list + ['E', 'F']
    >>>
    >>> # new instances generated by non-mutating
    >>> # operations are also type-safe
    >>> type( new_list ) # -> TypedList
    >>>
    >>> # Remark: 
    >>> # if the non-mutating operation is handled by the built-in, a built-in is returned
    >>> builtin_addition_result = [ 'E', 'F' ] + string_list
    >>> type( builtin_addition_result ) # -> list
    >>>
    >>> # trying to insert an incompatible item in the instance,
    >>> # or in a typed iterable instance generated by an operation,
    >>> # will raise a TypeRestrictionError
    >>> invalid_instance = TypedList( ( 1, 2, 3 ), i_type=str ) # will raise TypeRestrictionError
    >>> string_list.append( 12 ) # will raise TypeRestrictionError
    >>> invalid_operation_result = string_list + [ 1, 2, 3 ] # will raise TypeRestrictionError
    """
    __module__ = 'typediter.complete' # shorten class 'print' name

    # -------------------- Representation --------------------
    
    def __repr__( self ) -> str:
        typename = self.i_type.__name__
        default_representation = list(self)
        return f"List[{typename}]:{default_representation}"

    # -------------------- Overriding --------------------
    # (mutating methods overridden in parent 'light' verion class) 
    # -> Operations handled by current class should return current class instance

    @overload
    def __getitem__( self, key: SupportsIndex, / ) -> Item_T: ...
    @overload
    def __getitem__( self, key: slice, / ) -> 'TypedList[Item_T]': ...
    def __getitem__( self, key: SupportsIndex | slice, / ) ->  Item_T | 'TypedList[Item_T]':
        """ (NO TYPE CHECKS NEEDED) Returns value for self[key]
        
        Returns:
        - for an index key: an item
        - for a slice key: a new typed list instance
        """
        if isinstance( key, slice):
            new_list = super().__getitem__( key )
            return TypedList( new_list, i_type=self.i_type, _skip_type_check=True )
        return super().__getitem__( key )

    def __add__( self, other: list[Item_T], / ) -> 'TypedList[Item_T]': # type: ignore[override]
        """ (TYPE-CHECKED) Return self+value as a new typed list """
        check_object_compatibility( other, i_type=self.i_type )
        new_list = super().__add__( other )
        return TypedList( new_list, i_type=self.i_type, _skip_type_check=True )

    def __mul__( self, value: SupportsIndex, / ) -> 'TypedList[Item_T]':
        """ (NO TYPE CHECKS NEEDED) Return self*value as a new typed list """
        new_list = super().__mul__( value )
        return TypedList( new_list, i_type=self.i_type, _skip_type_check=True )

    def __rmul__( self, value: SupportsIndex, / ) -> 'TypedList[Item_T]':
        """ (NO TYPE CHECKS NEEDED) Return value*self as a new typed list """
        new_list = super().__rmul__( value )
        return TypedList( new_list, i_type=self.i_type, _skip_type_check=True )
