"""Machine and Asset relationship models."""

from shopdb.extensions import db
from .base import BaseModel


class RelationshipType(BaseModel):
    """
    Types of relationships between assets.

    ADR-001 seeds three canonical types: partof, controls, connectedto.
    Sites may add legacy/communication-flavored types (Serial Cable, Direct
    Ethernet, USB, WiFi, Dualpath) for backward compatibility with pre-1.0
    data, but new ADR-001 code paths only reason about the three canonical
    types via free-text label for nuance.
    """
    __tablename__ = 'relationshiptypes'

    relationshiptypeid = db.Column(db.Integer, primary_key=True)
    relationshiptype = db.Column(db.String(50), unique=True, nullable=False)
    description = db.Column(db.Text)
    color = db.Column(db.String(20), comment='CSS color for relationship badges')

    # True: edge has a source->target meaning (controls, partof, Backup For).
    # False: symmetric link (Dualpath, connectedto, USB...) shown on the card
    # once per peer with no direction, both stored direction rows collapsed.
    # At most one ACTIVE relationship of this type per source asset. A PC has
    # one default printer; the (source, target, type) unique constraint cannot
    # express that, because two different targets are two different rows.
    issingular = db.Column(db.Boolean, nullable=False, default=False)

    isdirectional = db.Column(
        db.Boolean,
        default=True,
        nullable=False,
        server_default='1',
        comment='False = symmetric connection, shown direction-blind'
    )

    # Sibling propagation (ADR-001), WIRED. When a relationship of this type is
    # created, the create path fans it out across every through-type in this
    # type's propagation set (relationshiptypepropagations rows). For each
    # SYMMETRIC through-type P, the source's edge to the target is mirrored to
    # every asset linked to the target via P. This is M:N so one type can
    # propagate through several rails at once. Seeded rows on this DB:
    #   controls -> partof   (declared; directional rail, NOT consumed yet -
    #                         parent/child fan-out direction is ambiguous)
    #   controls -> Dualpath (consumed; a dual-bay pair is one physical machine
    #                         with one controller, so both bays carry controls)
    # A dual-bay Dualpath pair thus gets the PC's controls link on both bays.
    # propagatesthrough is the list of through-type RelationshipType rows.
    propagatesthrough = db.relationship(
        'RelationshipType',
        secondary='relationshiptypepropagations',
        primaryjoin='RelationshipType.relationshiptypeid'
                    ' == RelationshipTypePropagation.relationshiptypeid',
        secondaryjoin='RelationshipType.relationshiptypeid'
                      ' == RelationshipTypePropagation.throughtypeid',
        viewonly=True,
    )

    def __repr__(self):
        return f"<RelationshipType {self.relationshiptype}>"


class RelationshipTypePropagation(db.Model):
    """
    M:N propagation rails (ADR-001). One row means: a relationship of type
    relationshiptypeid propagates through the connections of type throughtypeid.

    Replaces the old single-valued relationshiptypes.propagatesthroughid so a
    type (controls) can propagate through several through-types (partof AND
    Dualpath) at once. Seed/CLI-managed; no management UI.
    """
    __tablename__ = 'relationshiptypepropagations'

    propagationid = db.Column(db.Integer, primary_key=True)
    relationshiptypeid = db.Column(
        db.Integer,
        db.ForeignKey('relationshiptypes.relationshiptypeid'),
        nullable=False
    )
    throughtypeid = db.Column(
        db.Integer,
        db.ForeignKey('relationshiptypes.relationshiptypeid'),
        nullable=False
    )

    __table_args__ = (
        db.UniqueConstraint(
            'relationshiptypeid',
            'throughtypeid',
            name='uq_reltype_propagation'
        ),
    )

    def __repr__(self):
        return f"<RelationshipTypePropagation {self.relationshiptypeid} through {self.throughtypeid}>"


class AssetRelationship(BaseModel):
    """
    Relationships between assets.

    Examples:
    - Computer controls Machine
    - Two machines are dualpath partners
    - Network device connects to machine
    """
    __tablename__ = 'assetrelationships'

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

    sourceassetid = db.Column(
        db.Integer,
        db.ForeignKey('assets.assetid'),
        nullable=False
    )
    targetassetid = db.Column(
        db.Integer,
        db.ForeignKey('assets.assetid'),
        nullable=False
    )
    relationshiptypeid = db.Column(
        db.Integer,
        db.ForeignKey('relationshiptypes.relationshiptypeid'),
        nullable=False
    )

    # Free-text description carrying domain nuance ("DNC feed",
    # "operator workstation", "ethernet PoE"). Avoids inflating type list.
    label = db.Column(db.String(200), comment='Free-text relationship description (ADR-001)')

    # When true, resolve_asset_position walks across this edge (priority 2
    # in the resolution chain). Defaults to true for partof + controls when
    # the relationship is created via the API; nullable for legacy rows.
    inheritsposition = db.Column(
        db.Boolean,
        default=True,
        nullable=False,
        server_default='1',
        comment='If true, resolved-position walk follows this edge (ADR-001)'
    )

    notes = db.Column(db.Text)

    sourceasset = db.relationship(
        'Asset',
        foreign_keys=[sourceassetid],
        backref='outgoing_relationships'
    )
    targetasset = db.relationship(
        'Asset',
        foreign_keys=[targetassetid],
        backref='incoming_relationships'
    )
    relationshiptype = db.relationship('RelationshipType', backref='asset_relationships')

    __table_args__ = (
        db.UniqueConstraint(
            'sourceassetid',
            'targetassetid',
            'relationshiptypeid',
            name='uq_asset_relationship'
        ),
        db.Index('idx_asset_rel_source', 'sourceassetid'),
        db.Index('idx_asset_rel_target', 'targetassetid'),
    )

    def __repr__(self):
        return f"<AssetRelationship {self.sourceassetid} -> {self.targetassetid}>"
