import glob
import os

from core.database.db_context import DBContext
from core.logger import Logger
from data.migration import Migration
from data.schemas.system.executed_migration import ExecutedMigration
from data.schemas.system.executed_migration_dao import executedMigrationDao

logger = Logger(__name__)


class MigrationService:
    def __init__(
        self,
        db: DBContext,
    ):
        self._db = db

    async def _get_migration_history(self) -> list[ExecutedMigration]:
        results = await self._db.select(
            f"SELECT * FROM {executedMigrationDao.table_name}"
        )
        applied_migrations = []
        for result in results:
            applied_migrations.append(ExecutedMigration(result[0]))

        return applied_migrations

    @staticmethod
    def _load_scripts() -> list[Migration]:
        migrations = []
        path = os.path.join(os.path.dirname(__file__), "../", "scripts").replace(
            "\\", "/"
        )

        if not os.path.exists(path):
            raise Exception("Migration path not found")

        files = sorted(glob.glob(f"{path}/*"))

        for file in files:
            if not file.endswith(".sql"):
                continue

            name = str(file.split(".sql")[0])
            if "/" in name:
                name = name.split("/")[-1]

            with open(f"{file}", "r") as f:
                script = f.read()
                f.close()

            migrations.append(Migration(name, script))

        return migrations

    async def _execute(self, migrations: list[Migration]):
        result = await self._db.select(
            f"""
                    SELECT tablename
                    FROM pg_tables
                    WHERE schemaname = 'system' AND tablename = '{executedMigrationDao.table_name.split('.')[-1].lower()}';
                """
        )

        for migration in migrations:
            active_statement = ""
            try:
                # check if table exists
                if len(result) > 0:
                    migration_from_db = await executedMigrationDao.find_by_id(
                        migration.name
                    )
                    if migration_from_db is not None:
                        continue

                logger.debug(f"Running upgrade migration: {migration.name}")

                await self._db.execute(migration.script, multi=False)

                await executedMigrationDao.create(
                    ExecutedMigration(migration.name), skip_editor=True
                )
            except Exception as e:
                logger.fatal(
                    f"Migration failed: {migration.name}\n{active_statement}",
                    e,
                )

    async def migrate(self):
        await self._execute(self._load_scripts())
