import sqlalchemy as sa
from sqlalchemy.orm import backref, relationship

from ..config import Base


class __CommonColumns(object):
    f_binary = sa.Column(sa.LargeBinary, nullable=True)
    f_boolean = sa.Column(sa.Boolean, nullable=True)
    f_string = sa.Column(sa.String, nullable=True)
    f_text = sa.Column(sa.Text, nullable=True)
    f_integer = sa.Column(sa.Integer, nullable=True)
    f_float = sa.Column(sa.Float, nullable=True)
    f_decimal = sa.Column(sa.Float(asdecimal=True), nullable=True)
    f_date = sa.Column(sa.Date, nullable=True)
    f_time = sa.Column(sa.Time, nullable=True)
    f_datetime = sa.Column(sa.DateTime, nullable=True)


class ModelGeneralOneToOne(Base):
    __tablename__ = "model_general_one_to_one"

    parent_id = sa.Column(
        sa.Integer,
        sa.ForeignKey("model_general_one.id", ondelete="CASCADE"),
        primary_key=True,
    )
    parent = relationship("ModelGeneralOne", foreign_keys=[parent_id])

    child_id = sa.Column(
        sa.Integer,
        sa.ForeignKey("model_general_one.id", ondelete="CASCADE"),
        primary_key=True,
    )
    child = relationship("ModelGeneralOne", foreign_keys=[child_id])


class ModelGeneralOne(__CommonColumns, Base):
    __tablename__ = "model_general_one"

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

    # --- self relations ---

    # self one-to-one
    parent_1_1_id = sa.Column(
        sa.Integer, sa.ForeignKey("model_general_one.id", ondelete="CASCADE")
    )
    child_1_1 = relationship(
        "ModelGeneralOne",
        backref=backref("parent_1_1", remote_side=[id]),
        foreign_keys=[parent_1_1_id],
        uselist=False,
    )

    # self one-to-many and many-to-one
    parent_x_1_id = sa.Column(sa.Integer, sa.ForeignKey("model_general_one.id"))
    child_1_x = relationship(
        "ModelGeneralOne",
        backref=backref("parent_x_1", remote_side=[id]),
        foreign_keys=[parent_x_1_id],
    )

    # self many-to-many
    parent_x_x = relationship(
        "ModelGeneralOne",
        secondary="model_general_one_to_one",
        back_populates="child_x_x",
        primaryjoin=(id == ModelGeneralOneToOne.__table__.c.parent_id),
        secondaryjoin=(id == ModelGeneralOneToOne.__table__.c.child_id),
    )
    child_x_x = relationship(
        "ModelGeneralOne",
        secondary="model_general_one_to_one",
        back_populates="parent_x_x",
        primaryjoin=(id == ModelGeneralOneToOne.__table__.c.child_id),
        secondaryjoin=(id == ModelGeneralOneToOne.__table__.c.parent_id),
    )

    # --- relations with other ---

    # one-to-one
    two_1_1_id = sa.Column(
        sa.Integer, sa.ForeignKey("model_general_two.id", ondelete="CASCADE")
    )
    two_1_1 = relationship(
        "ModelGeneralTwo",
        back_populates="one_1_1",
        foreign_keys=[two_1_1_id],
        post_update=True,
    )

    # one-to-many
    two_1_x = relationship(
        "ModelGeneralTwo",
        foreign_keys="[ModelGeneralTwo.one_x_1_id]",
        back_populates="one_x_1",
        post_update=True,
    )

    # many-to-one
    two_x_1_id = sa.Column(
        sa.Integer, sa.ForeignKey("model_general_two.id", ondelete="CASCADE")
    )
    two_x_1 = relationship(
        "ModelGeneralTwo",
        back_populates="one_1_x",
        foreign_keys=[two_x_1_id],
        post_update=True,
    )

    # many-to-many
    two_x_x = relationship(
        "ModelGeneralTwo",
        secondary="model_general_one_to_two",
        back_populates="one_x_x",
    )


class ModelGeneralOneToTwo(Base):
    __tablename__ = "model_general_one_to_two"

    one_id = sa.Column(
        sa.Integer,
        sa.ForeignKey(ModelGeneralOne.id, ondelete="CASCADE"),
        primary_key=True,
    )
    one = relationship(ModelGeneralOne, foreign_keys=[one_id])

    two_id = sa.Column(
        sa.Integer,
        sa.ForeignKey("model_general_two.id", ondelete="CASCADE"),
        primary_key=True,
    )
    two = relationship("ModelGeneralTwo", foreign_keys=[two_id])


class ModelGeneralTwoToTwo(Base):
    __tablename__ = "model_general_two_to_two"

    parent_id = sa.Column(
        sa.Integer,
        sa.ForeignKey("model_general_two.id", ondelete="CASCADE"),
        primary_key=True,
    )
    parent = relationship("ModelGeneralTwo", foreign_keys=[parent_id])

    child_id = sa.Column(
        sa.Integer,
        sa.ForeignKey("model_general_two.id", ondelete="CASCADE"),
        primary_key=True,
    )
    child = relationship("ModelGeneralTwo", foreign_keys=[child_id])


class ModelGeneralTwo(__CommonColumns, Base):
    __tablename__ = "model_general_two"

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

    # --- self relations ---

    # self one-to-one
    parent_1_1_id = sa.Column(
        sa.Integer, sa.ForeignKey("model_general_two.id", ondelete="CASCADE")
    )
    child_1_1 = relationship(
        "ModelGeneralTwo",
        backref=backref("parent_1_1", remote_side=[id]),
        foreign_keys=[parent_1_1_id],
        uselist=False,
    )

    # self one-to-many and many-to-one
    parent_x_1_id = sa.Column(sa.Integer, sa.ForeignKey("model_general_two.id"))
    child_1_x = relationship(
        "ModelGeneralTwo",
        backref=backref("parent_x_1", remote_side=[id]),
        foreign_keys=[parent_x_1_id],
    )

    # self many-to-many
    parent_x_x = relationship(
        "ModelGeneralTwo",
        secondary="model_general_two_to_two",
        back_populates="child_x_x",
        primaryjoin=(id == ModelGeneralTwoToTwo.__table__.c.parent_id),
        secondaryjoin=(id == ModelGeneralTwoToTwo.__table__.c.child_id),
    )
    child_x_x = relationship(
        "ModelGeneralTwo",
        secondary="model_general_two_to_two",
        back_populates="parent_x_x",
        primaryjoin=(id == ModelGeneralTwoToTwo.__table__.c.child_id),
        secondaryjoin=(id == ModelGeneralTwoToTwo.__table__.c.parent_id),
    )

    # --- relations with other ---

    # one-to-one
    one_1_1 = relationship(
        ModelGeneralOne,
        back_populates="two_1_1",
        uselist=False,
        foreign_keys=[ModelGeneralOne.two_1_1_id],
    )

    # one-to-many
    one_1_x = relationship(
        ModelGeneralOne,
        foreign_keys=[ModelGeneralOne.two_x_1_id],
        back_populates="two_x_1",
        post_update=True,
    )

    # many-to-one
    one_x_1_id = sa.Column(
        sa.Integer,
        sa.ForeignKey(ModelGeneralOne.id, use_alter=True, ondelete="CASCADE"),
    )
    one_x_1 = relationship(
        ModelGeneralOne,
        back_populates="two_1_x",
        foreign_keys=[one_x_1_id],
        post_update=True,
    )

    # many-to-many
    one_x_x = relationship(
        ModelGeneralOne, secondary="model_general_one_to_two", back_populates="two_x_x"
    )
