from datetime import datetime
from typing import List, Optional, Union

import databases
import pydantic
import pytest
import sqlalchemy

import ormar as orm

from tests.settings import DATABASE_URL

database = databases.Database(DATABASE_URL, force_rollback=True)
metadata = sqlalchemy.MetaData()


class MainMeta(orm.ModelMeta):
    database = database
    metadata = metadata


class ChagenlogRelease(orm.Model):
    id: int = orm.Integer(name="id", primary_key=True)

    class Meta(MainMeta):
        tablename = "changelog_release"


class CommitIssue(orm.Model):
    id: int = orm.Integer(name="id", primary_key=True)

    class Meta(MainMeta):
        tablename = "commit_issues"


class CommitLabel(orm.Model):
    id: int = orm.Integer(name="id", primary_key=True)

    class Meta(MainMeta):
        tablename = "commit_label"


class MergeRequestCommit(orm.Model):
    id: int = orm.Integer(name="id", primary_key=True)

    class Meta(MainMeta):
        tablename = "merge_request_commits"


class MergeRequestIssue(orm.Model):
    id: int = orm.Integer(name="id", primary_key=True)

    class Meta(MainMeta):
        tablename = "merge_request_issues"


class MergeRequestLabel(orm.Model):
    id: int = orm.Integer(name="id", primary_key=True)

    class Meta(MainMeta):
        tablename = "merge_request_labels"


class ProjectLabel(orm.Model):
    id: int = orm.Integer(name="id", primary_key=True)

    class Meta(MainMeta):
        tablename = "project_label"


class PushCommit(orm.Model):
    id: int = orm.Integer(name="id", primary_key=True)

    class Meta(MainMeta):
        tablename = "push_commit"


class PushLabel(orm.Model):
    id: int = orm.Integer(name="id", primary_key=True)

    class Meta(MainMeta):
        tablename = "push_label"


class TagCommit(orm.Model):
    id: int = orm.Integer(name="id", primary_key=True)

    class Meta(MainMeta):
        tablename = "tag_commits"


class TagIssue(orm.Model):
    id: int = orm.Integer(name="id", primary_key=True)

    class Meta(MainMeta):
        tablename = "tag_issue"


class TagLabel(orm.Model):
    id: int = orm.Integer(name="id", primary_key=True)

    class Meta(MainMeta):
        tablename = "tag_label"


class UserProject(orm.Model):
    id: int = orm.Integer(name="id", primary_key=True)
    access_level: int = orm.Integer(default=0)

    class Meta(MainMeta):
        tablename = "user_project"


class Label(orm.Model):
    id: int = orm.Integer(name="id", primary_key=True)
    title: str = orm.String(max_length=100)
    description: str = orm.Text(default="")
    type: str = orm.String(max_length=100, default="")

    class Meta(MainMeta):
        tablename = "labels"


class Project(orm.Model):
    id: int = orm.Integer(name="id", primary_key=True)
    name: str = orm.String(max_length=100)
    description: str = orm.Text(default="")
    git_url: str = orm.String(max_length=500, default="")
    labels: Optional[Union[List[Label], Label]] = orm.ManyToMany(
        Label, through=ProjectLabel, ondelete="CASCADE", onupdate="CASCADE"
    )
    changelog_jira_tag: str = orm.String(max_length=100, default="")
    change_type_jira_tag: str = orm.String(max_length=100, default="")
    jira_prefix: str = orm.String(max_length=10, default="SAN")
    type: str = orm.String(max_length=10, default="cs")
    target_branch_name: str = orm.String(max_length=100, default="master")
    header: str = orm.String(max_length=250, default="")
    jira_url: str = orm.String(max_length=500)
    changelog_file: str = orm.String(max_length=250, default="")
    version_file: str = orm.String(max_length=250, default="")

    class Meta(MainMeta):
        tablename = "projects"


class Issue(orm.Model):
    id: int = orm.Integer(name="id", primary_key=True)
    summary: str = orm.Text(default="")
    description: str = orm.Text(default="")
    changelog: str = orm.Text(default="")
    link: str = orm.String(max_length=500)
    issue_type: str = orm.String(max_length=100)
    key: str = orm.String(max_length=100)
    change_type: str = orm.String(max_length=100, default="")
    data: pydantic.Json = orm.JSON(default={})

    class Meta(MainMeta):
        tablename = "issues"


