import os

from granian import Granian
from granian.constants import Interfaces, Loops
from pydantic import BaseModel, PositiveInt
from pydantic_settings import BaseSettings, SettingsConfigDict


class WorkersSettings(BaseModel):
    per_core: PositiveInt = 1
    max: PositiveInt | None = None
    exact: PositiveInt | None = None

    @property
    def count(self) -> int:
        # usage of "or" operator: values here are not allowed to be 0
        base = self.exact or (self.per_core * (os.cpu_count() or 1))
        return min(base, self.max or base)


class BindSettings(BaseModel):
    host: str = "0.0.0.0"
    port: int = 8000


class Settings(BaseSettings):
    model_config = SettingsConfigDict(
        env_prefix="ADVENT22__",
        env_nested_delimiter="__",
    )

    workers: WorkersSettings = WorkersSettings()
    bind: BindSettings = BindSettings()


def start():
    os.environ["ADVENT22__PRODUCTION_MODE"] = "true"

    settings = Settings()
    server = Granian(
        "advent22_api.app:app",
        address=settings.bind.host,
        port=settings.bind.port,
        workers=settings.workers.count,
        interface=Interfaces.ASGI,
        loop=Loops.uvloop,
        process_name="advent22",
    )
    server.serve()
