from __future__ import annotations

import json
import uuid

import pytest

from tests.testmodels import (
    Event,
    JSONFields,
    ModelWithIndexes,
    Reporter,
    SourceFields,
    StraightFields,
    Team,
    TestSchemaForJSONField,
    Tournament,
    UUIDFkRelatedModel,
    UUIDFkRelatedNullModel,
    UUIDM2MRelatedModel,
    UUIDPkModel,
    json_pydantic_default,
)
from tortoise import Tortoise, fields
from tortoise.fields.base import DB_DEFAULT_NOT_SET
from tortoise.fields.relational import (
    BackwardFKRelation,
    ForeignKeyFieldInstance,
    ManyToManyFieldInstance,
    OneToOneFieldInstance,
)


def union_annotation(x: str, y: str) -> str:
    return f"{x} | {y}"


UNION_DICT_LIST = union_annotation("dict", "list")


# Tests that require database connection (TestDescribeModels)
@pytest.mark.asyncio
async def test_describe_models_all_serializable(db):
    val = Tortoise.describe_models()
    json.dumps(val)
    assert "models.SourceFields" in val.keys()
    assert "models.Event" in val.keys()


@pytest.mark.asyncio
async def test_describe_models_all_not_serializable(db):
    val = Tortoise.describe_models(serializable=False)
    with pytest.raises(TypeError, match="not JSON serializable"):
        json.dumps(val)
    assert "models.SourceFields" in val.keys()
    assert "models.Event" in val.keys()


# Tests that don't require database connection (TestDescribeModel - SimpleTestCase equivalent)
def test_describe_field_noninit_ser():
    field = fields.IntField(primary_key=True)
    assert field.describe(serializable=True) == {
        "name": "",
        "field_type": "IntField",
        "db_column": "",
        "db_field_types": {"": "INT"},
        "python_type": "int",
        "generated": True,
        "nullable": False,
        "unique": True,
        "indexed": True,
        "default": None,
        "db_default": "__NOT_SET__",
        "description": None,
        "docstring": None,
        "constraints": {"ge": -2147483648, "le": 2147483647},
    }


def test_describe_field_noninit():
    field = fields.IntField(primary_key=True)
    assert field.describe(serializable=False) == {
        "name": "",
        "field_type": fields.IntField,
        "db_column": "",
        "db_field_types": {"": "INT"},
        "python_type": int,
        "generated": True,
        "nullable": False,
        "unique": True,
        "indexed": True,
        "default": None,
        "db_default": DB_DEFAULT_NOT_SET,
        "description": None,
        "docstring": None,
        "constraints": {"ge": -2147483648, "le": 2147483647},
    }


def test_describe_relfield_noninit_ser():
    field = fields.ForeignKeyField("a.b")
    assert field.describe(serializable=True) == {
        "name": "",
        "field_type": "ForeignKeyFieldInstance",
        "python_type": "None",
        "generated": False,
        "nullable": False,
        "on_delete": "CASCADE",
        "unique": False,
        "indexed": False,
        "default": None,
        "db_default": "__NOT_SET__",
        "description": None,
        "docstring": None,
        "constraints": {},
        "db_constraint": True,
        "raw_field": None,
    }


def test_describe_relfield_noninit():
    field = fields.ForeignKeyField("a.b")
    assert field.describe(serializable=False) == {
        "name": "",
        "field_type": ForeignKeyFieldInstance,
        "python_type": None,
        "generated": False,
        "nullable": False,
        "on_delete": "CASCADE",
        "unique": False,
        "indexed": False,
        "default": None,
        "db_default": DB_DEFAULT_NOT_SET,
        "description": None,
        "docstring": None,
        "constraints": {},
        "raw_field": None,
        "db_constraint": True,
    }


def test_describe_models_some():
    val = Tortoise.describe_models([Event, Tournament, Reporter, Team])
    assert {"models.Event", "models.Tournament", "models.Reporter", "models.Team"} == set(
        val.keys()
    )