class User(orm.Model):
    id: int = orm.Integer(name="id", primary_key=True)
    username: str = orm.String(max_length=100, unique=True)
    name: str = orm.String(max_length=200, default="")

    class Meta(MainMeta):
        tablename = "users"


class Branch(orm.Model):
    id: int = orm.Integer(name="id", primary_key=True)
    name: str = orm.String(max_length=200)
    description: str = orm.Text(default="")
    automatic_tags: bool = orm.Boolean(default=False)
    is_it_locked: bool = orm.Boolean(default=True)
    prefix_tag: str = orm.String(max_length=50, default="")
    postfix_tag: str = orm.String(max_length=50, default="")
    project: Project = orm.ForeignKey(Project, ondelete="CASCADE", onupdate="CASCADE")

    class Meta(MainMeta):
        tablename = "branches"


class Changelog(orm.Model):
    id: int = orm.Integer(name="id", primary_key=True)
    content: str = orm.Text(default="")
    version: str = orm.Text(default="")
    past_changelog: int = orm.Integer(default=0)
    label: Label = orm.ForeignKey(
        Label, nullable=True, ondelete="CASCADE", onupdate="CASCADE"
    )
    project: Project = orm.ForeignKey(Project, ondelete="CASCADE", onupdate="CASCADE")
    created_date: datetime = orm.DateTime(default=datetime.utcnow())

    class Meta(MainMeta):
        tablename = "changelogs"


class Commit(orm.Model):
    id: str = orm.String(max_length=500, primary_key=True)
    short_id: str = orm.String(max_length=500)
    title: str = orm.String(max_length=500)
    message: str = orm.Text(default="")
    url = orm.String(max_length=500, default="")
    author_name = orm.String(max_length=500, default="")
    labels: Optional[Union[List[Label], Label]] = orm.ManyToMany(
        Label, through=CommitLabel, ondelete="CASCADE", onupdate="CASCADE"
    )
    issues: Optional[Union[List[Issue], Issue]] = orm.ManyToMany(
        Issue, through=CommitIssue, ondelete="CASCADE", onupdate="CASCADE"
    )

    class Meta(MainMeta):
        tablename = "commits"


class MergeRequest(orm.Model):
    id: int = orm.Integer(name="id", primary_key=True)
    idd: int = orm.Integer(default=0)
    title: str = orm.String(max_length=500)
    state: str = orm.String(max_length=100)
    merge_status: str = orm.String(max_length=100)
    description: str = orm.Text(default="")
    source: Branch = orm.ForeignKey(Branch, related_name="source")
    target: Branch = orm.ForeignKey(Branch, related_name="target")
    labels: Optional[Union[List[Label], Label]] = orm.ManyToMany(
        Label, through=MergeRequestLabel, ondelete="CASCADE", onupdate="CASCADE"
    )
    commits: Optional[Union[List[Commit], Commit]] = orm.ManyToMany(
        Commit, through=MergeRequestCommit, ondelete="CASCADE", onupdate="CASCADE"
    )
    issues: Optional[Union[List[Issue], Issue]] = orm.ManyToMany(
        Issue, through=MergeRequestIssue, ondelete="CASCADE", onupdate="CASCADE"
    )
    project: Project = orm.ForeignKey(Project, ondelete="CASCADE", onupdate="CASCADE")

    class Meta(MainMeta):
        tablename = "merge_requests"


class Push(orm.Model):
    id: int = orm.Integer(name="id", primary_key=True)
    branch: Branch = orm.ForeignKey(
        Branch, nullable=True, ondelete="CASCADE", onupdate="CASCADE"
    )
    has_locking_changes: bool = orm.Boolean(default=False)
    sha: str = orm.String(max_length=200)
    labels: Optional[Union[List[Label], Label]] = orm.ManyToMany(
        Label, through=PushLabel, ondelete="CASCADE", onupdate="CASCADE"
    )
    commits: Optional[Union[List[Commit], Commit]] = orm.ManyToMany(
        Commit,
        through=PushCommit,
        through_relation_name="push",
        through_reverse_relation_name="commit_id",
        ondelete="CASCADE",
        onupdate="CASCADE",
    )
    author: User = orm.ForeignKey(User, ondelete="CASCADE", onupdate="CASCADE")
    project: Project = orm.ForeignKey(Project, ondelete="CASCADE", onupdate="CASCADE")

    class Meta(MainMeta):
        tablename = "pushes"


