#!/usr/bin/env python3
from __future__ import annotations

import argparse
import os
from pathlib import Path

from sqlalchemy import create_engine, text

from config.base import Config
from config.control_plane import get_control_plane_engine
from config.db_router import build_tenant_db_name, sanitize_routing_key


BASE_DIR = Path(__file__).resolve().parents[1]
TENANT_FILES_ROOT = BASE_DIR / "files" / "tenants"


def _provision_database_url() -> str:
    if not Config.PROVISION_DB_USER:
        raise RuntimeError("Missing PROVISION_DB_USER")
    return (
        f"{Config.DB_ENGINE}://{Config.PROVISION_DB_USER}:{Config.PROVISION_DB_PASSWORD}"
        f"@{Config.PROVISION_DB_HOST}:{Config.PROVISION_DB_PORT}/mysql"
    )


def ensure_control_plane_tables() -> None:
    engine = get_control_plane_engine()
    with engine.begin() as conn:
        conn.execute(
            text(
                """
                CREATE TABLE IF NOT EXISTS tenant_domains (
                    id BIGINT AUTO_INCREMENT PRIMARY KEY,
                    tenant_id VARCHAR(64) NOT NULL,
                    routing_key VARCHAR(64) NOT NULL,
                    domain VARCHAR(255) NOT NULL,
                    is_primary BOOLEAN NOT NULL DEFAULT 0,
                    created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
                    updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
                    UNIQUE KEY uq_tenant_domains_domain (domain),
                    KEY ix_tenant_domains_routing_key (routing_key)
                )
                """
            )
        )
        conn.execute(
            text(
                """
                CREATE TABLE IF NOT EXISTS tenant_assignments (
                    id BIGINT AUTO_INCREMENT PRIMARY KEY,
                    tenant_id VARCHAR(64) NOT NULL,
                    routing_key VARCHAR(64) NOT NULL,
                    server_role VARCHAR(32) NOT NULL DEFAULT 'primary',
                    tenant_db_name VARCHAR(128) NOT NULL,
                    files_base_path VARCHAR(512) NOT NULL,
                    created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
                    updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
                    UNIQUE KEY uq_tenant_assignments_routing_server (routing_key, server_role)
                )
                """
            )
        )


def create_tenant_database(db_name: str) -> None:
    engine = create_engine(_provision_database_url(), future=True)
    with engine.begin() as conn:
        conn.execute(text(f"CREATE DATABASE IF NOT EXISTS `{db_name}`"))


def upsert_assignment(routing_key: str, tenant_id: str, tenant_db_name: str, files_base_path: str) -> None:
    engine = get_control_plane_engine()
    with engine.begin() as conn:
        conn.execute(
            text(
                """
                INSERT INTO tenant_assignments (
                    tenant_id, routing_key, server_role, tenant_db_name, files_base_path
                ) VALUES (
                    :tenant_id, :routing_key, 'primary', :tenant_db_name, :files_base_path
                )
                ON DUPLICATE KEY UPDATE
                    tenant_id = VALUES(tenant_id),
                    tenant_db_name = VALUES(tenant_db_name),
                    files_base_path = VALUES(files_base_path)
                """
            ),
            {
                "tenant_id": tenant_id,
                "routing_key": routing_key,
                "tenant_db_name": tenant_db_name,
                "files_base_path": files_base_path,
            },
        )


def upsert_domain(routing_key: str, tenant_id: str, domain: str, is_primary: bool) -> None:
    normalized = domain.strip().lower()
    if not normalized:
        return

    engine = get_control_plane_engine()
    with engine.begin() as conn:
        conn.execute(
            text(
                """
                INSERT INTO tenant_domains (
                    tenant_id, routing_key, domain, is_primary
                ) VALUES (
                    :tenant_id, :routing_key, :domain, :is_primary
                )
                ON DUPLICATE KEY UPDATE
                    tenant_id = VALUES(tenant_id),
                    routing_key = VALUES(routing_key),
                    is_primary = VALUES(is_primary)
                """
            ),
            {
                "tenant_id": tenant_id,
                "routing_key": routing_key,
                "domain": normalized,
                "is_primary": 1 if is_primary else 0,
            },
        )


def main() -> int:
    parser = argparse.ArgumentParser(description="Provision a tenant DB and control-plane assignment.")
    parser.add_argument("routing_key", help="Tenant routing key. Allowed: ^[a-z0-9_]+$")
    parser.add_argument("--domain", help="Primary domain or subdomain to map to the tenant.")
    parser.add_argument("--tenant-id", help="Optional explicit tenant_id. Defaults to routing_key.")
    args = parser.parse_args()

    routing_key = sanitize_routing_key(args.routing_key)
    if routing_key is None:
        raise RuntimeError("routing_key is required")

    tenant_id = (args.tenant_id or routing_key).strip().lower()
    tenant_db_name = build_tenant_db_name(routing_key)
    files_base_path = str((TENANT_FILES_ROOT / routing_key).resolve())

    TENANT_FILES_ROOT.mkdir(parents=True, exist_ok=True)
    os.makedirs(files_base_path, exist_ok=True)

    ensure_control_plane_tables()
    create_tenant_database(tenant_db_name)
    upsert_assignment(routing_key, tenant_id, tenant_db_name, files_base_path)
    if args.domain:
        upsert_domain(routing_key, tenant_id, args.domain, is_primary=True)

    print(f"routing_key={routing_key}")
    print(f"tenant_id={tenant_id}")
    print(f"tenant_db_name={tenant_db_name}")
    print(f"files_base_path={files_base_path}")
    if args.domain:
        print(f"domain={args.domain.strip().lower()}")
    return 0


if __name__ == "__main__":
    raise SystemExit(main())
