from datetime import datetime
from typing import Any

from ._parser import (
    CalendarBound,
    DeltaBound,
    IndexPosition,
    IntBound,
    SlicePosition,
    parse_position,
)
from ._resolver import resolve_to_datetime


def _is_temporal(bound) -> bool:
    return isinstance(bound, (DeltaBound, CalendarBound))


def _validate_tz(objects: list, reference_dt: datetime, timestamp_key: str) -> None:
    ref_aware = reference_dt.tzinfo is not None
    for i, obj in enumerate(objects):
        ts = getattr(obj, timestamp_key)  # AttributeError propagates naturally
        obj_aware = ts.tzinfo is not None
        if obj_aware != ref_aware:
            ref_state = "aware" if ref_aware else "naive"
            obj_state = "aware" if obj_aware else "naive"
            raise TypeError(
                f"Timezone awareness mismatch: reference_dt is {ref_state} but "
                f"object at index {i} has a {obj_state} timestamp."
            )


def slice_objects(
    objects: list[Any],
    position: str,
    timestamp_key: str,
    reference_dt: datetime,
    week_start: int = 0,
) -> "Any | list[Any]":
    _validate_tz(objects, reference_dt, timestamp_key)
    sorted_objs = sorted(objects, key=lambda o: getattr(o, timestamp_key))

    parsed = parse_position(position)

    if isinstance(parsed, IndexPosition):
        try:
            return sorted_objs[parsed.bound.value]
        except IndexError:
            return None

    assert isinstance(parsed, SlicePosition)
    start, end = parsed.start, parsed.end

    if not (_is_temporal(start) or _is_temporal(end)):
        s = start.value if isinstance(start, IntBound) else None
        e = end.value if isinstance(end, IntBound) else None
        return sorted_objs[s:e]

    # Temporal path: resolve datetime thresholds, filter, then apply integer bounds.
    start_dt = (
        resolve_to_datetime(start, reference_dt, week_start)
        if _is_temporal(start)
        else None
    )
    end_dt = (
        resolve_to_datetime(end, reference_dt, week_start)
        if _is_temporal(end)
        else None
    )

    # Inclusive start, exclusive end — consistent with Python slice semantics.
    filtered = [
        obj
        for obj in sorted_objs
        if (start_dt is None or getattr(obj, timestamp_key) >= start_dt)
        and (end_dt is None or getattr(obj, timestamp_key) < end_dt)
    ]

    s = start.value if isinstance(start, IntBound) else None
    e = end.value if isinstance(end, IntBound) else None
    return filtered[s:e]