def test_describe_model_straight():
    val = StraightFields.describe()
    assert val == {
        "name": "models.StraightFields",
        "app": "models",
        "table": "straightfields",
        "abstract": False,
        "description": "Straight auto-mapped fields",
        "docstring": None,
        "unique_together": [["chars", "blip"]],
        "indexes": [],
        "pk_field": {
            "name": "eyedee",
            "field_type": "IntField",
            "db_column": "eyedee",
            "db_field_types": {"": "INT"},
            "python_type": "int",
            "generated": True,
            "nullable": False,
            "unique": True,
            "indexed": True,
            "default": None,
            "db_default": "__NOT_SET__",
            "description": "Da PK",
            "docstring": None,
            "constraints": {"ge": -2147483648, "le": 2147483647},
        },
        "data_fields": [
            {
                "name": "chars",
                "field_type": "CharField",
                "db_column": "chars",
                "db_field_types": {
                    "": "VARCHAR(50)",
                    "oracle": "NVARCHAR2(50)",
                },
                "python_type": "str",
                "generated": False,
                "nullable": False,
                "unique": False,
                "indexed": True,
                "default": None,
                "db_default": "__NOT_SET__",
                "description": "Some chars",
                "docstring": None,
                "constraints": {"max_length": 50},
            },
            {
                "name": "blip",
                "field_type": "CharField",
                "db_column": "blip",
                "db_field_types": {
                    "": "VARCHAR(50)",
                    "oracle": "NVARCHAR2(50)",
                },
                "python_type": "str",
                "generated": False,
                "nullable": False,
                "unique": False,
                "indexed": False,
                "default": "BLIP",
                "db_default": "__NOT_SET__",
                "description": None,
                "docstring": None,
                "constraints": {"max_length": 50},
            },
            {
                "name": "nullable",
                "field_type": "CharField",
                "db_column": "nullable",
                "db_field_types": {
                    "": "VARCHAR(50)",
                    "oracle": "NVARCHAR2(50)",
                },
                "python_type": "str",
                "generated": False,
                "nullable": True,
                "unique": False,
                "indexed": False,
                "default": None,
                "db_default": "__NOT_SET__",
                "description": None,
                "docstring": None,
                "constraints": {"max_length": 50},
            },
            {
                "name": "fk_id",
                "field_type": "IntField",
                "db_column": "fk_id",
                "db_field_types": {"": "INT"},
                "python_type": "int",
                "generated": False,
                "nullable": True,
                "unique": False,
                "indexed": False,
                "default": None,
                "db_default": "__NOT_SET__",
                "description": "Tree!",
                "docstring": None,
                "constraints": {"ge": -2147483648, "le": 2147483647},
            },
            {
                "db_column": "o2o_id",
                "db_field_types": {"": "INT"},
                "default": None,
                "db_default": "__NOT_SET__",
                "description": "Line",
                "docstring": None,
                "field_type": "IntField",
                "generated": False,
                "indexed": True,
                "name": "o2o_id",
                "nullable": True,
                "python_type": "int",
                "unique": True,
                "constraints": {"ge": -2147483648, "le": 2147483647},
            },
        ],
        "fk_fields": [
            {
                "name": "fk",
                "field_type": "ForeignKeyFieldInstance",
                "raw_field": "fk_id",
                "python_type": "models.StraightFields",
                "generated": False,
                "nullable": True,
                "unique": False,
                "on_delete": "NO ACTION",
                "indexed": False,
                "default": None,
                "db_default": "__NOT_SET__",
                "description": "Tree!",
                "docstring": None,
                "db_constraint": True,
                "constraints": {},
            }
        ],
        "backward_fk_fields": [
            {
                "name": "fkrev",
                "field_type": "BackwardFKRelation",
                "python_type": "models.StraightFields",
                "generated": False,
                "nullable": True,
                "unique": False,
                "indexed": False,
                "default": None,
                "db_default": "__NOT_SET__",
                "description": "Tree!",
                "docstring": None,
                "constraints": {},
                "db_constraint": True,
            }
        ],
        "o2o_fields": [
            {
                "default": None,
                "db_default": "__NOT_SET__",
                "description": "Line",
                "docstring": None,
                "field_type": "OneToOneFieldInstance",
                "generated": False,
                "indexed": True,
                "name": "o2o",
                "nullable": True,
                "on_delete": "NO ACTION",
                "python_type": "models.StraightFields",
                "raw_field": "o2o_id",
                "unique": True,
                "constraints": {},
                "db_constraint": True,
            }
        ],
        "backward_o2o_fields": [
            {
                "default": None,
                "db_default": "__NOT_SET__",
                "description": "Line",
                "docstring": None,
                "field_type": "BackwardOneToOneRelation",
                "generated": False,
                "indexed": False,
                "name": "o2o_rev",
                "nullable": True,
                "python_type": "models.StraightFields",
                "unique": False,
                "constraints": {},
                "db_constraint": True,
            }
        ],
        "m2m_fields": [
            {
                "name": "rel_to",
                "field_type": "ManyToManyFieldInstance",
                "python_type": "models.StraightFields",
                "generated": False,
                "nullable": False,
                "unique": True,
                "indexed": True,
                "default": None,
                "db_default": "__NOT_SET__",
                "description": "M2M to myself",
                "docstring": None,
                "constraints": {},
                "model_name": "models.StraightFields",
                "related_name": "rel_from",
                "forward_key": "straightfields_id",
                "backward_key": "straightfields_rel_id",
                "through": "straightfields_straightfields",
                "on_delete": "NO ACTION",
                "_generated": False,
                "db_constraint": True,
            },
            {
                "name": "rel_from",
                "field_type": "ManyToManyFieldInstance",
                "python_type": "models.StraightFields",
                "generated": False,
                "nullable": False,
                "unique": True,
                "indexed": True,
                "default": None,
                "db_default": "__NOT_SET__",
                "description": "M2M to myself",
                "docstring": None,
                "constraints": {},
                "model_name": "models.StraightFields",
                "related_name": "rel_to",
                "forward_key": "straightfields_rel_id",
                "backward_key": "straightfields_id",
                "through": "straightfields_straightfields",
                "on_delete": "CASCADE",
                "db_constraint": True,
                "_generated": True,
            },
        ],
    }


