import traceback
from typing import Optional, Any

from bussdcc.device import DeviceProtocol
from bussdcc.context import ContextProtocol
from bussdcc import message


class Device(DeviceProtocol):
    kind: str = "device"
    id: str
    ctx: ContextProtocol | None
    online: bool

    def __init__(self, *, id: str, config: Optional[dict[str, Any]] = None):
        self.id = id
        self.config = config or {}
        self.ctx = None
        self.online = False

    def attach(self, ctx: ContextProtocol) -> None:
        self.ctx = ctx
        try:
            self.connect()
        except Exception as e:
            if self.ctx:
                self.ctx.emit(
                    message.DeviceFailed(
                        device=self.id,
                        kind=self.kind,
                        error=repr(e),
                        traceback=traceback.format_exc(),
                    )
                )
            raise
        self.online = True
        if self.ctx:
            self.ctx.emit(message.DeviceAttached(device=self.id, kind=self.kind))

    def detach(self) -> None:
        try:
            self.disconnect()
        except Exception as e:
            if self.ctx:
                self.ctx.emit(
                    message.DeviceFailed(
                        device=self.id,
                        kind=self.kind,
                        error=repr(e),
                        traceback=traceback.format_exc(),
                    )
                )
        finally:
            self.online = False
            if self.ctx:
                self.ctx.emit(message.DeviceDetached(device=self.id, kind=self.kind))
            self.ctx = None

    def connect(self) -> None:
        """Open hardware / resources"""
        pass

    def disconnect(self) -> None:
        """Close hardware / resources"""
        pass
