from typing import Union, Iterable, Iterator, TextIO, Any, cast
from pathlib import Path


class LineSource:
    """
    Adapter that accepts a file path, file-like object, or iterable of strings
    and yields lines with consistent newline handling.
    """

    def __init__(self, source: Union[str, Path, TextIO, Iterable[str]]):
        """
        Initialize the LineSource adapter.

        Args:
            source: The input source. Can be:
                - A String or Path object (file path)
                - A file-like object (with a .read() method)
                - An iterable of strings (e.g. list, generator)
        """
        self.source = source

    def __iter__(self) -> Iterator[str]:
        """
        Iterate over the source and yield lines with normalized newlines
        Yields:
            str: The next line from the source, with trailing newline characters removed.
        """
        if self._is_file_path(self.source):
            yield from self._iter_from_path(cast(Union[str, Path], self.source))
        elif self._is_file_like(self.source):
            yield from self._iter_from_file_like(cast(TextIO, self.source))
        elif isinstance(self.source, Iterable) and not isinstance(
            self.source, (str, bytes)
        ):
            yield from self._iter_from_iterable(cast(Iterable[str], self.source))
        else:
            raise ValueError(f"Unsupported source type: {type(self.source)}")

    def _is_file_path(self, source: Any) -> bool:
        return isinstance(source, (str, Path)) and not self._is_file_like(source)

    def _is_file_like(self, source: Any) -> bool:
        return hasattr(source, "read") and hasattr(source, "__iter__")

    def _iter_from_path(self, path: Union[str, Path]) -> Iterator[str]:
        with open(path, "r", encoding="utf-8", newline=None) as file:
            yield from self._iter_from_file_like(file)

    def _iter_from_file_like(self, file: TextIO) -> Iterator[str]:
        for line in file:
            yield self._normalize_line(line)

    def _iter_from_iterable(self, iterable: Iterable[str]) -> Iterator[str]:
        for line in iterable:
            yield self._normalize_line(line)

    def _normalize_line(self, line: str) -> str:
        return line.rstrip("\r\n")