def test_describe_model_straight_native():
    val = StraightFields.describe(serializable=False)
    assert val == {
        "name": "models.StraightFields",
        "app": "models",
        "table": "straightfields",
        "abstract": False,
        "description": "Straight auto-mapped fields",
        "docstring": None,
        "unique_together": [["chars", "blip"]],
        "indexes": [],
        "pk_field": {
            "name": "eyedee",
            "field_type": fields.IntField,
            "db_column": "eyedee",
            "db_field_types": {"": "INT"},
            "python_type": int,
            "generated": True,
            "nullable": False,
            "unique": True,
            "indexed": True,
            "default": None,
            "db_default": DB_DEFAULT_NOT_SET,
            "description": "Da PK",
            "docstring": None,
            "constraints": {"ge": -2147483648, "le": 2147483647},
        },
        "data_fields": [
            {
                "name": "chars",
                "field_type": fields.CharField,
                "db_column": "chars",
                "db_field_types": {
                    "": "VARCHAR(50)",
                    "oracle": "NVARCHAR2(50)",
                },
                "python_type": str,
                "generated": False,
                "nullable": False,
                "unique": False,
                "indexed": True,
                "default": None,
                "db_default": DB_DEFAULT_NOT_SET,
                "description": "Some chars",
                "docstring": None,
                "constraints": {"max_length": 50},
            },
            {
                "name": "blip",
                "field_type": fields.CharField,
                "db_column": "blip",
                "db_field_types": {
                    "": "VARCHAR(50)",
                    "oracle": "NVARCHAR2(50)",
                },
                "python_type": str,
                "generated": False,
                "nullable": False,
                "unique": False,
                "indexed": False,
                "default": "BLIP",
                "db_default": DB_DEFAULT_NOT_SET,
                "description": None,
                "docstring": None,
                "constraints": {"max_length": 50},
            },
            {
                "name": "nullable",
                "field_type": fields.CharField,
                "db_column": "nullable",
                "db_field_types": {
                    "": "VARCHAR(50)",
                    "oracle": "NVARCHAR2(50)",
                },
                "python_type": str,
                "generated": False,
                "nullable": True,
                "unique": False,
                "indexed": False,
                "default": None,
                "db_default": DB_DEFAULT_NOT_SET,
                "description": None,
                "docstring": None,
                "constraints": {"max_length": 50},
            },
            {
                "name": "fk_id",
                "field_type": fields.IntField,
                "db_column": "fk_id",
                "db_field_types": {"": "INT"},
                "python_type": int,
                "generated": False,
                "nullable": True,
                "unique": False,
                "indexed": False,
                "default": None,
                "db_default": DB_DEFAULT_NOT_SET,
                "description": "Tree!",
                "docstring": None,
                "constraints": {"ge": -2147483648, "le": 2147483647},
            },
            {
                "name": "o2o_id",
                "field_type": fields.IntField,
                "db_column": "o2o_id",
                "db_field_types": {"": "INT"},
                "python_type": int,
                "generated": False,
                "nullable": True,
                "unique": True,
                "indexed": True,
                "default": None,
                "db_default": DB_DEFAULT_NOT_SET,
                "description": "Line",
                "docstring": None,
                "constraints": {"ge": -2147483648, "le": 2147483647},
            },
        ],
        "fk_fields": [
            {
                "name": "fk",
                "field_type": ForeignKeyFieldInstance,
                "raw_field": "fk_id",
                "python_type": StraightFields,
                "generated": False,
                "nullable": True,
                "on_delete": "NO ACTION",
                "unique": False,
                "indexed": False,
                "default": None,
                "db_default": DB_DEFAULT_NOT_SET,
                "description": "Tree!",
                "docstring": None,
                "constraints": {},
                "db_constraint": True,
            }
        ],
        "backward_fk_fields": [
            {
                "name": "fkrev",
                "field_type": BackwardFKRelation,
                "python_type": StraightFields,
                "generated": False,
                "nullable": True,
                "unique": False,
                "indexed": False,
                "default": None,
                "db_default": DB_DEFAULT_NOT_SET,
                "description": "Tree!",
                "docstring": None,
                "constraints": {},
                "db_constraint": True,
            }
        ],
        "o2o_fields": [
            {
                "default": None,
                "db_default": DB_DEFAULT_NOT_SET,
                "description": "Line",
                "docstring": None,
                "field_type": OneToOneFieldInstance,
                "generated": False,
                "indexed": True,
                "name": "o2o",
                "nullable": True,
                "on_delete": "NO ACTION",
                "python_type": StraightFields,
                "raw_field": "o2o_id",
                "unique": True,
                "constraints": {},
                "db_constraint": True,
            },
        ],
        "backward_o2o_fields": [
            {
                "default": None,
                "db_default": DB_DEFAULT_NOT_SET,
                "description": "Line",
                "docstring": None,
                "field_type": fields.BackwardOneToOneRelation,
                "generated": False,
                "indexed": False,
                "name": "o2o_rev",
                "nullable": True,
                "python_type": StraightFields,
                "unique": False,
                "constraints": {},
                "db_constraint": True,
            },
        ],
        "m2m_fields": [
            {
                "name": "rel_to",
                "field_type": ManyToManyFieldInstance,
                "python_type": StraightFields,
                "generated": False,
                "nullable": False,
                "unique": True,
                "indexed": True,
                "default": None,
                "db_default": DB_DEFAULT_NOT_SET,
                "description": "M2M to myself",
                "docstring": None,
                "constraints": {},
                "model_name": "models.StraightFields",
                "related_name": "rel_from",
                "forward_key": "straightfields_id",
                "backward_key": "straightfields_rel_id",
                "through": "straightfields_straightfields",
                "on_delete": "NO ACTION",
                "_generated": False,
                "db_constraint": True,
            },
            {
                "name": "rel_from",
                "field_type": ManyToManyFieldInstance,
                "python_type": StraightFields,
                "generated": False,
                "nullable": False,
                "unique": True,
                "indexed": True,
                "default": None,
                "db_default": DB_DEFAULT_NOT_SET,
                "description": "M2M to myself",
                "docstring": None,
                "constraints": {},
                "model_name": "models.StraightFields",
                "related_name": "rel_to",
                "forward_key": "straightfields_rel_id",
                "backward_key": "straightfields_id",
                "through": "straightfields_straightfields",
                "on_delete": "CASCADE",
                "_generated": True,
                "db_constraint": True,
            },
        ],
    }


