"""Monthly Google Sheets tab generation and HACCP value filling rules."""

from __future__ import annotations

import calendar
import hashlib
import logging
import random
import re
from dataclasses import dataclass
from datetime import date, datetime, timedelta
from typing import Any, Iterable, Literal
from zoneinfo import ZoneInfo

from .config import AppConfig
from .google_client import GoogleWorkspaceClient
from .workdays import HungaryWorkdayCalendar

LOGGER = logging.getLogger(__name__)

FillMode = Literal["morning", "afternoon", "all"]
DocumentKey = Literal["takaritasi", "meleg", "huto"]

PERSON_NAME = "Szokodi Noémi"
BUSINESS_ADDRESS = "Nomad Bistro, 2337 Délegyháza szarvas utca 4/b"
ROOM_NAME = "Mozgóbolt"

MONTH_TAB_NAMES = {
    1: "01-jan",
    2: "02-febr",
    3: "03-marc",
    4: "04-apr",
    5: "05-maj",
    6: "06-jun",
    7: "07-jul",
    8: "08-aug",
    9: "09-szept",
    10: "10-okt",
    11: "11-nov",
    12: "12-dec",
}

MONTH_HEADER_NAMES = {
    1: "január",
    2: "február",
    3: "március",
    4: "április",
    5: "május",
    6: "június",
    7: "július",
    8: "augusztus",
    9: "szeptember",
    10: "október",
    11: "november",
    12: "december",
}

DEFAULT_TAB_NAMES = {"Sheet1", "Sheet 1"}
MONTH_TAB_PATTERN = re.compile(r"^(\d{4})-(\d{2})-")


@dataclass(frozen=True)
class DocumentSpec:
    """Static metadata for one precreated HACCP spreadsheet."""

    key: DocumentKey
    spreadsheet_name: str


@dataclass(frozen=True)
class SheetPayload:
    """Values and formatting needed to render one month tab."""

    values: list[list[object]]
    column_count: int
    layout_requests: list[dict[str, Any]]


DOCUMENTS: dict[DocumentKey, DocumentSpec] = {
    "takaritasi": DocumentSpec(
        key="takaritasi",
        spreadsheet_name="takaritasi_naplo",
    ),
    "meleg": DocumentSpec(
        key="meleg",
        spreadsheet_name="haccp_meleg_vizes_melegentarto_homerseklet_ellenorzo",
    ),
    "huto": DocumentSpec(
        key="huto",
        spreadsheet_name="haccp_huto_es_melyhuto_homerseklet_ellenorzo",
    ),
}

MELEG_DEVICE = "Meleg vizes melegentartó"
MELEG_MIN_TEMP = "min. +63 °C"
MELEG_ROWS = (
    ("Tálalás kezdete", "Virsli - külön GN edény", "morning"),
    ("Tálalás kezdete", "Lesütött gyros hús - külön GN edény", "morning"),
    ("Tálalás közben / vége", "Virsli - külön GN edény", "afternoon"),
    ("Tálalás közben / vége", "Lesütött gyros hús - külön GN edény", "afternoon"),
)

HUTO_ROWS = (("Munkakezdés", "morning"), ("Munka vége", "afternoon"))


