"""Relationship-type propagation goes M:N (relationshiptypepropagations)

Replaces the single-valued relationshiptypes.propagatesthroughid with a join
table so one type can propagate through several through-types at once. On this
DB `controls` needs both `partof` (declared) and `Dualpath` (consumed: a
dual-bay pair is one physical machine with a single controller, so both bays
must carry the controls link to that PC).

Upgrade: create relationshiptypepropagations, migrate the existing
propagatesthroughid value into a row, drop the column, then seed the
controls -> Dualpath row (resolved by name, skipped if either type missing).
Downgrade: recreate the column from the first propagation row per type (best
effort) and drop the table.

Idempotent guards so it is safe on a partially-migrated box.

Revision ID: 7d20_relationshiptypepropagations
Revises: 7d19_relationshiptype_directional
Create Date: 2026-07-11

"""
from alembic import op
import sqlalchemy as sa


revision = '7d20_relationshiptypepropagations'
down_revision = '7d19_relationshiptype_directional'
branch_labels = None
depends_on = None


def _has_column(insp, table, column):
    return column in [c['name'] for c in insp.get_columns(table)]


def upgrade():
    bind = op.get_bind()
    insp = sa.inspect(bind)

    if 'relationshiptypepropagations' not in insp.get_table_names():
        op.create_table(
            'relationshiptypepropagations',
            sa.Column('propagationid', sa.Integer(), primary_key=True),
            sa.Column('relationshiptypeid', sa.Integer(), nullable=False),
            sa.Column('throughtypeid', sa.Integer(), nullable=False),
            sa.ForeignKeyConstraint(['relationshiptypeid'],
                                    ['relationshiptypes.relationshiptypeid']),
            sa.ForeignKeyConstraint(['throughtypeid'],
                                    ['relationshiptypes.relationshiptypeid']),
            sa.UniqueConstraint('relationshiptypeid', 'throughtypeid',
                                name='uq_reltype_propagation'),
        )

    if _has_column(insp, 'relationshiptypes', 'propagatesthroughid'):
        # migrate the single-FK value into a row (dedup guard for re-runs)
        bind.execute(sa.text(
            "INSERT INTO relationshiptypepropagations "
            "(relationshiptypeid, throughtypeid) "
            "SELECT rt.relationshiptypeid, rt.propagatesthroughid "
            "FROM relationshiptypes rt "
            "WHERE rt.propagatesthroughid IS NOT NULL AND NOT EXISTS ("
            "  SELECT 1 FROM relationshiptypepropagations p "
            "  WHERE p.relationshiptypeid = rt.relationshiptypeid "
            "    AND p.throughtypeid = rt.propagatesthroughid)"))
        # drop the self-FK (known name from 7a01, else reflect) then the column
        fk_names = [fk['name'] for fk in insp.get_foreign_keys('relationshiptypes')
                    if fk.get('constrained_columns') == ['propagatesthroughid']
                    and fk.get('name')]
        for name in fk_names:
            op.drop_constraint(name, 'relationshiptypes', type_='foreignkey')
        op.drop_column('relationshiptypes', 'propagatesthroughid')

    # seed controls -> Dualpath by name; skip if either type is missing
    rows = bind.execute(sa.text(
        "SELECT relationshiptypeid, relationshiptype FROM relationshiptypes")).fetchall()
    byname = {r[1]: r[0] for r in rows}
    controls = byname.get('controls')
    dualpath = byname.get('Dualpath')
    if controls and dualpath:
        exists = bind.execute(sa.text(
            "SELECT 1 FROM relationshiptypepropagations "
            "WHERE relationshiptypeid = :c AND throughtypeid = :d"),
            {'c': controls, 'd': dualpath}).fetchone()
        if not exists:
            bind.execute(sa.text(
                "INSERT INTO relationshiptypepropagations "
                "(relationshiptypeid, throughtypeid) VALUES (:c, :d)"),
                {'c': controls, 'd': dualpath})


def downgrade():
    bind = op.get_bind()
    insp = sa.inspect(bind)

    if not _has_column(insp, 'relationshiptypes', 'propagatesthroughid'):
        op.add_column('relationshiptypes',
                      sa.Column('propagatesthroughid', sa.Integer(), nullable=True))
        op.create_foreign_key('fk_relationshiptype_propagation',
                              'relationshiptypes', 'relationshiptypes',
                              ['propagatesthroughid'], ['relationshiptypeid'])

    if 'relationshiptypepropagations' in insp.get_table_names():
        # best effort: restore the first propagation row per type
        bind.execute(sa.text(
            "UPDATE relationshiptypes rt SET propagatesthroughid = ("
            "  SELECT p.throughtypeid FROM relationshiptypepropagations p "
            "  WHERE p.relationshiptypeid = rt.relationshiptypeid "
            "  ORDER BY p.propagationid LIMIT 1)"))
        op.drop_table('relationshiptypepropagations')