def test_describe_model_source():
    val = SourceFields.describe()
    assert val == {
        "name": "models.SourceFields",
        "app": "models",
        "table": "sometable",
        "abstract": False,
        "description": "Source mapped fields",
        "docstring": "A Docstring.",
        "unique_together": [["chars", "blip"]],
        "indexes": [],
        "pk_field": {
            "name": "eyedee",
            "field_type": "IntField",
            "db_column": "sometable_id",
            "db_field_types": {"": "INT"},
            "python_type": "int",
            "generated": True,
            "nullable": False,
            "unique": True,
            "indexed": True,
            "default": None,
            "db_default": "__NOT_SET__",
            "description": "Da PK",
            "docstring": None,
            "constraints": {"ge": -2147483648, "le": 2147483647},
        },
        "data_fields": [
            {
                "name": "chars",
                "field_type": "CharField",
                "db_column": "some_chars_table",
                "db_field_types": {
                    "": "VARCHAR(50)",
                    "oracle": "NVARCHAR2(50)",
                },
                "python_type": "str",
                "generated": False,
                "nullable": False,
                "unique": False,
                "indexed": True,
                "default": None,
                "db_default": "__NOT_SET__",
                "description": "Some chars",
                "docstring": None,
                "constraints": {"max_length": 50},
            },
            {
                "name": "blip",
                "field_type": "CharField",
                "db_column": "da_blip",
                "db_field_types": {
                    "": "VARCHAR(50)",
                    "oracle": "NVARCHAR2(50)",
                },
                "python_type": "str",
                "generated": False,
                "nullable": False,
                "unique": False,
                "indexed": False,
                "default": "BLIP",
                "db_default": "__NOT_SET__",
                "description": "A docstring comment",
                "docstring": "A docstring comment",
                "constraints": {"max_length": 50},
            },
            {
                "name": "nullable",
                "field_type": "CharField",
                "db_column": "some_nullable",
                "db_field_types": {
                    "": "VARCHAR(50)",
                    "oracle": "NVARCHAR2(50)",
                },
                "python_type": "str",
                "generated": False,
                "nullable": True,
                "unique": False,
                "indexed": False,
                "default": None,
                "db_default": "__NOT_SET__",
                "description": None,
                "docstring": None,
                "constraints": {"max_length": 50},
            },
            {
                "name": "fk_id",
                "field_type": "IntField",
                "db_column": "fk_sometable",
                "db_field_types": {"": "INT"},
                "python_type": "int",
                "generated": False,
                "nullable": True,
                "unique": False,
                "indexed": False,
                "default": None,
                "db_default": "__NOT_SET__",
                "description": "Tree!",
                "docstring": None,
                "constraints": {"ge": -2147483648, "le": 2147483647},
            },
            {
                "name": "o2o_id",
                "field_type": "IntField",
                "db_column": "o2o_sometable",
                "db_field_types": {"": "INT"},
                "python_type": "int",
                "generated": False,
                "nullable": True,
                "unique": True,
                "indexed": True,
                "default": None,
                "db_default": "__NOT_SET__",
                "description": "Line",
                "docstring": None,
                "constraints": {"ge": -2147483648, "le": 2147483647},
            },
        ],
        "fk_fields": [
            {
                "name": "fk",
                "field_type": "ForeignKeyFieldInstance",
                "raw_field": "fk_id",
                "python_type": "models.SourceFields",
                "generated": False,
                "nullable": True,
                "on_delete": "NO ACTION",
                "unique": False,
                "indexed": False,
                "default": None,
                "db_default": "__NOT_SET__",
                "description": "Tree!",
                "docstring": None,
                "constraints": {},
                "db_constraint": True,
            }
        ],
        "backward_fk_fields": [
            {
                "name": "fkrev",
                "field_type": "BackwardFKRelation",
                "python_type": "models.SourceFields",
                "generated": False,
                "nullable": True,
                "unique": False,
                "indexed": False,
                "default": None,
                "db_default": "__NOT_SET__",
                "description": "Tree!",
                "docstring": None,
                "constraints": {},
                "db_constraint": True,
            }
        ],
        "o2o_fields": [
            {
                "default": None,
                "db_default": "__NOT_SET__",
                "description": "Line",
                "docstring": None,
                "field_type": "OneToOneFieldInstance",
                "generated": False,
                "indexed": True,
                "name": "o2o",
                "nullable": True,
                "on_delete": "NO ACTION",
                "python_type": "models.SourceFields",
                "raw_field": "o2o_id",
                "unique": True,
                "constraints": {},
                "db_constraint": True,
            }
        ],
        "backward_o2o_fields": [
            {
                "default": None,
                "db_default": "__NOT_SET__",
                "description": "Line",
                "docstring": None,
                "field_type": "BackwardOneToOneRelation",
                "generated": False,
                "indexed": False,
                "name": "o2o_rev",
                "nullable": True,
                "python_type": "models.SourceFields",
                "unique": False,
                "constraints": {},
                "db_constraint": True,
            }
        ],
        "m2m_fields": [
            {
                "name": "rel_to",
                "field_type": "ManyToManyFieldInstance",
                "python_type": "models.SourceFields",
                "generated": False,
                "nullable": False,
                "unique": True,
                "indexed": True,
                "default": None,
                "db_default": "__NOT_SET__",
                "description": "M2M to myself",
                "docstring": None,
                "constraints": {},
                "model_name": "models.SourceFields",
                "related_name": "rel_from",
                "forward_key": "sts_forward",
                "backward_key": "backward_sts",
                "through": "sometable_self",
                "on_delete": "NO ACTION",
                "_generated": False,
                "db_constraint": True,
            },
            {
                "name": "rel_from",
                "field_type": "ManyToManyFieldInstance",
                "python_type": "models.SourceFields",
                "generated": False,
                "nullable": False,
                "unique": True,
                "indexed": True,
                "default": None,
                "db_default": "__NOT_SET__",
                "description": "M2M to myself",
                "docstring": None,
                "constraints": {},
                "model_name": "models.SourceFields",
                "related_name": "rel_to",
                "forward_key": "backward_sts",
                "backward_key": "sts_forward",
                "through": "sometable_self",
                "on_delete": "CASCADE",
                "_generated": True,
                "db_constraint": True,
            },
        ],
    }