class HaccpGenerator:
    """Coordinates fixed spreadsheet lookup and monthly tab updates."""

    def __init__(
        self,
        config: AppConfig,
        google: GoogleWorkspaceClient,
        workdays: HungaryWorkdayCalendar,
    ) -> None:
        """Store collaborators used by all generation commands."""

        self.config = config
        self.google = google
        self.workdays = workdays
        self.timezone = ZoneInfo(config.timezone)
        self._spreadsheet_ids: dict[DocumentKey, str] = {}

    def run_daily(self, fill_mode: FillMode) -> None:
        """Generate or update the current month tab for a scheduled run."""

        today = datetime.now(self.timezone).date()
        first_day = today.replace(day=1)
        documents: tuple[DocumentKey, ...]
        if fill_mode == "morning":
            documents = ("meleg", "huto")
        else:
            documents = ("takaritasi", "meleg", "huto")

        self.generate_month(
            year=today.year,
            month=today.month,
            fill_start=first_day,
            fill_through=today,
            documents=documents,
            fill_mode=fill_mode,
        )

    def backfill(self, start: date, end: date) -> None:
        """Generate all monthly tabs and values between two inclusive dates."""

        if end < start:
            raise ValueError("Backfill end date must not be before start date")

        for year, month in iter_months(start, end):
            month_first = date(year, month, 1)
            month_last = date(year, month, days_in_month(year, month))
            self.generate_month(
                year=year,
                month=month,
                fill_start=max(start, month_first),
                fill_through=min(end, month_last),
                documents=("takaritasi", "meleg", "huto"),
                fill_mode="all",
            )

    def fix_tabs(self) -> None:
        """Delete default blank tabs and sort month tabs newest-first."""

        for key, spec in DOCUMENTS.items():
            spreadsheet_id = self._spreadsheet_id(key)
            sheets = self.google.list_sheets(spreadsheet_id)
            for sheet in list(sheets):
                title = sheet.get("title")
                if title not in DEFAULT_TAB_NAMES:
                    continue
                if len(sheets) <= 1:
                    LOGGER.warning("Skipping deletion of only tab in %s", spec.spreadsheet_name)
                    continue
                self.google.delete_sheet(spreadsheet_id, sheet["sheetId"])
                sheets = [item for item in sheets if item["sheetId"] != sheet["sheetId"]]
                LOGGER.info("Deleted default tab %s from %s", title, spec.spreadsheet_name)

            ordered = sorted(
                sheets,
                key=lambda sheet: sheet_order_key(str(sheet.get("title", ""))),
            )
            ordered_ids = [sheet["sheetId"] for sheet in ordered]
            current_ids = [sheet["sheetId"] for sheet in sheets]
            if ordered_ids != current_ids:
                self.google.set_sheet_order(spreadsheet_id, ordered_ids)
                LOGGER.info("Sorted tabs newest-first in %s", spec.spreadsheet_name)

    def generate_month(
        self,
        year: int,
        month: int,
        fill_start: date,
        fill_through: date,
        documents: tuple[DocumentKey, ...],
        fill_mode: FillMode,
    ) -> None:
        """Update each requested precreated spreadsheet with one monthly tab."""

        tab_name = month_tab_name(year, month)
        LOGGER.info(
            "Generating tab %s for %s through %s (%s)",
            tab_name,
            fill_start,
            fill_through,
            fill_mode,
        )
        for key in documents:
            spreadsheet_id = self._spreadsheet_id(key)
            self.update_document(
                key=key,
                spreadsheet_id=spreadsheet_id,
                tab_name=tab_name,
                year=year,
                month=month,
                fill_start=fill_start,
                fill_through=fill_through,
                fill_mode=fill_mode,
            )

    def update_document(
        self,
        key: DocumentKey,
        spreadsheet_id: str,
        tab_name: str,
        year: int,
        month: int,
        fill_start: date,
        fill_through: date,
        fill_mode: FillMode,
    ) -> None:
        """Render a document-specific payload into its month tab."""

        payload = self._payload(
            key=key,
            sheet_id=0,
            year=year,
            month=month,
            fill_start=fill_start,
            fill_through=fill_through,
            fill_mode=fill_mode,
        )
        sheet_id = self.google.ensure_sheet(
            spreadsheet_id, tab_name, len(payload.values), payload.column_count, index=0
        )
        self.google.move_sheet(spreadsheet_id, sheet_id, 0)
        payload = self._payload(
            key=key,
            sheet_id=sheet_id,
            year=year,
            month=month,
            fill_start=fill_start,
            fill_through=fill_through,
            fill_mode=fill_mode,
        )
        self.google.reset_sheet(
            spreadsheet_id, sheet_id, len(payload.values), payload.column_count
        )
        self.google.update_values(
            spreadsheet_id,
            [
                {
                    "range": a1(
                        tab_name,
                        f"A1:{column_name(payload.column_count)}{len(payload.values)}",
                    ),
                    "values": payload.values,
                }
            ],
        )
        self.google.batch_update(
            spreadsheet_id,
            payload.layout_requests,
            f"format tab {tab_name}",
        )

    def _spreadsheet_id(self, key: DocumentKey) -> str:
        """Resolve and cache the ID of one precreated spreadsheet."""

        if key not in self._spreadsheet_ids:
            spec = DOCUMENTS[key]
            self._spreadsheet_ids[key] = self.google.find_spreadsheet_recursive(
                self.config.drive_root_folder_id, spec.spreadsheet_name
            )
        return self._spreadsheet_ids[key]

    def _payload(
        self,
        key: DocumentKey,
        sheet_id: int,
        year: int,
        month: int,
        fill_start: date,
        fill_through: date,
        fill_mode: FillMode,
    ) -> SheetPayload:
        """Build values and formatting for one document type."""

        if key == "takaritasi":
            return self._takaritasi_payload(sheet_id, year, month, fill_start, fill_through)
        if key == "meleg":
            return self._meleg_payload(sheet_id, year, month, fill_start, fill_through, fill_mode)
        if key == "huto":
            return self._huto_payload(sheet_id, year, month, fill_start, fill_through, fill_mode)
        raise ValueError(f"Unknown document key: {key}")

    def _takaritasi_payload(
        self,
        sheet_id: int,
        year: int,
        month: int,
        fill_start: date,
        fill_through: date,
    ) -> SheetPayload:
        """Build the cleaning log month tab from the Excel-style template."""

        rows = [[""] * 5 for _ in range(43)]
        rows[0][0] = "TAKARÍTÁSI NAPLÓ"
        rows[2][0] = "Üzlet (neve, címe):"
        rows[2][1] = BUSINESS_ADDRESS
        rows[2][3] = "Év, hónap:"
        rows[2][4] = month_header(year, month)
        rows[3][0] = "Helyiség neve:"
        rows[3][1] = ROOM_NAME
        rows[4][0] = "Dátum"
        rows[4][1] = "Takarítást végezte"
        rows[5][1] = "Név"
        rows[5][3] = "Időpont"
        rows[39][0] = "HACCP Terv melléklet\nKiadás: 1.0 - 2015.03.14."
        rows[39][4] = "Oldal: 1/1"
        rows[42][0] = "Forrás: takarítási-napló.pdf – HACCP Terv melléklet"

        month_length = days_in_month(year, month)
        for day_num in range(1, 32):
            row = 5 + day_num
            if day_num > month_length:
                continue
            day = date(year, month, day_num)
            rows[row][0] = day_num
            if self._should_fill_day(day, fill_start, fill_through):
                rows[row][1] = PERSON_NAME
                rows[row][3] = cleaning_time(day)

        return SheetPayload(
            values=rows,
            column_count=5,
            layout_requests=cleaning_layout_requests(sheet_id),
        )

    def _meleg_payload(
        self,
        sheet_id: int,
        year: int,
        month: int,
        fill_start: date,
        fill_through: date,
        fill_mode: FillMode,
    ) -> SheetPayload:
        """Build the hot-water food temperature month tab."""

        rows = common_haccp_header(
            title="Meleg vizes melegentartó hőmérséklet-ellenőrző napló",
            subtitle="(28/2017. (V. 30.) FM rendelet alapján készült)",
            year=year,
            month=month,
            document_title="Közös gép, külön GN edények: virsli és lesütött gyros hús",
            note=(
                "A mérés az étel hőmérsékletére / maghőmérsékletére vonatkozik, "
                "nem csak a vízfürdő hőmérsékletére. Előírt érték: legalább +63 °C. "
                "+63 °C alatti mérésnél a tálalást fel kell függeszteni, és az "
                "intézkedést rögzíteni kell."
            ),
            columns=[
                "Dátum",
                "Időpont / mérés",
                "Berendezés",
                "GN edény / étel",
                "Előírt hőm.",
                "Mért étel-hőm.",
                "Megfelel?",
                "Ellenőrizte",
                "Eltérés / intézkedés",
            ],
        )

        for day_num in range(1, days_in_month(year, month) + 1):
            rows.extend(
                self._meleg_day_rows(
                    year, month, day_num, fill_start, fill_through, fill_mode
                )
            )

        return SheetPayload(
            values=rows,
            column_count=9,
            layout_requests=haccp_layout_requests(sheet_id, len(rows), meleg_widths()),
        )

    def _meleg_day_rows(
        self,
        year: int,
        month: int,
        day_num: int,
        fill_start: date,
        fill_through: date,
        fill_mode: FillMode,
    ) -> list[list[object]]:
        """Return the four hot-holding rows for one calendar day."""

        day = date(year, month, day_num)
        rows: list[list[object]] = []
        for index, (label, food, slot) in enumerate(MELEG_ROWS):
            row: list[object] = [
                day_num if index == 0 else "",
                label,
                MELEG_DEVICE,
                food,
                MELEG_MIN_TEMP,
                "",
                "",
                "",
                "",
            ]
            if self._should_fill_slot(day, fill_start, fill_through, fill_mode, slot):
                row[5] = meleg_temperature(day, index)
                row[6] = "Igen"
                row[7] = PERSON_NAME
            rows.append(row)
        return rows

    def _huto_payload(
        self,
        sheet_id: int,
        year: int,
        month: int,
        fill_start: date,
        fill_through: date,
        fill_mode: FillMode,
    ) -> SheetPayload:
        """Build the fridge/freezer temperature month tab."""

        rows = common_haccp_header(
            title="HACCP hőmérséklet-ellenőrző napló",
            subtitle="(28/2017. (V. 30.) FM rendelet alapján készült)",
            year=year,
            month=month,
            document_title="Hűtő- és mélyhűtő hőmérséklet-ellenőrző lap",
            note=(
                "Előírt értékek: Hűtő 1: 0-5 °C | Hűtő 2: 0-5 °C | "
                "Mélyhűtő: -18 °C vagy alatta. Eltérés esetén az intézkedést "
                "mindig rögzíteni kell."
            ),
            columns=[
                "Dátum",
                "Időpont",
                "Hűtő 1 °C",
                "Hűtő 2 °C",
                "Mélyhűtő °C",
                "Tisztaság\nrendben?",
                "Megfelel?",
                "Ellenőrizte",
                "Eltérés / intézkedés",
            ],
        )

        for day_num in range(1, days_in_month(year, month) + 1):
            rows.extend(
                self._huto_day_rows(
                    year, month, day_num, fill_start, fill_through, fill_mode
                )
            )

        return SheetPayload(
            values=rows,
            column_count=9,
            layout_requests=haccp_layout_requests(sheet_id, len(rows), huto_widths()),
        )

    def _huto_day_rows(
        self,
        year: int,
        month: int,
        day_num: int,
        fill_start: date,
        fill_through: date,
        fill_mode: FillMode,
    ) -> list[list[object]]:
        """Return the two fridge/freezer rows for one calendar day."""

        day = date(year, month, day_num)
        rows: list[list[object]] = []
        for index, (label, slot) in enumerate(HUTO_ROWS):
            row: list[object] = [
                day_num if index == 0 else "",
                label,
                "",
                "",
                "",
                "",
                "",
                "",
                "",
            ]
            if self._should_fill_slot(day, fill_start, fill_through, fill_mode, slot):
                fridge_1, fridge_2, freezer = huto_temperatures(day, slot)
                row[2] = fridge_1
                row[3] = fridge_2
                row[4] = freezer
                row[5] = "Igen"
                row[6] = "Igen"
                row[7] = PERSON_NAME
            rows.append(row)
        return rows

    def _should_fill_day(self, day: date, fill_start: date, fill_through: date) -> bool:
        """Return true when a date is inside range and is a workday."""

        return fill_start <= day <= fill_through and self.workdays.is_workday(day)

    def _should_fill_slot(
        self,
        day: date,
        fill_start: date,
        fill_through: date,
        fill_mode: FillMode,
        slot: str,
    ) -> bool:
        """Return true when a morning/afternoon row should be filled now."""

        if not self._should_fill_day(day, fill_start, fill_through):
            return False
        if fill_mode == "all" or fill_mode == "afternoon":
            return True
        if slot == "morning":
            return True
        return day < fill_through


