from typing import Optional, TypedDict

from sqlalchemy.orm import Mapped, mapped_column, relationship

from app.brm.brn import BRN
from app.extensions import db
from app.models import AbstractConfiguration, AbstractResource
from app.models.base import Group
from app.util.onion import onion_hostname


class OnionDict(TypedDict):
    Id: int
    DomainName: str
    OnionName: str


class Onion(AbstractConfiguration):
    @property
    def brn(self) -> BRN:
        return BRN(
            group_id=self.group_id,
            product="eotk",
            provider="*",
            resource_type="onion",
            resource_id=self.onion_name
        )

    group_id = db.Column(db.Integer(), db.ForeignKey("group.id"), nullable=False)
    domain_name = db.Column(db.String(255), nullable=False)

    onion_public_key = db.Column(db.LargeBinary, nullable=False)
    onion_private_key = db.Column(db.LargeBinary, nullable=False)

    tls_public_key = db.Column(db.LargeBinary, nullable=False)
    tls_private_key = db.Column(db.LargeBinary, nullable=False)

    group = db.relationship("Group", back_populates="onions")

    @property
    def onion_name(self) -> str:
        return onion_hostname(self.onion_public_key)

    def to_dict(self) -> OnionDict:
        return {
            "Id": self.id,
            "DomainName": self.domain_name,
            "OnionName": self.onion_name,
        }


class Eotk(AbstractResource):
    group_id: Mapped[int] = mapped_column(db.Integer(), db.ForeignKey("group.id"))
    instance_id: Mapped[Optional[str]]
    provider: Mapped[str]
    region: Mapped[str]

    group: Mapped[Group] = relationship("Group", back_populates="eotks")

    @property
    def brn(self) -> BRN:
        return BRN(
            group_id=self.group_id,
            provider=self.provider,
            product="eotk",
            resource_type="instance",
            resource_id=self.region
        )