def test_describe_model_source_native():
    val = SourceFields.describe(serializable=False)
    assert val == {
        "name": "models.SourceFields",
        "app": "models",
        "table": "sometable",
        "abstract": False,
        "description": "Source mapped fields",
        "docstring": "A Docstring.",
        "unique_together": [["chars", "blip"]],
        "indexes": [],
        "pk_field": {
            "name": "eyedee",
            "field_type": fields.IntField,
            "db_column": "sometable_id",
            "db_field_types": {"": "INT"},
            "python_type": int,
            "generated": True,
            "nullable": False,
            "unique": True,
            "indexed": True,
            "default": None,
            "db_default": DB_DEFAULT_NOT_SET,
            "description": "Da PK",
            "docstring": None,
            "constraints": {"ge": -2147483648, "le": 2147483647},
        },
        "data_fields": [
            {
                "name": "chars",
                "field_type": fields.CharField,
                "db_column": "some_chars_table",
                "db_field_types": {
                    "": "VARCHAR(50)",
                    "oracle": "NVARCHAR2(50)",
                },
                "python_type": str,
                "generated": False,
                "nullable": False,
                "unique": False,
                "indexed": True,
                "default": None,
                "db_default": DB_DEFAULT_NOT_SET,
                "description": "Some chars",
                "docstring": None,
                "constraints": {"max_length": 50},
            },
            {
                "name": "blip",
                "field_type": fields.CharField,
                "db_column": "da_blip",
                "db_field_types": {
                    "": "VARCHAR(50)",
                    "oracle": "NVARCHAR2(50)",
                },
                "python_type": str,
                "generated": False,
                "nullable": False,
                "unique": False,
                "indexed": False,
                "default": "BLIP",
                "db_default": DB_DEFAULT_NOT_SET,
                "description": "A docstring comment",
                "docstring": "A docstring comment",
                "constraints": {"max_length": 50},
            },
            {
                "name": "nullable",
                "field_type": fields.CharField,
                "db_column": "some_nullable",
                "db_field_types": {
                    "": "VARCHAR(50)",
                    "oracle": "NVARCHAR2(50)",
                },
                "python_type": str,
                "generated": False,
                "nullable": True,
                "unique": False,
                "indexed": False,
                "default": None,
                "db_default": DB_DEFAULT_NOT_SET,
                "description": None,
                "docstring": None,
                "constraints": {"max_length": 50},
            },
            {
                "name": "fk_id",
                "field_type": fields.IntField,
                "db_column": "fk_sometable",
                "db_field_types": {"": "INT"},
                "python_type": int,
                "generated": False,
                "nullable": True,
                "unique": False,
                "indexed": False,
                "default": None,
                "db_default": DB_DEFAULT_NOT_SET,
                "description": "Tree!",
                "docstring": None,
                "constraints": {"ge": -2147483648, "le": 2147483647},
            },
            {
                "name": "o2o_id",
                "field_type": fields.IntField,
                "db_column": "o2o_sometable",
                "db_field_types": {"": "INT"},
                "python_type": int,
                "generated": False,
                "nullable": True,
                "unique": True,
                "indexed": True,
                "default": None,
                "db_default": DB_DEFAULT_NOT_SET,
                "description": "Line",
                "docstring": None,
                "constraints": {"ge": -2147483648, "le": 2147483647},
            },
        ],
        "fk_fields": [
            {
                "name": "fk",
                "field_type": ForeignKeyFieldInstance,
                "raw_field": "fk_id",
                "python_type": SourceFields,
                "generated": False,
                "nullable": True,
                "unique": False,
                "on_delete": "NO ACTION",
                "indexed": False,
                "default": None,
                "db_default": DB_DEFAULT_NOT_SET,
                "description": "Tree!",
                "docstring": None,
                "constraints": {},
                "db_constraint": True,
            }
        ],
        "backward_fk_fields": [
            {
                "name": "fkrev",
                "field_type": BackwardFKRelation,
                "python_type": SourceFields,
                "generated": False,
                "nullable": True,
                "unique": False,
                "indexed": False,
                "default": None,
                "db_default": DB_DEFAULT_NOT_SET,
                "description": "Tree!",
                "docstring": None,
                "constraints": {},
                "db_constraint": True,
            }
        ],
        "o2o_fields": [
            {
                "default": None,
                "db_default": DB_DEFAULT_NOT_SET,
                "description": "Line",
                "docstring": None,
                "field_type": OneToOneFieldInstance,
                "generated": False,
                "indexed": True,
                "name": "o2o",
                "nullable": True,
                "on_delete": "NO ACTION",
                "python_type": SourceFields,
                "raw_field": "o2o_id",
                "unique": True,
                "constraints": {},
                "db_constraint": True,
            }
        ],
        "backward_o2o_fields": [
            {
                "default": None,
                "db_default": DB_DEFAULT_NOT_SET,
                "description": "Line",
                "docstring": None,
                "field_type": fields.BackwardOneToOneRelation,
                "generated": False,
                "indexed": False,
                "name": "o2o_rev",
                "nullable": True,
                "python_type": SourceFields,
                "unique": False,
                "constraints": {},
                "db_constraint": True,
            }
        ],
        "m2m_fields": [
            {
                "name": "rel_to",
                "field_type": ManyToManyFieldInstance,
                "python_type": SourceFields,
                "generated": False,
                "nullable": False,
                "unique": True,
                "indexed": True,
                "default": None,
                "db_default": DB_DEFAULT_NOT_SET,
                "description": "M2M to myself",
                "docstring": None,
                "constraints": {},
                "model_name": "models.SourceFields",
                "related_name": "rel_from",
                "forward_key": "sts_forward",
                "backward_key": "backward_sts",
                "through": "sometable_self",
                "on_delete": "NO ACTION",
                "_generated": False,
                "db_constraint": True,
            },
            {
                "name": "rel_from",
                "field_type": ManyToManyFieldInstance,
                "python_type": SourceFields,
                "generated": False,
                "nullable": False,
                "unique": True,
                "indexed": True,
                "default": None,
                "db_default": DB_DEFAULT_NOT_SET,
                "description": "M2M to myself",
                "docstring": None,
                "constraints": {},
                "model_name": "models.SourceFields",
                "related_name": "rel_to",
                "forward_key": "backward_sts",
                "backward_key": "sts_forward",
                "through": "sometable_self",
                "on_delete": "CASCADE",
                "_generated": True,
                "db_constraint": True,
            },
        ],
    }