def common_haccp_header(
    title: str,
    subtitle: str,
    year: int,
    month: int,
    document_title: str,
    note: str,
    columns: list[str],
) -> list[list[object]]:
    """Return the six common header rows for HACCP measurement tabs."""

    return [
        [title, "", "", "", "", "", "", "", ""],
        [subtitle, "", "", "", "", "", "", "", ""],
        [
            "Egység neve: Nomad Bistro mozgóbolt",
            "",
            "",
            "",
            "",
            f"Hónap: {MONTH_HEADER_NAMES[month]}",
            "",
            f"Év: {year}",
            "",
        ],
        [document_title, "", "", "", "", "", "", "", ""],
        [note, "", "", "", "", "", "", "", ""],
        columns,
    ]


def cleaning_layout_requests(sheet_id: int) -> list[dict[str, Any]]:
    """Return formatting requests for the cleaning log month tab."""

    requests = base_format_requests(sheet_id, row_count=43, column_count=5)
    requests.extend(width_requests(sheet_id, [70, 150, 150, 90, 90]))
    requests.extend(
        [
            merge_request(sheet_id, 0, 1, 0, 5),
            merge_request(sheet_id, 4, 5, 1, 5),
            merge_request(sheet_id, 4, 6, 0, 1),
            merge_request(sheet_id, 5, 6, 1, 3),
            merge_request(sheet_id, 5, 6, 3, 5),
            merge_request(sheet_id, 39, 41, 0, 2),
            merge_request(sheet_id, 39, 41, 2, 4),
            merge_request(sheet_id, 39, 41, 4, 5),
            merge_request(sheet_id, 42, 43, 0, 5),
        ]
    )
    for row_index in range(6, 37):
        requests.append(merge_request(sheet_id, row_index, row_index + 1, 1, 3))
        requests.append(merge_request(sheet_id, row_index, row_index + 1, 3, 5))
    requests.extend(
        [
            format_request(sheet_id, 0, 1, 0, 5, bold=True, font_size=14, center=True),
            format_request(sheet_id, 4, 6, 0, 5, bold=True, background=grey(0.9), center=True),
            format_request(sheet_id, 6, 37, 0, 5, center=True),
            format_request(sheet_id, 39, 43, 0, 5, font_size=9, wrap=True),
            border_request(sheet_id, 4, 37, 0, 5),
            border_request(sheet_id, 39, 43, 0, 5),
        ]
    )
    return requests


