"""event host contact

Revision ID: a3c9d05e2f71
Revises: e1f4a90c73bd
Create Date: 2026-08-18 10:00:00.000000

Moves host identity down a level, from the wedding to the event (design D1). `event` gains
`host_name_1` (required), `host_name_2` (optional) and `host_phone` (required), because two
ceremonies of one wedding are routinely hosted by different families and a wedding-wide
number cannot be right for both.

Add-nullable → backfill → set-NOT-NULL, in one revision rather than three (design D5): a
half-applied sequence would leave `event` in a state neither the old nor the new code can
use.

The backfill is honest about what it does and does not know — see the comments in
`_backfill`. Every pre-existing event comes out of this revision carrying a placeholder name,
and possibly a placeholder phone, that an operator must replace.

DOWNGRADE DESTROYS DATA. Dropping the three columns discards every host name and phone
entered since the upgrade, and there is nothing to restore them from — the wedding-wide
`host_contact_phone` this revision reads is never written back to.
"""

from collections.abc import Sequence

import sqlalchemy as sa
from alembic import op

revision: str = "a3c9d05e2f71"
down_revision: str | None = "e1f4a90c73bd"
branch_labels: str | Sequence[str] | None = None
depends_on: str | Sequence[str] | None = None

#: Written where the wedding holds no contact number. Deliberately not a blank, not a
#: plausible-looking number, and not a valid Bangladeshi mobile: an admin opening the editor
#: sees something obviously wrong, and a guest who dials it reaches nobody rather than
#: reaching a stranger who has nothing to do with the wedding (design D5).
PLACEHOLDER_PHONE = "+880000000000"

#: Written as the host name on every pre-existing event. There is nothing true to copy: the
#: host is a *parent* of the couple, and `wedding` stores only `bride_name` and `groom_name`.
#: Writing those would put the couple's own names under "Invited By" on every existing
#: invitation — not an approximation of the truth but a statement the domain says is false.
#: A placeholder is wrong in a way an admin notices; the couple's names would be wrong in a
#: way a guest believes (design D5).
PLACEHOLDER_HOST_NAME = "Set host name"


#: Copies the wedding's contact number onto each of its events, falling back to the sentinel
#: where the wedding has none. Module-level and parameterised rather than inline so
#: `test_event_host_backfill.py` can run this exact statement — a test against a
#: reimplementation of it would agree with itself while the migration was wrong.
BACKFILL_FROM_WEDDING_SQL = """
    UPDATE event
       SET host_phone = COALESCE(
               NULLIF(TRIM(wedding.host_contact_phone), ''),
               :placeholder_phone
           ),
           host_name_1 = :placeholder_name,
           host_name_2 = NULL
      FROM wedding
     WHERE wedding.id = event.wedding_id
"""

#: An event whose wedding row is somehow missing would be skipped by the join above and would
#: then fail the NOT NULL step with an error naming neither the row nor the cause. The foreign
#: key makes that impossible, which is exactly what makes being certain cheap.
BACKFILL_ORPHANS_SQL = """
    UPDATE event
       SET host_phone = COALESCE(host_phone, :placeholder_phone),
           host_name_1 = COALESCE(host_name_1, :placeholder_name)
     WHERE host_phone IS NULL
        OR host_name_1 IS NULL
"""


def _backfill() -> None:
    """Give every existing row a value, so the NOT NULL constraints below can be applied.

    The phone is a genuine derivation — `wedding.host_contact_phone` always was the family's
    contact number, which is precisely what a host phone is. The name is not derivable at
    all, so it gets the placeholder rather than a guess.
    """
    params = {
        "placeholder_phone": PLACEHOLDER_PHONE,
        "placeholder_name": PLACEHOLDER_HOST_NAME,
    }
    op.execute(sa.text(BACKFILL_FROM_WEDDING_SQL).bindparams(**params))
    op.execute(sa.text(BACKFILL_ORPHANS_SQL).bindparams(**params))


def upgrade() -> None:
    op.add_column("event", sa.Column("host_name_1", sa.Text(), nullable=True))
    op.add_column("event", sa.Column("host_name_2", sa.Text(), nullable=True))
    op.add_column("event", sa.Column("host_phone", sa.String(length=20), nullable=True))

    _backfill()

    op.alter_column("event", "host_name_1", existing_type=sa.Text(), nullable=False)
    op.alter_column("event", "host_phone", existing_type=sa.String(length=20), nullable=False)


def downgrade() -> None:
    op.drop_column("event", "host_phone")
    op.drop_column("event", "host_name_2")
    op.drop_column("event", "host_name_1")
