from typing import Optional, Set

from pydantic import BaseModel, root_validator, validator

from .defaults import LEFT_ARROW_KEY_CODE, RIGHT_ARROW_KEY_CODE


class Key(BaseModel):
    ids: Set[int]
    name: Optional[str] = None

    @validator("ids", each_item=True)
    def id_is_posint(cls, v: int):
        if v < 0:
            raise ValueError("Key ids cannot be negative integers")
        return v

    def match(self, key_id: int):
        return key_id in self.ids


class Config(BaseModel):
    QUIT: Key = Key(ids=[ord("q")], name="QUIT")
    CONTINUE: Key = Key(ids=[RIGHT_ARROW_KEY_CODE], name="CONTINUE / NEXT")
    BACK: Key = Key(ids=[LEFT_ARROW_KEY_CODE], name="BACK")
    REWIND: Key = Key(ids=[ord("r")], name="REWIND")
    PLAY_PAUSE: Key = Key(ids=[32], name="PLAY / PAUSE")

    @root_validator
    def ids_are_unique_across_keys(cls, values):
        ids = set()

        for key in values.values():
            if len(ids.intersection(key.ids)) != 0:
                raise ValueError(
                    f"Two or more keys share a common key code: please make sure each key has distinc key codes"
                )
            ids.update(key.ids)

        return values

    def merge_with(self, other: "Config") -> "Config":
        for key_name, key in self:
            other_key = getattr(other, key_name)
            key.ids.update(other_key.ids)
            key.name = other_key.name or key.name

        return self