def haccp_layout_requests(
    sheet_id: int, row_count: int, widths: list[int]
) -> list[dict[str, Any]]:
    """Return formatting requests for HACCP measurement month tabs."""

    requests = base_format_requests(sheet_id, row_count=row_count, column_count=9)
    requests.extend(width_requests(sheet_id, widths))
    requests.extend(
        [
            merge_request(sheet_id, 0, 1, 0, 9),
            merge_request(sheet_id, 1, 2, 0, 9),
            merge_request(sheet_id, 3, 4, 0, 9),
            merge_request(sheet_id, 4, 5, 0, 9),
            format_request(sheet_id, 0, 1, 0, 9, bold=True, font_size=13, center=True),
            format_request(sheet_id, 1, 2, 0, 9, italic=True, center=True),
            format_request(sheet_id, 3, 5, 0, 9, bold=True, background=grey(0.92), wrap=True),
            format_request(sheet_id, 5, 6, 0, 9, bold=True, background=grey(0.86), center=True, wrap=True),
            format_request(sheet_id, 6, row_count, 0, 9, center=True, wrap=True),
            border_request(sheet_id, 0, row_count, 0, 9),
        ]
    )
    return requests


def base_format_requests(
    sheet_id: int, row_count: int, column_count: int
) -> list[dict[str, Any]]:
    """Return common text wrapping and vertical alignment formatting."""

    return [
        {
            "repeatCell": {
                "range": grid_range(sheet_id, 0, row_count, 0, column_count),
                "cell": {
                    "userEnteredFormat": {
                        "wrapStrategy": "WRAP",
                        "verticalAlignment": "MIDDLE",
                        "textFormat": {"fontSize": 10},
                    }
                },
                "fields": "userEnteredFormat(wrapStrategy,verticalAlignment,textFormat)",
            }
        }
    ]


