from __future__ import annotations

import typing as T

from datetime import datetime

from pycs import db
from pycs.database.base import NamedBaseModel
from pycs.database.util import commit_on_return

def compare_children(start_label: Label, identifier: int) -> bool:
    """ check for cyclic relationships """

    labels_to_check = [start_label]

    while labels_to_check:
        label = labels_to_check.pop(0)

        if label.id == identifier:
            return False

        labels_to_check.extend(label.children)

    return True

def _label_id():
    return Label.id

class Label(NamedBaseModel):
    """ DB Model for labels """

    id = db.Column(db.Integer, primary_key=True)

    project_id = db.Column(
        db.Integer,
        db.ForeignKey("project.id", ondelete="CASCADE"),
        nullable=False)

    parent_id = db.Column(
        db.Integer,
        db.ForeignKey("label.id", ondelete="SET NULL"))

    created = db.Column(db.DateTime, default=datetime.utcnow,
        index=True, nullable=False)

    reference = db.Column(db.String)
    hierarchy_level = db.Column(db.String)

    # contraints
    __table_args__ = (
        db.UniqueConstraint('project_id', 'reference'),
    )

    # relationships to other models
    parent = db.relationship("Label",
        backref="children",
        remote_side=_label_id,
    )

    results = db.relationship("Result",
        backref="label",
        passive_deletes=True,
        lazy="dynamic",
    )

    serialize_only = NamedBaseModel.serialize_only + (
        "project_id",
        "parent_id",
        "reference",
        "hierarchy_level",
        "children",
    )

    @commit_on_return
    def set_parent(self, parent: T.Optional[T.Union[int, str, Label]] = None) -> None:

        """
        set this labels parent

        :param parent: parent label. Can be a reference, an id or a Label instance
        :return:
        """
        parent_id = None
        if parent is not None:
            if isinstance(parent, Label):
                parent_id = parent.id

            elif isinstance(parent, str):
                parent_id = Label.query.filter(Label.reference == parent).one().id

            elif isinstance(parent, int):
                parent_id = parent

            if not compare_children(self, parent_id):
                raise ValueError('Cyclic relationship detected!')

        self.parent_id = parent_id