def test_describe_model_uuidpk():
    val = UUIDPkModel.describe()

    assert val == {
        "name": "models.UUIDPkModel",
        "app": "models",
        "table": "uuidpkmodel",
        "abstract": False,
        "description": None,
        "docstring": None,
        "unique_together": [],
        "indexes": [],
        "pk_field": {
            "name": "id",
            "field_type": "UUIDField",
            "db_column": "id",
            "db_field_types": {"": "CHAR(36)", "postgres": "UUID"},
            "python_type": "uuid.UUID",
            "generated": False,
            "nullable": False,
            "unique": True,
            "indexed": True,
            "default": "<function uuid.uuid4>",
            "db_default": "__NOT_SET__",
            "description": None,
            "docstring": None,
            "constraints": {},
        },
        "data_fields": [],
        "fk_fields": [],
        "backward_fk_fields": [
            {
                "name": "children",
                "field_type": "BackwardFKRelation",
                "python_type": "models.UUIDFkRelatedModel",
                "generated": False,
                "nullable": False,
                "unique": False,
                "indexed": False,
                "default": None,
                "db_default": "__NOT_SET__",
                "description": None,
                "docstring": None,
                "constraints": {},
                "db_constraint": True,
            },
        ],
        "o2o_fields": [],
        "backward_o2o_fields": [],
        "m2m_fields": [
            {
                "_generated": True,
                "backward_key": "uuidpkmodel_id",
                "constraints": {},
                "db_constraint": True,
                "default": None,
                "db_default": "__NOT_SET__",
                "description": None,
                "docstring": None,
                "field_type": "ManyToManyFieldInstance",
                "forward_key": "uuidm2mrelatedmodel_id",
                "generated": False,
                "indexed": True,
                "model_name": "models.UUIDM2MRelatedModel",
                "name": "peers",
                "nullable": False,
                "on_delete": "CASCADE",
                "python_type": "models.UUIDM2MRelatedModel",
                "related_name": "models",
                "through": "uuidm2mrelatedmodel_uuidpkmodel",
                "unique": True,
            }
        ],
    }


def test_describe_model_uuidpk_native():
    val = UUIDPkModel.describe(serializable=False)
    assert val == {
        "name": "models.UUIDPkModel",
        "app": "models",
        "table": "uuidpkmodel",
        "abstract": False,
        "description": None,
        "docstring": None,
        "unique_together": [],
        "indexes": [],
        "pk_field": {
            "name": "id",
            "field_type": fields.UUIDField,
            "db_column": "id",
            "db_field_types": {"": "CHAR(36)", "postgres": "UUID"},
            "python_type": uuid.UUID,
            "generated": False,
            "nullable": False,
            "unique": True,
            "indexed": True,
            "default": uuid.uuid4,
            "db_default": DB_DEFAULT_NOT_SET,
            "description": None,
            "docstring": None,
            "constraints": {},
        },
        "data_fields": [],
        "fk_fields": [],
        "backward_fk_fields": [
            {
                "name": "children",
                "field_type": BackwardFKRelation,
                "python_type": UUIDFkRelatedModel,
                "generated": False,
                "nullable": False,
                "unique": False,
                "indexed": False,
                "default": None,
                "db_default": DB_DEFAULT_NOT_SET,
                "description": None,
                "docstring": None,
                "constraints": {},
                "db_constraint": True,
            },
        ],
        "o2o_fields": [],
        "backward_o2o_fields": [],
        "m2m_fields": [
            {
                "name": "peers",
                "db_constraint": True,
                "generated": False,
                "nullable": False,
                "field_type": ManyToManyFieldInstance,
                "python_type": UUIDM2MRelatedModel,
                "unique": True,
                "indexed": True,
                "default": None,
                "db_default": DB_DEFAULT_NOT_SET,
                "description": None,
                "docstring": None,
                "constraints": {},
                "model_name": "models.UUIDM2MRelatedModel",
                "related_name": "models",
                "forward_key": "uuidm2mrelatedmodel_id",
                "backward_key": "uuidpkmodel_id",
                "through": "uuidm2mrelatedmodel_uuidpkmodel",
                "on_delete": "CASCADE",
                "_generated": True,
            }
        ],
    }