def merge_request(
    sheet_id: int, start_row: int, end_row: int, start_col: int, end_col: int
) -> dict[str, Any]:
    """Return a merge request using zero-based exclusive grid indexes."""

    return {
        "mergeCells": {
            "range": grid_range(sheet_id, start_row, end_row, start_col, end_col),
            "mergeType": "MERGE_ALL",
        }
    }


def format_request(
    sheet_id: int,
    start_row: int,
    end_row: int,
    start_col: int,
    end_col: int,
    *,
    bold: bool = False,
    italic: bool = False,
    font_size: int | None = None,
    background: dict[str, float] | None = None,
    center: bool = False,
    wrap: bool = False,
) -> dict[str, Any]:
    """Return a repeatCell request for common formatting flags."""

    user_format: dict[str, Any] = {"textFormat": {"bold": bold, "italic": italic}}
    fields = ["userEnteredFormat.textFormat.bold", "userEnteredFormat.textFormat.italic"]
    if font_size is not None:
        user_format["textFormat"]["fontSize"] = font_size
        fields.append("userEnteredFormat.textFormat.fontSize")
    if background is not None:
        user_format["backgroundColor"] = background
        fields.append("userEnteredFormat.backgroundColor")
    if center:
        user_format["horizontalAlignment"] = "CENTER"
        fields.append("userEnteredFormat.horizontalAlignment")
    if wrap:
        user_format["wrapStrategy"] = "WRAP"
        fields.append("userEnteredFormat.wrapStrategy")
    return {
        "repeatCell": {
            "range": grid_range(sheet_id, start_row, end_row, start_col, end_col),
            "cell": {"userEnteredFormat": user_format},
            "fields": ",".join(fields),
        }
    }


