import asyncio
import datetime

import typing_extensions as typing

from unitelabs.cdk import sila


class MultiClientTest(sila.Feature):
    """Test different server behaviors when multiple clients request execution of the same command."""

    def __init__(self):
        super().__init__(originator="org.silastandard", category="test")

    @sila.ObservableCommand(mode=sila.ExecutionMode.PARALLEL)
    async def run_in_parallel(
        self,
        duration: typing.Annotated[
            float, sila.constraints.Unit(label="s", components=[sila.constraints.Unit.Component("Second")])
        ],
        *,
        status: sila.Status,
    ) -> None:
        """
        Multiple invocations of this command will be running in parallel.

        Args:
          Duration: The duration of the command execution
        """

        status.update(progress=0, remaining_time=datetime.timedelta(seconds=duration))

        await asyncio.sleep(duration)

    @sila.ObservableCommand(mode=sila.ExecutionMode.QUEUED)
    async def run_queued(
        self,
        duration: typing.Annotated[
            float, sila.constraints.Unit(label="s", components=[sila.constraints.Unit.Component("Second")])
        ],
        *,
        status: sila.Status,
    ) -> None:
        """
        Multiple invocations of this command will be queued.

        Args:
          Duration: The duration of the command execution
        """

        status.update(progress=0, remaining_time=datetime.timedelta(seconds=duration))

        await asyncio.sleep(duration)

    @sila.ObservableCommand(mode=sila.ExecutionMode.QUEUED)
    async def reject_parallel_execution(
        self,
        duration: typing.Annotated[
            float, sila.constraints.Unit(label="s", components=[sila.constraints.Unit.Component("Second")])
        ],
        *,
        status: sila.Status,
    ) -> None:
        """
        Invocations will be rejected, if there is another command instance already running.

        Args:
          Duration: The duration of the command execution
        """

        status.update(progress=0, remaining_time=datetime.timedelta(seconds=duration))

        await asyncio.sleep(duration)
