"""BuildMonthlyReport use case."""
from __future__ import annotations

import datetime

from domain.entities import MonthlyReport, MonthlyReportRow
from domain.repositories import (
    AttendanceRepository,
    HolidayRepository,
    LeaveRequestRepository,
    WorkScheduleRepository,
)
from domain.value_objects import (
    format_minutes_as_hm,
    gregorian_to_jalali_string,
    jalali_month_to_gregorian_range,
)


def _minutes_between(earlier: datetime.time, later: datetime.time) -> int:
    today = datetime.date.today()
    dt_earlier = datetime.datetime.combine(today, earlier)
    dt_later = datetime.datetime.combine(today, later)
    return int((dt_later - dt_earlier).total_seconds() // 60)


class BuildMonthlyReport:
    def __init__(
        self,
        attendance_repo: AttendanceRepository,
        holiday_repo: HolidayRepository,
        leave_repo: LeaveRequestRepository,
        work_schedule_repo: WorkScheduleRepository | None = None,
    ):
        self.attendance_repo = attendance_repo
        self.holiday_repo = holiday_repo
        self.leave_repo = leave_repo
        self.work_schedule_repo = work_schedule_repo

    def execute(self, user_id: int, jalali_year: int, jalali_month: int) -> MonthlyReport:
        start, end = jalali_month_to_gregorian_range(jalali_year, jalali_month)
        today = datetime.date.today()
        if end > today:
            end = today
        if start > end:
            # Whole month is in the future: no rows.
            return MonthlyReport(rows=[], monthly_total=format_minutes_as_hm(0))

        attendances = {a.date: a for a in self.attendance_repo.list_for_range(user_id, start, end)}
        holidays = {h.date: h for h in self.holiday_repo.list_for_range(start, end)}
        approved_leaves = self.leave_repo.list_approved_for_user_range(user_id, start, end)
        leaves_by_date: dict[datetime.date, list] = {}
        for lv in approved_leaves:
            leaves_by_date.setdefault(lv.date, []).append(lv)

        rows = []
        total_minutes = 0
        current = start
        while current <= end:
            attendance = attendances.get(current)
            holiday = holidays.get(current)
            day_leaves = leaves_by_date.get(current, [])

            worked = None
            if attendance is not None:
                worked = attendance.worked_hours_formatted
                minutes = attendance.worked_minutes
                if minutes:
                    total_minutes += minutes

            has_daily_leave = any(lv.type == "daily" for lv in day_leaves)
            is_holiday = holiday is not None
            missing = attendance is None and not is_holiday and not has_daily_leave

            schedule = self.work_schedule_repo.get_effective_for_date(current) if self.work_schedule_repo else None
            late = None
            overtime = None
            if schedule is not None and attendance is not None:
                if attendance.check_in and attendance.check_in > schedule.standard_check_in:
                    late = format_minutes_as_hm(
                        _minutes_between(schedule.standard_check_in, attendance.check_in)
                    )
                if (
                    attendance.check_out
                    and not attendance.is_default_checkout
                    and attendance.check_out > schedule.standard_check_out
                ):
                    overtime = format_minutes_as_hm(
                        _minutes_between(schedule.standard_check_out, attendance.check_out)
                    )

            rows.append(
                MonthlyReportRow(
                    date=current,
                    jalali_date=gregorian_to_jalali_string(current),
                    check_in=attendance.check_in if attendance else None,
                    check_out=attendance.check_out if attendance else None,
                    is_default_checkout=attendance.is_default_checkout if attendance else False,
                    worked=worked,
                    tasks_note=attendance.tasks_note if attendance else None,
                    is_holiday=is_holiday,
                    holiday_title=holiday.title if holiday else None,
                    leaves=day_leaves,
                    missing=missing,
                    standard_check_in=schedule.standard_check_in if schedule else None,
                    standard_check_out=schedule.standard_check_out if schedule else None,
                    late=late,
                    overtime=overtime,
                )
            )
            current += datetime.timedelta(days=1)

        return MonthlyReport(rows=rows, monthly_total=format_minutes_as_hm(total_minutes))
