"""Timed task operations mixin for TaskMateCoordinator."""

from __future__ import annotations

import logging
from datetime import datetime
from typing import TYPE_CHECKING

from homeassistant.util import dt as dt_util

from .models import ChoreCompletion, TimedSession

if TYPE_CHECKING:
    pass

_LOGGER = logging.getLogger(__name__)


class TimedMixin:
    """Mixin providing timed task start/pause/stop and session management."""

    async def async_start_timed_task(self, chore_id: str, child_id: str) -> None:
        """Start or resume a timed task session."""
        chore = self.get_chore(chore_id)
        if not chore:
            raise ValueError(f"Chore {chore_id} not found")
        if chore.task_type != "timed":
            raise ValueError(f"Chore '{chore.name}' is not a timed task")

        child = self.get_child(child_id)
        if not child:
            raise ValueError(f"Child {child_id} not found")

        now = dt_util.now()
        today = dt_util.as_local(now).date().isoformat()

        existing = self.storage.get_active_timed_session(chore_id, child_id)
        if existing and existing.state == "running":
            raise ValueError("Timer is already running")

        if existing and existing.state == "paused":
            # Check daily cap before resuming
            if chore.timed_max_daily_minutes > 0 and existing.total_seconds_today >= chore.timed_max_daily_minutes * 60:
                raise ValueError(f"Daily cap reached ({chore.timed_max_daily_minutes} min)")
            existing.state = "running"
            existing.segments.append({"start": now.isoformat(), "end": None})
            self.storage.save_timed_session(existing)
        else:
            # Check daily cap before starting fresh
            if chore.timed_max_daily_minutes > 0:
                old_session = self.storage.get_timed_session(chore_id, child_id, today)
                if old_session and old_session.total_seconds_today >= chore.timed_max_daily_minutes * 60:
                    raise ValueError(f"Daily cap reached ({chore.timed_max_daily_minutes} min)")
            session = TimedSession(
                chore_id=chore_id,
                child_id=child_id,
                state="running",
                segments=[{"start": now.isoformat(), "end": None}],
                total_seconds_today=0,
                session_date=today,
            )
            self.storage.save_timed_session(session)

        await self.storage.async_save()
        await self.async_refresh()

    async def async_pause_timed_task(self, chore_id: str, child_id: str) -> None:
        """Pause a running timed task session."""
        session = self.storage.get_active_timed_session(chore_id, child_id)
        if not session or session.state != "running":
            raise ValueError("No running timer to pause")

        now = dt_util.now()
        if session.segments and session.segments[-1].get("end") is None:
            session.segments[-1]["end"] = now.isoformat()

        session.total_seconds_today = self._calc_session_seconds(session)
        session.state = "paused"
        self.storage.save_timed_session(session)
        await self.storage.async_save()
        await self.async_refresh()

    async def async_stop_timed_task(self, chore_id: str, child_id: str) -> None:
        """Stop a timed task session and create a completion."""
        session = self.storage.get_active_timed_session(chore_id, child_id)
        if not session:
            raise ValueError("No active timer to stop")

        chore = self.get_chore(chore_id)
        if not chore:
            raise ValueError(f"Chore {chore_id} not found")

        child = self.get_child(child_id)
        if not child:
            raise ValueError(f"Child {child_id} not found")

        now = dt_util.now()

        # Close running segment
        if session.state == "running" and session.segments and session.segments[-1].get("end") is None:
            session.segments[-1]["end"] = now.isoformat()

        total_seconds = self._calc_session_seconds(session)

        # Clamp to daily cap
        if chore.timed_max_daily_minutes > 0:
            max_seconds = chore.timed_max_daily_minutes * 60
            total_seconds = min(total_seconds, max_seconds)

        # Calculate points. Guard against a mis-configured zero rate, which
        # would otherwise raise ZeroDivisionError and wedge the session.
        rate_seconds = chore.timed_rate_minutes * 60
        pts = (total_seconds // rate_seconds) * chore.timed_rate_points if rate_seconds > 0 else 0

        completion = ChoreCompletion(
            chore_id=chore_id,
            child_id=child_id,
            completed_at=now,
            approved=not chore.requires_approval,
            points_awarded=pts if not chore.requires_approval else 0,
            timed_duration_seconds=total_seconds,
        )

        if not chore.requires_approval:
            total_awarded = await self._award_points(child, pts)
            completion.approved = True
            completion.approved_at = dt_util.now()
            completion.points_awarded = total_awarded

        self.storage.add_completion(completion)
        self.storage.set_last_completed(chore_id, child_id, now.isoformat())
        self.storage.remove_timed_session(session.id)

        await self.storage.async_save()

        if chore.requires_approval:
            await self._async_notify_pending_approval(
                child.name,
                chore.name,
                pts,
                completion_id=completion.id,
            )

        await self.async_refresh()

    def _calc_session_seconds(self, session: TimedSession) -> int:
        """Calculate total elapsed seconds from session segments."""
        total = 0
        for seg in session.segments:
            start_str = seg.get("start")
            end_str = seg.get("end")
            if not start_str:
                continue
            try:
                start_dt = datetime.fromisoformat(start_str)
                end_dt = datetime.fromisoformat(end_str) if end_str else dt_util.now()
                diff = (end_dt - start_dt).total_seconds()
                if diff > 0:
                    total += int(diff)
            except (ValueError, TypeError):
                continue
        return total

    async def _async_stop_stale_timed_sessions(self) -> None:
        """Auto-stop any timed sessions from a previous day (midnight cleanup)."""
        today = dt_util.as_local(dt_util.now()).date().isoformat()
        sessions = self.storage.get_timed_sessions()
        stale = [s for s in sessions if s.session_date != today and s.state in ("running", "paused")]

        for session in stale:
            chore = self.get_chore(session.chore_id)
            child = self.get_child(session.child_id)
            if not chore or not child:
                self.storage.remove_timed_session(session.id)
                continue

            # Close any open segment at midnight (tz-aware so it can be
            # subtracted from the tz-aware segment start)
            if session.segments and session.segments[-1].get("end") is None:
                midnight = dt_util.start_of_local_day()
                session.segments[-1]["end"] = midnight.isoformat()

            total_seconds = self._calc_session_seconds(session)
            if chore.timed_max_daily_minutes > 0:
                total_seconds = min(total_seconds, chore.timed_max_daily_minutes * 60)

            rate_seconds = chore.timed_rate_minutes * 60
            pts = (total_seconds // rate_seconds) * chore.timed_rate_points if rate_seconds > 0 else 0

            if total_seconds > 0:
                completion = ChoreCompletion(
                    chore_id=session.chore_id,
                    child_id=session.child_id,
                    completed_at=dt_util.now(),
                    approved=not chore.requires_approval,
                    points_awarded=pts if not chore.requires_approval else 0,
                    timed_duration_seconds=total_seconds,
                )
                if not chore.requires_approval:
                    total_awarded = await self._award_points(child, pts)
                    completion.approved = True
                    completion.approved_at = dt_util.now()
                    completion.points_awarded = total_awarded
                self.storage.add_completion(completion)

            self.storage.remove_timed_session(session.id)

        if stale:
            await self.storage.async_save()
            await self.async_refresh()

    async def _async_auto_stop_capped_sessions(self) -> None:
        """Check running sessions against daily cap and auto-stop if exceeded."""
        sessions = self.storage.get_timed_sessions()
        for session in sessions:
            if session.state != "running":
                continue
            chore = self.get_chore(session.chore_id)
            if not chore or chore.timed_max_daily_minutes <= 0:
                continue
            elapsed = self._calc_session_seconds(session)
            if elapsed >= chore.timed_max_daily_minutes * 60:
                await self.async_stop_timed_task(session.chore_id, session.child_id)
