from datetime import datetime
from zoneinfo import ZoneInfo

from ports.availability_repository import AvailabilityRepository
from use_cases.resolve_services_by_names import ResolveServicesByNamesUseCase


class GetServiceAvailabilityUseCase:
    NO_SERVICES_MESSAGE = "No active services matching those names could be found."
    NO_SLOTS_MESSAGE = (
        "No availability found for the requested services in the next 7 days."
    )

    def __init__(
        self,
        resolve_services: ResolveServicesByNamesUseCase,
        availability_repository: AvailabilityRepository,
    ):
        self._resolve_services = resolve_services
        self._availability = availability_repository

    async def execute(
        self,
        *,
        salon_id: str,
        timezone: str,
        service_names: list[str],
        date: str,
    ) -> str:
        services = await self._resolve_services.execute(salon_id, service_names)
        if not services:
            return self.NO_SERVICES_MESSAGE

        self._validate_date(date, timezone)

        schedule: dict[str, list[str]] = {}

        for service in services:
            availability = await self._availability.get_next_7_days_availability(
                service.id, date, timezone
            )
            for stylist in availability:
                stylist_name = stylist.get("name") or "Unknown stylist"
                for day in stylist.get("days") or []:
                    day_date = day.get("date")
                    if not day_date:
                        continue
                    schedule.setdefault(day_date, [])
                    for slot in day.get("slots") or []:
                        start_raw = slot.get("start")
                        end_raw = slot.get("end")
                        if not start_raw or not end_raw:
                            continue
                        tz = ZoneInfo(timezone)
                        start = datetime.fromisoformat(
                            str(start_raw).replace("Z", "+00:00")
                        ).astimezone(tz)
                        end = datetime.fromisoformat(
                            str(end_raw).replace("Z", "+00:00")
                        ).astimezone(tz)
                        entry = (
                            f"{stylist_name}: {start.strftime('%H:%M')}"
                            f"–{end.strftime('%H:%M')}"
                        )
                        if entry not in schedule[day_date]:
                            schedule[day_date].append(entry)

        if not schedule:
            return self.NO_SLOTS_MESSAGE

        lines = []
        for day_date in sorted(schedule.keys()):
            slots = "\n".join(f"  {entry}" for entry in sorted(schedule[day_date]))
            lines.append(f"{day_date}\n{slots}")
        return "\n\n".join(lines)

    @staticmethod
    def _validate_date(date: str, timezone: str) -> None:
        datetime.strptime(date, "%Y-%m-%d")
        ZoneInfo(timezone)