class Tag(orm.Model):
    id: int = orm.Integer(name="id", primary_key=True)
    name: str = orm.String(max_length=200)
    ref: str = orm.String(max_length=200)
    project: Project = orm.ForeignKey(Project, ondelete="CASCADE", onupdate="CASCADE")
    title: str = orm.String(max_length=200, default="")
    description: str = orm.Text(default="")
    commits: Optional[Union[List[Commit], Commit]] = orm.ManyToMany(
        Commit,
        through=TagCommit,
        through_relation_name="tag",
        through_reverse_relation_name="commit_id",
        ondelete="CASCADE",
        onupdate="CASCADE",
    )
    issues: Optional[Union[List[Issue], Issue]] = orm.ManyToMany(
        Issue, through=TagIssue, ondelete="CASCADE", onupdate="CASCADE"
    )
    labels: Optional[Union[List[Label], Label]] = orm.ManyToMany(
        Label, through=TagLabel, ondelete="CASCADE", onupdate="CASCADE"
    )
    user: User = orm.ForeignKey(
        User, nullable=True, ondelete="CASCADE", onupdate="CASCADE"
    )
    branch: Branch = orm.ForeignKey(
        Branch, nullable=True, ondelete="CASCADE", onupdate="CASCADE"
    )

    class Meta(MainMeta):
        tablename = "tags"


class Release(orm.Model):
    id: int = orm.Integer(name="id", primary_key=True)
    title: str = orm.String(max_length=200, default="")
    description: str = orm.Text(default="")
    tag: Tag = orm.ForeignKey(Tag, ondelete="CASCADE", onupdate="CASCADE")
    changelogs: List[Changelog] = orm.ManyToMany(
        Changelog, through=ChagenlogRelease, ondelete="CASCADE", onupdate="CASCADE"
    )
    data: pydantic.Json = orm.JSON(default={})

    class Meta(MainMeta):
        tablename = "releases"


class Webhook(orm.Model):
    id: int = orm.Integer(name="id", primary_key=True)
    object_kind = orm.String(max_length=100)
    project: Project = orm.ForeignKey(Project, ondelete="CASCADE", onupdate="CASCADE")
    merge_request: MergeRequest = orm.ForeignKey(
        MergeRequest, nullable=True, ondelete="CASCADE", onupdate="CASCADE"
    )
    tag: Tag = orm.ForeignKey(
        Tag, nullable=True, ondelete="CASCADE", onupdate="CASCADE"
    )
    push: Push = orm.ForeignKey(
        Push, nullable=True, ondelete="CASCADE", onupdate="CASCADE"
    )
    created_at: datetime = orm.DateTime(default=datetime.now())
    data: pydantic.Json = orm.JSON(default={})
    status: int = orm.Integer(default=200)
    error: str = orm.Text(default="")


@pytest.fixture(autouse=True, scope="module")
def create_test_database():
    engine = sqlalchemy.create_engine(DATABASE_URL)
    metadata.drop_all(engine)
    metadata.create_all(engine)
    yield
    metadata.drop_all(engine)


