#  Copyright 2026 Synnax Labs, Inc.
#
#  Use of this software is governed by the Business Source License included in the file
#  licenses/BSL.txt.
#
#  As of the Change Date specified in that file, in accordance with the Business Source
#  License, use of this software will be governed by the Apache License, Version 2.0,
#  included in the file licenses/APL.txt.

from __future__ import annotations

from dataclasses import dataclass
from typing import Literal, Sequence, TypeAlias

from pydantic import BaseModel

from synnax import ontology
from synnax.telem import DataType, TimeSpan
from synnax.util.normalize import normalize

Key = int

ONTOLOGY_TYPE = ontology.ID(type="channel")


def ontology_id(key: Key) -> ontology.ID:
    """Returns the ontology ID for the Channel entity."""
    return ontology.ID(type=ONTOLOGY_TYPE.type, key=str(key))


OPERATION_TYPES = Literal["min", "max", "avg", "none"]


class Operation(BaseModel):
    """Represents an operation on a calculated channel."""

    type: OPERATION_TYPES
    reset_channel: Key = 0
    duration: TimeSpan = TimeSpan(0)


class Payload(BaseModel):
    """A payload container that represent the properties of a channel exchanged to and
    from the Synnax server.
    """

    key: Key = 0
    data_type: DataType
    name: str = ""
    leaseholder: int = 0
    is_index: bool = False
    index: Key = 0
    internal: bool = False
    virtual: bool = False
    expression: str | None = ""
    operations: list[Operation] | None = None

    def __str__(self) -> str:
        return f"Channel(name={self.name}, key={self.key})"

    def __hash__(self) -> int:
        return hash(self.key)


@dataclass
class NormalizedKeyResult:
    single: bool
    channels: list[Key] | tuple[Key]


@dataclass
class NormalizedNameResult:
    single: bool
    channels: list[str]


Params: TypeAlias = (
    Key | str | Payload | Sequence[Key] | Sequence[str] | Sequence[Payload]
)


def normalize_params(
    channels: Params,
) -> NormalizedKeyResult | NormalizedNameResult:
    """Determine if a list of keys or names is a single key or name."""
    normalized = normalize(channels)
    if len(normalized) == 0:
        return NormalizedKeyResult(single=False, channels=[])
    single = isinstance(channels, (Key, str))
    first = normalized[0]
    if isinstance(first, str):
        str_list = [s for s in normalized if isinstance(s, str)]
        if len(str_list) != len(normalized):
            raise TypeError(
                "channel params must be all keys or all names, got a mix of both"
            )
        try:
            return NormalizedKeyResult(
                single=single,
                channels=[Key(s) for s in str_list],
            )
        except (ValueError, TypeError):
            return NormalizedNameResult(
                single=single,
                channels=str_list,
            )
    elif isinstance(first, Payload):
        payload_list = [c.key for c in normalized if isinstance(c, Payload)]
        if len(payload_list) != len(normalized):
            raise TypeError(
                "channel params must be all keys or all names, got a mix of both"
            )
        return NormalizedKeyResult(single=single, channels=payload_list)
    key_list = [k for k in normalized if isinstance(k, int)]
    if len(key_list) != len(normalized):
        raise TypeError(
            "channel params must be all keys or all names, got a mix of both"
        )
    return NormalizedKeyResult(single=single, channels=key_list)


def has_params(channels: Params | None) -> bool:
    if channels is None:
        return False
    if isinstance(channels, (Key, str, Payload)):
        return True
    return len(channels) > 0


from synnax.util.deprecation import deprecated_getattr

_DEPRECATED = {
    "ChannelKey": "Key",
    "ChannelParams": "Params",
    "ChannelPayload": "Payload",
}

__getattr__ = deprecated_getattr(__name__, _DEPRECATED, globals())