def border_request(
    sheet_id: int, start_row: int, end_row: int, start_col: int, end_col: int
) -> dict[str, Any]:
    """Return a thin border request for a rectangular range."""

    border = {"style": "SOLID", "width": 1, "color": grey(0.65)}
    return {
        "updateBorders": {
            "range": grid_range(sheet_id, start_row, end_row, start_col, end_col),
            "top": border,
            "bottom": border,
            "left": border,
            "right": border,
            "innerHorizontal": border,
            "innerVertical": border,
        }
    }


def width_requests(sheet_id: int, widths: list[int]) -> list[dict[str, Any]]:
    """Return column width requests for every generated column."""

    return [
        {
            "updateDimensionProperties": {
                "range": {
                    "sheetId": sheet_id,
                    "dimension": "COLUMNS",
                    "startIndex": index,
                    "endIndex": index + 1,
                },
                "properties": {"pixelSize": width},
                "fields": "pixelSize",
            }
        }
        for index, width in enumerate(widths)
    ]


def grid_range(
    sheet_id: int, start_row: int, end_row: int, start_col: int, end_col: int
) -> dict[str, int]:
    """Return a Sheets API GridRange dictionary."""

    return {
        "sheetId": sheet_id,
        "startRowIndex": start_row,
        "endRowIndex": end_row,
        "startColumnIndex": start_col,
        "endColumnIndex": end_col,
    }


