import threading
from typing import TypeVar

from .. import abc
from ..disposable import Disposable
from ..internal import DisposedException
from ..observable import Observable
from ..observer import Observer
from .innersubscription import InnerSubscription

_T = TypeVar("_T")


class Subject(Observable[_T], Observer[_T], abc.SubjectBase[_T]):
    """Represents an object that is both an observable sequence as well
    as an observer. Each notification is broadcasted to all subscribed
    observers.
    """

    def __init__(self) -> None:
        super().__init__()

        self.is_disposed = False
        self.observers: list[abc.ObserverBase[_T]] = []
        self.exception: Exception | None = None

        self.lock = threading.RLock()

    def check_disposed(self) -> None:
        if self.is_disposed:
            raise DisposedException()

    def _subscribe_core(
        self,
        observer: abc.ObserverBase[_T],
        scheduler: abc.SchedulerBase | None = None,
    ) -> abc.DisposableBase:
        with self.lock:
            self.check_disposed()
            if not self.is_stopped:
                self.observers.append(observer)
                return InnerSubscription(self, observer)

            if self.exception is not None:
                observer.on_error(self.exception)
            else:
                observer.on_completed()
            return Disposable()

    def on_next(self, value: _T) -> None:
        """Notifies all subscribed observers with the value.

        Args:
            value: The value to send to all subscribed observers.
        """

        with self.lock:
            self.check_disposed()
        super().on_next(value)

    def _on_next_core(self, value: _T) -> None:
        with self.lock:
            observers = self.observers.copy()

        for observer in observers:
            observer.on_next(value)

    def on_error(self, error: Exception) -> None:
        """Notifies all subscribed observers with the exception.

        Args:
            error: The exception to send to all subscribed observers.
        """

        with self.lock:
            self.check_disposed()
        super().on_error(error)

    def _on_error_core(self, error: Exception) -> None:
        with self.lock:
            observers = self.observers.copy()
            self.observers.clear()
            self.exception = error

        for observer in observers:
            observer.on_error(error)

    def on_completed(self) -> None:
        """Notifies all subscribed observers of the end of the sequence."""

        with self.lock:
            self.check_disposed()
        super().on_completed()

    def _on_completed_core(self) -> None:
        with self.lock:
            observers = self.observers.copy()
            self.observers.clear()

        for observer in observers:
            observer.on_completed()

    def dispose(self) -> None:
        """Unsubscribe all observers and release resources."""

        with self.lock:
            self.is_disposed = True
            self.observers = []
            self.exception = None
            super().dispose()
