import datetime
from collections.abc import Iterator

from personal_data.activity import HIDDEN_LABEL_CATEGORY, Label, RealizedActivitySample

ZERO_DURATION = datetime.timedelta(seconds=0)
HOUR = datetime.timedelta(hours=1)
MINUTE = datetime.timedelta(minutes=1)


def fmt_year_ranges_internal(years: list[int]) -> Iterator[str]:
    years = sorted(years)
    for idx, year in enumerate(years):
        at_end = idx == len(years) - 1
        range_before = idx > 0 and years[idx - 1] == year - 1
        range_after = not at_end and years[idx + 1] == year + 1

        if not range_before or not range_after:
            yield str(year)

        if not at_end:
            if not range_before and range_after:
                yield '-'
            elif not range_after:
                yield ','


def fmt_year_ranges(years: list[int]) -> str:
    return ''.join(list(fmt_year_ranges_internal(years)))


def fmt_line(label: Label, total_time: datetime.timedelta) -> str:
    hours = int(total_time / HOUR)
    minutes = int((total_time - hours * HOUR) / MINUTE)
    label_str = str(label.label)
    return f'  {label.category:20} {label_str:50}  {hours:-4d}h {minutes:-2d}m'


LINE_LENGTH = len(fmt_line(Label('', ''), datetime.timedelta()))


def generate_report(
    samples: list[RealizedActivitySample],
) -> Iterator[str]:
    # Time spent per label
    time_per_label: dict[Label, datetime.timedelta] = {}
    years_per_label: dict[Label, set[int]] = {}
    for sample in samples:
        duration = sample.end_at - sample.start_at

        for label in sample.labels:
            time_per_label.setdefault(label, ZERO_DURATION)
            time_per_label[label] += duration
            years_per_label.setdefault(label, set()).add(sample.end_at.year)

        del sample, duration

    time_and_label = [(duration, label) for label, duration in time_per_label.items()]
    time_and_label.sort(reverse=True)

    #
    yield '-' * LINE_LENGTH
    yield '\n'
    for total_time, label in time_and_label:
        if label.category == HIDDEN_LABEL_CATEGORY:
            continue

        yield fmt_line(label, total_time)
        yield '  ('
        yield fmt_year_ranges(years_per_label.get(label, []))
        yield ')'
        yield '\n'
        del label, total_time

    yield '-' * LINE_LENGTH
    yield '\n'

    label_total = Label(HIDDEN_LABEL_CATEGORY, 'total')
    yield fmt_line(label_total, time_per_label.get(label_total, ZERO_DURATION))
    yield '\n'