def test_describe_model_uuidpk_relatednull():
    val = UUIDFkRelatedNullModel.describe(serializable=True)

    assert val == {
        "abstract": False,
        "app": "models",
        "backward_fk_fields": [],
        "backward_o2o_fields": [],
        "data_fields": [
            {
                "db_column": "name",
                "db_field_types": {
                    "": "VARCHAR(50)",
                    "oracle": "NVARCHAR2(50)",
                },
                "default": None,
                "db_default": "__NOT_SET__",
                "description": None,
                "docstring": None,
                "field_type": "CharField",
                "generated": False,
                "indexed": False,
                "name": "name",
                "nullable": True,
                "python_type": "str",
                "unique": False,
                "constraints": {"max_length": 50},
            },
            {
                "db_column": "model_id",
                "db_field_types": {"": "CHAR(36)", "postgres": "UUID"},
                "default": None,
                "db_default": "__NOT_SET__",
                "description": None,
                "docstring": None,
                "field_type": "UUIDField",
                "generated": False,
                "indexed": False,
                "name": "model_id",
                "nullable": True,
                "python_type": "uuid.UUID",
                "unique": False,
                "constraints": {},
            },
            {
                "db_column": "parent_id",
                "db_field_types": {"": "CHAR(36)", "postgres": "UUID"},
                "default": None,
                "db_default": "__NOT_SET__",
                "description": None,
                "docstring": None,
                "field_type": "UUIDField",
                "generated": False,
                "indexed": True,
                "name": "parent_id",
                "nullable": True,
                "python_type": "uuid.UUID",
                "unique": True,
                "constraints": {},
            },
        ],
        "description": None,
        "docstring": None,
        "fk_fields": [
            {
                "default": None,
                "db_default": "__NOT_SET__",
                "description": None,
                "docstring": None,
                "field_type": "ForeignKeyFieldInstance",
                "generated": False,
                "indexed": False,
                "name": "model",
                "nullable": True,
                "on_delete": "CASCADE",
                "python_type": "models.UUIDPkModel",
                "raw_field": "model_id",
                "unique": False,
                "constraints": {},
                "db_constraint": True,
            }
        ],
        "m2m_fields": [],
        "name": "models.UUIDFkRelatedNullModel",
        "o2o_fields": [
            {
                "default": None,
                "db_default": "__NOT_SET__",
                "description": None,
                "docstring": None,
                "field_type": "OneToOneFieldInstance",
                "generated": False,
                "indexed": True,
                "name": "parent",
                "nullable": True,
                "on_delete": "NO ACTION",
                "python_type": "models.UUIDPkModel",
                "raw_field": "parent_id",
                "unique": True,
                "constraints": {},
                "db_constraint": True,
            }
        ],
        "pk_field": {
            "db_column": "id",
            "db_field_types": {"": "CHAR(36)", "postgres": "UUID"},
            "default": "<function uuid.uuid4>",
            "db_default": "__NOT_SET__",
            "description": None,
            "docstring": None,
            "field_type": "UUIDField",
            "generated": False,
            "indexed": True,
            "name": "id",
            "nullable": False,
            "python_type": "uuid.UUID",
            "unique": True,
            "constraints": {},
        },
        "table": "uuidfkrelatednullmodel",
        "unique_together": [],
        "indexes": [],
    }


def test_describe_model_json():
    val = JSONFields.describe()

    assert val == {
        "name": "models.JSONFields",
        "app": "models",
        "table": "jsonfields",
        "abstract": False,
        "description": "This model contains many JSON blobs",
        "docstring": "This model contains many JSON blobs",
        "unique_together": [],
        "indexes": [],
        "pk_field": {
            "name": "id",
            "field_type": "IntField",
            "db_column": "id",
            "db_field_types": {"": "INT"},
            "python_type": "int",
            "generated": True,
            "nullable": False,
            "unique": True,
            "indexed": True,
            "default": None,
            "db_default": "__NOT_SET__",
            "description": None,
            "docstring": None,
            "constraints": {"ge": -2147483648, "le": 2147483647},
        },
        "data_fields": [
            {
                "name": "data",
                "field_type": "JSONField",
                "db_column": "data",
                "db_field_types": {
                    "": "JSON",
                    "mssql": "NVARCHAR(MAX)",
                    "oracle": "NCLOB",
                    "postgres": "JSONB",
                },
                "python_type": UNION_DICT_LIST,
                "generated": False,
                "nullable": False,
                "unique": False,
                "indexed": False,
                "default": None,
                "db_default": "__NOT_SET__",
                "description": None,
                "docstring": None,
                "constraints": {},
            },
            {
                "name": "data_null",
                "field_type": "JSONField",
                "db_column": "data_null",
                "db_field_types": {
                    "": "JSON",
                    "mssql": "NVARCHAR(MAX)",
                    "oracle": "NCLOB",
                    "postgres": "JSONB",
                },
                "python_type": UNION_DICT_LIST,
                "generated": False,
                "nullable": True,
                "unique": False,
                "indexed": False,
                "default": None,
                "db_default": "__NOT_SET__",
                "description": None,
                "docstring": None,
                "constraints": {},
            },
            {
                "name": "data_default",
                "field_type": "JSONField",
                "db_column": "data_default",
                "db_field_types": {
                    "": "JSON",
                    "mssql": "NVARCHAR(MAX)",
                    "oracle": "NCLOB",
                    "postgres": "JSONB",
                },
                "python_type": UNION_DICT_LIST,
                "generated": False,
                "nullable": False,
                "unique": False,
                "indexed": False,
                "default": "{'a': 1}",
                "db_default": "__NOT_SET__",
                "description": None,
                "docstring": None,
                "constraints": {},
            },
            {
                "name": "data_validate",
                "field_type": "JSONField",
                "db_column": "data_validate",
                "db_field_types": {
                    "": "JSON",
                    "mssql": "NVARCHAR(MAX)",
                    "oracle": "NCLOB",
                    "postgres": "JSONB",
                },
                "python_type": UNION_DICT_LIST,
                "generated": False,
                "nullable": True,
                "unique": False,
                "indexed": False,
                "default": None,
                "db_default": "__NOT_SET__",
                "description": None,
                "docstring": None,
                "constraints": {},
            },
            {
                "name": "data_pydantic",
                "field_type": "JSONField",
                "db_column": "data_pydantic",
                "db_field_types": {
                    "": "JSON",
                    "mssql": "NVARCHAR(MAX)",
                    "oracle": "NCLOB",
                    "postgres": "JSONB",
                },
                "python_type": "tests.testmodels.TestSchemaForJSONField",
                "generated": False,
                "nullable": False,
                "unique": False,
                "indexed": False,
                "default": "foo=1 bar='baz'",
                "db_default": "__NOT_SET__",
                "description": None,
                "docstring": None,
                "constraints": {},
            },
        ],
        "fk_fields": [],
        "backward_fk_fields": [],
        "o2o_fields": [],
        "backward_o2o_fields": [],
        "m2m_fields": [],
    }


