"""Verify a DMS data export by importing it into an isolated scratch database."""

from __future__ import annotations

import argparse
import os
from pathlib import Path

from alembic import command
from alembic.config import Config
from dotenv import dotenv_values
from pymysql.constants import CLIENT
from sqlalchemy import create_engine, text
from sqlalchemy.engine import make_url


TABLES = {
    "sys_organization": 3,
    "sys_user": 3,
    "doc_category": 21,
    "doc_document": 31,
    "doc_attachment_binding": 19,
    "doc_permission": 7,
    "sys_audit_log": 291,
}


def execute_script(database_url: str, path: Path) -> None:
    engine = create_engine(
        database_url,
        connect_args={"client_flag": CLIENT.MULTI_STATEMENTS},
    )
    connection = engine.raw_connection()
    try:
        driver_connection = connection.driver_connection
        driver_connection.autocommit(True)
        with driver_connection.cursor() as cursor:
            cursor.execute(path.read_text(encoding="utf-8"))
            while cursor.nextset():
                pass
    finally:
        connection.close()
        engine.dispose()


def main() -> None:
    parser = argparse.ArgumentParser()
    parser.add_argument("--env-file", type=Path, required=True)
    parser.add_argument("--schema", type=Path, required=True)
    parser.add_argument("--data", type=Path, required=True)
    parser.add_argument("--backend", type=Path, required=True)
    parser.add_argument("--scratch-database", required=True)
    args = parser.parse_args()

    if not args.scratch_database.startswith("dms_full_demo_verify_"):
        raise RuntimeError("Scratch database name must start with dms_full_demo_verify_")

    source_url = make_url(dotenv_values(args.env_file).get("DMS_DATABASE_URL") or "")
    server_url = source_url.set(database=None)
    scratch_url = source_url.set(database=args.scratch_database)
    server_engine = create_engine(server_url)
    scratch_created = False
    try:
        with server_engine.connect().execution_options(isolation_level="AUTOCOMMIT") as connection:
            exists = connection.execute(
                text("SELECT SCHEMA_NAME FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME=:name"),
                {"name": args.scratch_database},
            ).first()
            if exists:
                raise RuntimeError("Scratch database already exists; refusing to overwrite it")
            connection.execute(text(f"CREATE DATABASE `{args.scratch_database}` CHARACTER SET utf8mb4"))
            scratch_created = True

        scratch_url_text = scratch_url.render_as_string(hide_password=False)
        execute_script(scratch_url_text, args.schema.resolve())
        previous_directory = Path.cwd()
        try:
            os.chdir(args.backend.resolve())
            os.environ["DMS_DATABASE_URL"] = scratch_url_text
            os.environ["DMS_JWT_SECRET"] = "verification-only-secret-not-for-deployment"
            command.upgrade(Config("migrations/alembic.ini"), "head")
        finally:
            os.chdir(previous_directory)
        execute_script(scratch_url_text, args.data.resolve())

        scratch_engine = create_engine(scratch_url)
        try:
            with scratch_engine.connect() as connection:
                for table_name, expected in TABLES.items():
                    actual = connection.execute(text(f"SELECT COUNT(*) FROM `{table_name}`")).scalar_one()
                    if actual != expected:
                        raise RuntimeError(f"{table_name}: expected {expected}, got {actual}")
                    print(f"{table_name}={actual}")
        finally:
            scratch_engine.dispose()
        print("scratch import verification passed")
    finally:
        if scratch_created:
            with server_engine.connect().execution_options(isolation_level="AUTOCOMMIT") as connection:
                connection.execute(text(f"DROP DATABASE `{args.scratch_database}`"))
            print("scratch database removed")
        server_engine.dispose()


if __name__ == "__main__":
    main()
