from typing import Optional

from sqlalchemy import Integer
from sqlalchemy import String
from sqlalchemy.orm import DeclarativeBase
from sqlalchemy.orm import Mapped
from sqlalchemy.orm import mapped_column


class Base(DeclarativeBase):
    pass


class X(Base):
    __tablename__ = "x"

    # these are fine - pk, column is not null, have the attribute be
    # non-optional, fine
    id: Mapped[int] = mapped_column(primary_key=True)
    int_id: Mapped[int] = mapped_column(Integer, primary_key=True)

    # but this is also "fine" because the developer may wish to have the object
    # in a pending state with None for the id for some period of time.
    # "primary_key=True" will still be interpreted correctly in DDL
    err_int_id: Mapped[Optional[int]] = mapped_column(
        Integer, primary_key=True
    )

    # also fine, X(err_int_id_name) is None when you first make the
    # object
    err_int_id_name: Mapped[Optional[int]] = mapped_column(
        "err_int_id_name", Integer, primary_key=True
    )

    id_name: Mapped[int] = mapped_column("id_name", primary_key=True)
    int_id_name: Mapped[int] = mapped_column(
        "int_id_name", Integer, primary_key=True
    )

    a: Mapped[str] = mapped_column()
    b: Mapped[Optional[str]] = mapped_column()

    # this can't be detected because we don't know the type
    c: Mapped[str] = mapped_column(nullable=True)
    d: Mapped[str] = mapped_column(nullable=False)

    e: Mapped[Optional[str]] = mapped_column(nullable=True)

    f: Mapped[Optional[str]] = mapped_column(nullable=False)

    g: Mapped[str] = mapped_column(String)
    h: Mapped[Optional[str]] = mapped_column(String)

    # this probably is wrong.  however at the moment it seems better to
    # decouple the right hand arguments from declaring things about the
    # left side since it mostly doesn't work in any case.
    i: Mapped[str] = mapped_column(String, nullable=True)

    j: Mapped[str] = mapped_column(String, nullable=False)

    k: Mapped[Optional[str]] = mapped_column(String, nullable=True)

    l: Mapped[Optional[str]] = mapped_column(String, nullable=False)

    a_name: Mapped[str] = mapped_column("a_name")
    b_name: Mapped[Optional[str]] = mapped_column("b_name")

    c_name: Mapped[str] = mapped_column("c_name", nullable=True)
    d_name: Mapped[str] = mapped_column("d_name", nullable=False)

    e_name: Mapped[Optional[str]] = mapped_column("e_name", nullable=True)

    f_name: Mapped[Optional[str]] = mapped_column("f_name", nullable=False)

    g_name: Mapped[str] = mapped_column("g_name", String)
    h_name: Mapped[Optional[str]] = mapped_column("h_name", String)

    i_name: Mapped[str] = mapped_column("i_name", String, nullable=True)

    j_name: Mapped[str] = mapped_column("j_name", String, nullable=False)

    k_name: Mapped[Optional[str]] = mapped_column(
        "k_name", String, nullable=True
    )

    l_name: Mapped[Optional[str]] = mapped_column(
        "l_name",
        String,
        nullable=False,
    )