@pytest.mark.asyncio
async def test_very_complex_relation_map():
    async with database:
        tags = [
            {"id": 18, "name": "name-18", "ref": "ref-18"},
            {"id": 17, "name": "name-17", "ref": "ref-17"},
            {"id": 12, "name": "name-12", "ref": "ref-12"},
        ]
        payload = [
            {
                "id": 9,
                "title": "prueba-2321",
                "description": "\n<!--- start changelog ver.v.1.3.0.0 -->\n### [v.1.3.0.0] - 2021-08-19\n#### Resolved Issues\n\n#### Task\n\n- Probar flujo de changelog Automatic  Jira: [SAN-86](https://htech.atlassian.net/browse/SAN-86)\n\n    Description: Se probara el flujo de changelog automatic.  \n\n    Changelog: Se agrega función para extraer texto del campo changelog del dashboard de Sanval y ponerlo directamente en el changelog.md del repositorio. \n\n\n            \n<!--- end changelog ver.v.1.3.0.0 -->\n",
                "data": {},
            },
            {
                "id": 8,
                "title": "prueba-123-prod",
                "description": "\n<!--- start changelog ver.v.1.2.0.0 -->\n### [v.1.2.0.0] - 2021-08-19\n#### Resolved Issues\n\n#### Task\n\n- Probar flujo de changelog Automatic  Jira: [SAN-86](https://htech.atlassian.net/browse/SAN-86)\n\n    Description: Se probara el flujo de changelog automatic.  \n\n    Changelog: Se agrega función para extraer texto del campo changelog del dashboard de Sanval y ponerlo directamente en el changelog.md del repositorio. \n\n\n            \n<!--- end changelog ver.v.1.2.0.0 -->\n",
                "data": {},
            },
            {
                "id": 6,
                "title": "prueba-3-2",
                "description": "\n<!--- start changelog ver.v.1.1.0.0 -->\n### [v.1.1.0.0] - 2021-07-29\n#### Resolved Issues\n\n#### Task\n\n- Probar flujo de changelog Automatic  Jira: [SAN-86](https://htech.atlassian.net/browse/SAN-86)\n\n    Description: Se probara el flujo de changelog automatic.  \n\n    Changelog: Se agrega función para extraer texto del campo changelog del dashboard de Sanval y ponerlo directamente en el changelog.md del repositorio. \n\n\n            \n<!--- end changelog ver.v.1.1.0.0 -->\n",
                "data": {},
            },
        ]
        saved_tags = []
        for tag in tags:
            saved_tags.append(await Tag(**tag).save())

        for ind, pay in enumerate(payload):
            await Release(**pay, tag=saved_tags[ind]).save()

        releases = await Release.objects.order_by(Release.id.desc()).all()
        dicts = [release.dict() for release in releases]

        result = [
            {
                "id": 9,
                "title": "prueba-2321",
                "description": "\n<!--- start changelog ver.v.1.3.0.0 -->\n### [v.1.3.0.0] - 2021-08-19\n#### Resolved Issues\n\n#### Task\n\n- Probar flujo de changelog Automatic  Jira: [SAN-86](https://htech.atlassian.net/browse/SAN-86)\n\n    Description: Se probara el flujo de changelog automatic.  \n\n    Changelog: Se agrega función para extraer texto del campo changelog del dashboard de Sanval y ponerlo directamente en el changelog.md del repositorio. \n\n\n            \n<!--- end changelog ver.v.1.3.0.0 -->\n",
                "data": {},
                "tag": {
                    "id": 18,
                    "taglabel": None,
                    "tagcommit": None,
                    "tagissue": None,
                },
                "changelogs": [],
            },
            {
                "id": 8,
                "title": "prueba-123-prod",
                "description": "\n<!--- start changelog ver.v.1.2.0.0 -->\n### [v.1.2.0.0] - 2021-08-19\n#### Resolved Issues\n\n#### Task\n\n- Probar flujo de changelog Automatic  Jira: [SAN-86](https://htech.atlassian.net/browse/SAN-86)\n\n    Description: Se probara el flujo de changelog automatic.  \n\n    Changelog: Se agrega función para extraer texto del campo changelog del dashboard de Sanval y ponerlo directamente en el changelog.md del repositorio. \n\n\n            \n<!--- end changelog ver.v.1.2.0.0 -->\n",
                "data": {},
                "tag": {
                    "id": 17,
                    "taglabel": None,
                    "tagcommit": None,
                    "tagissue": None,
                },
                "changelogs": [],
            },
            {
                "id": 6,
                "title": "prueba-3-2",
                "description": "\n<!--- start changelog ver.v.1.1.0.0 -->\n### [v.1.1.0.0] - 2021-07-29\n#### Resolved Issues\n\n#### Task\n\n- Probar flujo de changelog Automatic  Jira: [SAN-86](https://htech.atlassian.net/browse/SAN-86)\n\n    Description: Se probara el flujo de changelog automatic.  \n\n    Changelog: Se agrega función para extraer texto del campo changelog del dashboard de Sanval y ponerlo directamente en el changelog.md del repositorio. \n\n\n            \n<!--- end changelog ver.v.1.1.0.0 -->\n",
                "data": {},
                "tag": {
                    "id": 12,
                    "taglabel": None,
                    "tagcommit": None,
                    "tagissue": None,
                },
                "changelogs": [],
            },
        ]

        assert dicts == result
