# remottxrea/automation/circadian_scheduler.py

import asyncio
import math
from datetime import datetime, timedelta, time
from typing import List


class CircadianScheduler:
    """
    Circadian Activity Scheduler

    Features:
    - Even shift distribution (4 → 20 sessions)
    - 24h divisibility logic
    - True offline / online switching
    - Weekly rotation
    - Async safe execution
    """

    MIN_SESSIONS = 4
    MAX_SESSIONS = 20

    def __init__(self, clients: List):

        if len(clients) < self.MIN_SESSIONS:
            raise ValueError("Minimum 4 sessions required")

        if len(clients) > self.MAX_SESSIONS:
            raise ValueError("Maximum 20 sessions supported")

        if len(clients) % 2 != 0:
            raise ValueError("Session count must be even")

        self.clients = clients
        self.session_count = len(clients)

        # Generate shifts
        self.shifts = self._generate_shifts()

    # --------------------------------------------------
    # SHIFT GENERATOR
    # --------------------------------------------------

    def _generate_shifts(self):
        """
        Divide 24h into equal parts
        Example:
            4  → 6h shifts
            6  → 4h shifts
            8  → 3h shifts
            12 → 2h shifts
        """

        shift_count = self.session_count // 2
        shift_hours = 24 / shift_count

        shifts = []

        for i in range(shift_count):
            start_hour = shift_hours * i
            end_hour = start_hour + shift_hours

            shifts.append(
                (
                    time(int(start_hour) % 24),
                    time(int(end_hour) % 24)
                )
            )

        return shifts

    # --------------------------------------------------
    # WEEKLY ROTATION
    # --------------------------------------------------

    def _get_rotation_offset(self):

        """
        Rotate shifts weekly
        """

        week_number = datetime.utcnow().isocalendar()[1]

        return week_number % len(self.shifts)

    # --------------------------------------------------
    # SHIFT ASSIGNMENT
    # --------------------------------------------------

    def get_client_shift(self, index):

        rotation = self._get_rotation_offset()

        shift_index = (index + rotation) % len(self.shifts)

        return self.shifts[shift_index]

    # --------------------------------------------------
    # TIME CHECK
    # --------------------------------------------------

    @staticmethod
    def _is_now_in_shift(start: time, end: time):

        now = datetime.utcnow().time()

        if start < end:
            return start <= now < end
        else:
            # Overnight shift
            return now >= start or now < end

    # --------------------------------------------------
    # ONLINE / OFFLINE CONTROL
    # --------------------------------------------------

    async def go_online(self, client):

        if not client.is_connected:
            await client.connect()

    async def go_offline(self, client):

        if client.is_connected:
            await client.disconnect()

    # --------------------------------------------------
    # MAIN LOOP
    # --------------------------------------------------

    async def run(self):

        """
        Infinite safe scheduler loop
        """

        while True:

            tasks = []

            for index, client in enumerate(self.clients):

                start, end = self.get_client_shift(index)

                in_shift = self._is_now_in_shift(start, end)

                if in_shift:
                    tasks.append(self.go_online(client))
                else:
                    tasks.append(self.go_offline(client))

            await asyncio.gather(*tasks)

            # Safety delay
            await asyncio.sleep(300)  # check every 5 min


# Singleton
circadian_scheduler = None