def test_describe_model_json_native():
    val = JSONFields.describe(serializable=False)

    assert val == {
        "name": "models.JSONFields",
        "app": "models",
        "table": "jsonfields",
        "abstract": False,
        "description": "This model contains many JSON blobs",
        "docstring": "This model contains many JSON blobs",
        "unique_together": [],
        "indexes": [],
        "pk_field": {
            "name": "id",
            "field_type": fields.IntField,
            "db_column": "id",
            "db_field_types": {"": "INT"},
            "python_type": int,
            "generated": True,
            "nullable": False,
            "unique": True,
            "indexed": True,
            "default": None,
            "db_default": DB_DEFAULT_NOT_SET,
            "description": None,
            "docstring": None,
            "constraints": {"ge": -2147483648, "le": 2147483647},
        },
        "data_fields": [
            {
                "name": "data",
                "field_type": fields.JSONField,
                "db_column": "data",
                "db_field_types": {
                    "": "JSON",
                    "mssql": "NVARCHAR(MAX)",
                    "oracle": "NCLOB",
                    "postgres": "JSONB",
                },
                "python_type": dict | list,
                "generated": False,
                "nullable": False,
                "unique": False,
                "indexed": False,
                "default": None,
                "db_default": DB_DEFAULT_NOT_SET,
                "description": None,
                "docstring": None,
                "constraints": {},
            },
            {
                "name": "data_null",
                "field_type": fields.JSONField,
                "db_column": "data_null",
                "db_field_types": {
                    "": "JSON",
                    "mssql": "NVARCHAR(MAX)",
                    "oracle": "NCLOB",
                    "postgres": "JSONB",
                },
                "python_type": dict | list,
                "generated": False,
                "nullable": True,
                "unique": False,
                "indexed": False,
                "default": None,
                "db_default": DB_DEFAULT_NOT_SET,
                "description": None,
                "docstring": None,
                "constraints": {},
            },
            {
                "name": "data_default",
                "field_type": fields.JSONField,
                "db_column": "data_default",
                "db_field_types": {
                    "": "JSON",
                    "mssql": "NVARCHAR(MAX)",
                    "oracle": "NCLOB",
                    "postgres": "JSONB",
                },
                "python_type": dict | list,
                "generated": False,
                "nullable": False,
                "unique": False,
                "indexed": False,
                "default": {"a": 1},
                "db_default": DB_DEFAULT_NOT_SET,
                "description": None,
                "docstring": None,
                "constraints": {},
            },
            {
                "name": "data_validate",
                "field_type": fields.JSONField,
                "db_column": "data_validate",
                "db_field_types": {
                    "": "JSON",
                    "mssql": "NVARCHAR(MAX)",
                    "oracle": "NCLOB",
                    "postgres": "JSONB",
                },
                "python_type": dict | list,
                "generated": False,
                "nullable": True,
                "unique": False,
                "indexed": False,
                "default": None,
                "db_default": DB_DEFAULT_NOT_SET,
                "description": None,
                "docstring": None,
                "constraints": {},
            },
            {
                "name": "data_pydantic",
                "field_type": fields.JSONField,
                "db_column": "data_pydantic",
                "db_field_types": {
                    "": "JSON",
                    "mssql": "NVARCHAR(MAX)",
                    "oracle": "NCLOB",
                    "postgres": "JSONB",
                },
                "python_type": TestSchemaForJSONField,
                "generated": False,
                "nullable": False,
                "unique": False,
                "indexed": False,
                "default": json_pydantic_default,
                "db_default": DB_DEFAULT_NOT_SET,
                "description": None,
                "docstring": None,
                "constraints": {},
            },
        ],
        "fk_fields": [],
        "backward_fk_fields": [],
        "o2o_fields": [],
        "backward_o2o_fields": [],
        "m2m_fields": [],
    }


def test_describe_indexes_serializable():
    val = ModelWithIndexes.describe()

    assert val["indexes"] == [
        {"fields": ["f1", "f2"], "expressions": [], "name": None, "type": "", "extra": ""},
        {
            "fields": ["f3"],
            "expressions": [],
            "name": "model_with_indexes__f3",
            "type": "",
            "extra": "",
        },
    ]


def test_describe_indexes_not_serializable():
    val = ModelWithIndexes.describe(serializable=False)

    assert val["indexes"] == ModelWithIndexes._meta.indexes
