import pytest
from flask_sqlalchemy.model import Model
from sqlalchemy.orm import declarative_base

from .test_basic import CustomModelView


@pytest.mark.parametrize(
    "session_or_db",
    [
        pytest.param("session", id="with_session_deprecated"),
        pytest.param("db", id="with_db"),
    ],
)
def test_multiple_pk(app, db, admin, session_or_db):
    # Test multiple primary keys - mix int and string together
    with app.app_context():

        class Model(db.Model):  # type: ignore[name-defined, misc]
            id = db.Column(db.Integer, primary_key=True)
            id2 = db.Column(db.String(20), primary_key=True)
            test = db.Column(db.String)

        db.create_all()

        param = db.session if session_or_db == "session" else db
        view = CustomModelView(Model, param, form_columns=["id", "id2", "test"])
        admin.add_view(view)

        client = app.test_client()

        rv = client.get("/admin/model/")
        assert rv.status_code == 200

        rv = client.post("/admin/model/new/", data=dict(id=1, id2="two", test="test3"))
        assert rv.status_code == 302

        rv = client.get("/admin/model/")
        assert rv.status_code == 200
        data = rv.data.decode("utf-8")
        assert "test3" in data

        rv = client.get("/admin/model/edit/?id=1,two")
        assert rv.status_code == 200
        data = rv.data.decode("utf-8")
        assert "test3" in data

        # Correct order is mandatory -> fail here
        rv = client.get("/admin/model/edit/?id=two,1")
        assert rv.status_code == 302


@pytest.mark.parametrize(
    "session_or_db",
    [
        pytest.param("session", id="with_session_deprecated"),
        pytest.param("db", id="with_db"),
    ],
)
def test_joined_inheritance(app, db, admin, session_or_db):
    # Test multiple primary keys - mix int and string together
    with app.app_context():

        class Parent(db.Model):  # type: ignore[name-defined, misc]
            id = db.Column(db.Integer, primary_key=True)
            test = db.Column(db.String)

            discriminator = db.Column("type", db.String(50))
            __mapper_args__ = {"polymorphic_on": discriminator}

        class Child(Parent):
            __tablename__ = "children"
            __mapper_args__ = {"polymorphic_identity": "child"}

            id = db.Column(db.ForeignKey(Parent.id), primary_key=True)
            name = db.Column(db.String(100))

        db.create_all()

        param = db.session if session_or_db == "session" else db
        view = CustomModelView(Child, param, form_columns=["id", "test", "name"])
        admin.add_view(view)

        client = app.test_client()

        rv = client.get("/admin/child/")
        assert rv.status_code == 200

        rv = client.post("/admin/child/new/", data=dict(id=1, test="foo", name="bar"))
        assert rv.status_code == 302

        rv = client.get("/admin/child/edit/?id=1")
        assert rv.status_code == 200
        data = rv.data.decode("utf-8")
        assert "foo" in data
        assert "bar" in data


@pytest.mark.parametrize(
    "session_or_db",
    [
        pytest.param("session", id="with_session_deprecated"),
        pytest.param("db", id="with_db"),
    ],
)
def test_single_table_inheritance(app, db, admin, session_or_db):
    # Test multiple primary keys - mix int and string together
    with app.app_context():
        CustomModel = declarative_base(cls=Model, name="Model")

        class Parent(CustomModel):  # type: ignore[valid-type, misc]
            __tablename__ = "parent"

            id = db.Column(db.Integer, primary_key=True)
            test = db.Column(db.String)

            discriminator = db.Column("type", db.String(50))
            __mapper_args__ = {"polymorphic_on": discriminator}

        class Child(Parent):
            __mapper_args__ = {"polymorphic_identity": "child"}
            name = db.Column(db.String(100))

        CustomModel.metadata.create_all(db.engine)

        param = db.session if session_or_db == "session" else db
        view = CustomModelView(Child, param, form_columns=["id", "test", "name"])
        admin.add_view(view)

        client = app.test_client()

        rv = client.get("/admin/child/")
        assert rv.status_code == 200

        rv = client.post("/admin/child/new/", data=dict(id=1, test="foo", name="bar"))
        assert rv.status_code == 302

        rv = client.get("/admin/child/edit/?id=1")
        assert rv.status_code == 200
        data = rv.data.decode("utf-8")
        assert "foo" in data
        assert "bar" in data


@pytest.mark.parametrize(
    "session_or_db",
    [
        pytest.param("session", id="with_session_deprecated"),
        pytest.param("db", id="with_db"),
    ],
)
def test_concrete_table_inheritance(app, db, admin, session_or_db):
    # Test multiple primary keys - mix int and string together
    with app.app_context():

        class Parent(db.Model):  # type: ignore[name-defined, misc]
            id = db.Column(db.Integer, primary_key=True)
            test = db.Column(db.String)

        class Child(Parent):
            __mapper_args__ = {"concrete": True}
            id = db.Column(db.Integer, primary_key=True)
            name = db.Column(db.String(100))
            test = db.Column(db.String)

        db.create_all()

        param = db.session if session_or_db == "session" else db
        view = CustomModelView(Child, param, form_columns=["id", "test", "name"])
        admin.add_view(view)

        client = app.test_client()

        rv = client.get("/admin/child/")
        assert rv.status_code == 200

        rv = client.post("/admin/child/new/", data=dict(id=1, test="foo", name="bar"))
        assert rv.status_code == 302

        rv = client.get("/admin/child/edit/?id=1")
        assert rv.status_code == 200
        data = rv.data.decode("utf-8")
        assert "foo" in data
        assert "bar" in data


@pytest.mark.parametrize(
    "session_or_db",
    [
        pytest.param("session", id="with_session_deprecated"),
        pytest.param("db", id="with_db"),
    ],
)
def test_concrete_multipk_inheritance(app, db, admin, session_or_db):
    # Test multiple primary keys - mix int and string together
    with app.app_context():

        class Parent(db.Model):  # type: ignore[name-defined, misc]
            id = db.Column(db.Integer, primary_key=True)
            test = db.Column(db.String)

        class Child(Parent):
            __mapper_args__ = {"concrete": True}
            id = db.Column(db.Integer, primary_key=True)
            id2 = db.Column(db.Integer, primary_key=True)
            name = db.Column(db.String(100))
            test = db.Column(db.String)

        db.create_all()

        param = db.session if session_or_db == "session" else db
        view = CustomModelView(Child, param, form_columns=["id", "id2", "test", "name"])
        admin.add_view(view)

        client = app.test_client()

        rv = client.get("/admin/child/")
        assert rv.status_code == 200

        rv = client.post(
            "/admin/child/new/", data=dict(id=1, id2=2, test="foo", name="bar")
        )
        assert rv.status_code == 302

        rv = client.get("/admin/child/edit/?id=1,2")
        assert rv.status_code == 200
        data = rv.data.decode("utf-8")
        assert "foo" in data
        assert "bar" in data
