import csv
from typing import Any, Callable, Iterable, Mapping, Optional, Sequence


def convert_csv(
    lines: Iterable[str],
    conv: Callable[[Sequence[Any], Sequence[str]], Any],
    headers: Optional[Sequence[str]] = None,
) -> Sequence[Any]:
    rows = csv.reader(lines)
    if headers is None:
        headers = next(rows)
    records = list(map(lambda row: conv(row, headers), rows))
    return records


def csv_as_dicts(
    lines: Iterable[str], types: Sequence[type], headers: Optional[Sequence[str]] = None
) -> Sequence[Mapping[str, Any]]:
    """Parse CSV lines into a list of dictionaries."""

    def _conv(row, hdrs):
        return {name: func(val) for name, func, val in zip(hdrs, types, row)}

    return convert_csv(lines, _conv, headers)


def read_csv_as_dicts(
    filename: str, types: Sequence[type], headers: Optional[Sequence[str]] = None
) -> Sequence[Mapping[str, Any]]:
    """
    Read CSV data into list of dictionaries with optional type conversion.
    """
    with open(filename) as file:
        return csv_as_dicts(file, types, headers)


def csv_as_instances(
    lines: Iterable[str], cls: type, headers: Optional[Sequence[str]] = None
) -> Sequence[Any]:
    """Parse CSV lines into a list of of class instances."""
    return convert_csv(lines, lambda row, _: cls.from_row(row), headers)


def read_csv_as_instances(
    filename: str, cls: type, has_headers: bool = True
) -> Sequence[Any]:
    """
    Read CSV data into list of instances.
    """
    with open(filename) as file:
        return csv_as_instances(file, cls, has_headers)
