"""
    We need to use alembic to host multiple projects in one directory
"""
import os
import shutil
from configparser import SafeConfigParser
from alembic.config import Config
from invoke import task
from bluemax.tasks.utils import confirm_action, Colored


def get_db_url(name):
    """ get the db_url from alembic.ini """
    alembic_cfg = Config("alembic.ini")
    return alembic_cfg.get_section_option(name, "sqlalchemy.url")


def confirm_db_action(action, name):
    """db are you sure? """
    db_url = get_db_url(name)
    return confirm_action(f"{action} {db_url}")


def _run_with_pythonpath(ctx, cmd):
    """ extend the python path environment variable with cwd """
    path = os.getenv("PYTHONPATH", "")
    env = {"PYTHONPATH": ".:" + path}
    return ctx.run(cmd, env=env)


def revise_ini(adding=None, removing=None):
    """ add or remove from ini file """
    parser = SafeConfigParser()
    with open("alembic.ini", "r") as configfile:
        parser.read_file(configfile)

    if parser.has_section("alembic"):
        del parser["alembic"]
    if adding:
        parser.add_section(adding)
        parser.set(adding, "script_location", f"database/{adding}")
        parser.set(adding, "sqlalchemy.url", f"sqlite:///{adding}.sqlite3")
    elif parser.has_section(removing):
        del parser[removing]

    with open("alembic.ini", "w") as configfile:
        parser.write(configfile)


@task(help={"project": "name of the project to add a model package to"})
def create_model(_, project):
    """ creates a sqlalchemy model package inside project """
    model_path = os.path.join(project, "model")
    if os.path.isdir(model_path):
        print("model exists")
        return
    os.makedirs(model_path)
    if not os.path.isfile(os.path.join(project, "__init__.py")):
        open(os.path.join(project, "__init__.py"), "w")
    open(os.path.join(model_path, "__init__.py"), "w")
    with open(os.path.join(model_path, "base.py"), "w") as file:
        file.write(
            '''""" Used by alembic """
from sqlalchemy.ext.declarative import declarative_base, declared_attr


class Base:
    """ Base for orm model classes """

    @declared_attr
    def __tablename__(cls):
        """ with this tablename is class name lower"""
        return cls.__name__.lower()


Base = declarative_base(cls=Base)
'''
        )


@task(
    help={
        "name": "name of the database",
        "project": "where model is created, defaults to name of database",
    }
)
def create(ctx, name, project=None):
    """ creates an alembic managed db """
    if project is None:
        project = name
    if not os.path.isdir("database"):
        os.makedirs("database")

    ctx.run(f"alembic init database/{name}")
    revise_ini(name)

    with open(os.path.join("database", name, "env.py"), "r") as file:
        contents = file.read()
        contents = contents.split("target_metadata = None\n")

    with open(os.path.join("database", name, "env.py"), "w") as file:
        file.write(contents[0])
        file.write("\n")
        file.write(f"from {project}.model.base import Base\n")
        file.write("target_metadata = Base.metadata\n")
        file.write(contents[1])

    create_model(ctx, project)


@task(help={"name": "name of the database"})
def remove(_, name):
    """ removes a database of name from the database folder """
    db_path = os.path.abspath(os.path.join("database", name))
    if os.path.isdir(db_path):
        shutil.rmtree(db_path)
        print(f"removed {db_path}")
    revise_ini(removing=name)


@task(help={"name": "name of the database", "message": "revision title"})
def revise(ctx, name, message):
    """ creates an autogenerated revision in alembic """
    _run_with_pythonpath(
        ctx, f'alembic -n {name} revision --autogenerate -m "{message}"'
    )


@task(
    help={"name": "name of the database", "revision": "almebic revision to upgrade to"}
)
def upgrade(ctx, name, revision="head"):
    """ upgrades with alembic to revision """
    if confirm_db_action(Colored.green("UPGRADE"), name):
        _run_with_pythonpath(ctx, f"alembic -n {name} upgrade {revision}")


@task(
    help={
        "name": "name of the database",
        "revision": "almebic revision to downgrade to",
    }
)
def downgrade(ctx, name, revision="base"):
    """ downgrades to revsion with alembic - base is nothing!"""
    if confirm_db_action(Colored.red("DOWNGRADE"), name):
        _run_with_pythonpath(ctx, f"alembic -n {name} downgrade {revision}")