def grey(value: float) -> dict[str, float]:
    """Return a grayscale RGB color for Sheets API formatting."""

    return {"red": value, "green": value, "blue": value}


def meleg_widths() -> list[int]:
    """Return readable column widths for the hot-holding sheet."""

    return [55, 145, 170, 210, 95, 115, 85, 135, 180]


def huto_widths() -> list[int]:
    """Return readable column widths for the fridge/freezer sheet."""

    return [55, 110, 90, 90, 105, 110, 85, 135, 180]


def a1(sheet: str, cell_range: str) -> str:
    """Build a safely quoted A1 notation range for a sheet name."""

    escaped = sheet.replace("'", "''")
    return f"'{escaped}'!{cell_range}"


def column_name(column_count: int) -> str:
    """Return the A1 column name for a one-based column count."""

    name = ""
    current = column_count
    while current:
        current, remainder = divmod(current - 1, 26)
        name = chr(65 + remainder) + name
    return name


def month_tab_name(year: int, month: int) -> str:
    """Return the stable year-prefixed Hungarian month tab name."""

    return f"{year}-{MONTH_TAB_NAMES[month]}"


def sheet_order_key(title: str) -> tuple[int, int, int]:
    """Sort month tabs newest-first while keeping non-month tabs at the end."""

    match = MONTH_TAB_PATTERN.match(title)
    if not match:
        return (1, 0, 0)
    year = int(match.group(1))
    month = int(match.group(2))
    return (0, -year, -month)


def month_header(year: int, month: int) -> str:
    """Return the human-readable year/month text used in sheet headers."""

    return f"{year}. {MONTH_HEADER_NAMES[month]}"


def days_in_month(year: int, month: int) -> int:
    """Return the number of calendar days in a month."""

    return calendar.monthrange(year, month)[1]


def iter_months(start: date, end: date) -> Iterable[tuple[int, int]]:
    """Yield `(year, month)` pairs from start month through end month."""

    current = start.replace(day=1)
    last = end.replace(day=1)
    while current <= last:
        yield current.year, current.month
        if current.month == 12:
            current = current.replace(year=current.year + 1, month=1)
        else:
            current = current.replace(month=current.month + 1)


def cleaning_time(day: date) -> str:
    """Return a stable random-looking cleaning time around 15:00."""

    minute = 14 * 60 + 50 + stable_rng(f"cleaning:{day.isoformat()}").randint(0, 20)
    return f"{minute // 60:02d}:{minute % 60:02d}"


def meleg_temperature(day: date, row_index: int) -> str:
    """Return a stable integer food temperature between +65 and +75."""

    return str(stable_rng(f"meleg:{day.isoformat()}:{row_index}").randint(65, 75))


def huto_temperatures(day: date, slot: str) -> tuple[str, str, str]:
    """Return stable fridge/freezer temperatures for one measurement slot."""

    rng = stable_rng(f"huto:{day.isoformat()}:{slot}")
    fridge_1 = f"{rng.uniform(1, 3):.1f}"
    fridge_2 = f"{rng.uniform(1, 3):.1f}"
    freezer = str(rng.randint(-25, -19))
    return fridge_1, fridge_2, freezer


def stable_rng(seed_text: str) -> random.Random:
    """Create deterministic pseudo-random values so reruns do not rewrite history."""

    digest = hashlib.sha256(seed_text.encode("utf-8")).digest()
    return random.Random(int.from_bytes(digest[:8], "big"))


def today_in_timezone(timezone: str) -> date:
    """Return today's date in the configured business timezone."""

    return datetime.now(ZoneInfo(timezone)).date()


def parse_date(value: str, timezone: str) -> date:
    """Parse an ISO date string or the special value `today`."""

    if value == "today":
        return today_in_timezone(timezone)
    return date.fromisoformat(value)


def inclusive_dates(start: date, end: date) -> Iterable[date]:
    """Yield every date in an inclusive range."""

    current = start
    while current <= end:
        yield current
        current += timedelta(days=1)
