import 'dotenv/config';
import { randomUUID } from 'crypto';
import mysql from 'mysql2/promise';
import {
  ALL_PERMISSION_KEYS,
  getDefaultPermissionMap,
  SYSTEM_ROLE_TEMPLATES,
  type PermissionValue,
} from '../src/modules/access-control/permission-catalog';

async function persistRolePermissions(
  connection: mysql.Connection,
  roleId: string,
  permissionMap: Record<string, PermissionValue>,
) {
  await connection.query('DELETE FROM role_permissions WHERE role_id = ?', [
    roleId,
  ]);

  const rows = Object.entries(permissionMap).filter(([permission_key]) =>
    ALL_PERMISSION_KEYS.includes(permission_key),
  );

  if (rows.length === 0) {
    return;
  }

  const values: unknown[] = [];
  const placeholders = rows
    .map(([permission_key, permission_value]) => {
      values.push(
        randomUUID(),
        roleId,
        permission_key,
        permission_value,
      );
      return '(?, ?, ?, ?, CURRENT_TIMESTAMP(3), CURRENT_TIMESTAMP(3))';
    })
    .join(', ');

  await connection.query(
    `INSERT INTO role_permissions (id, role_id, permission_key, permission_value, created_at, updated_at)
     VALUES ${placeholders}`,
    values,
  );
}

async function ensureOwnerPermissionKeysExist(
  connection: mysql.Connection,
  ownerRoleId: string,
) {
  const [existingRows] = await connection.query<mysql.RowDataPacket[]>(
    'SELECT permission_key FROM role_permissions WHERE role_id = ?',
    [ownerRoleId],
  );

  const existingKeys = new Set(
    existingRows.map((row) => String(row.permission_key)),
  );
  const defaults = getDefaultPermissionMap('owner');
  const missing = Object.entries(defaults).filter(
    ([permission_key]) =>
      ALL_PERMISSION_KEYS.includes(permission_key) &&
      !existingKeys.has(permission_key),
  );

  if (missing.length === 0) {
    return;
  }

  const values: unknown[] = [];
  const placeholders = missing
    .map(([permission_key, permission_value]) => {
      values.push(
        randomUUID(),
        ownerRoleId,
        permission_key,
        permission_value,
      );
      return '(?, ?, ?, ?, CURRENT_TIMESTAMP(3), CURRENT_TIMESTAMP(3))';
    })
    .join(', ');

  await connection.query(
    `INSERT INTO role_permissions (id, role_id, permission_key, permission_value, created_at, updated_at)
     VALUES ${placeholders}`,
    values,
  );
}

async function ensureDefaultRolesForOwner(
  connection: mysql.Connection,
  ownerId: string,
) {
  const [existingRows] = await connection.query<mysql.RowDataPacket[]>(
    'SELECT id, slug FROM access_roles WHERE owner_id = ?',
    [ownerId],
  );

  if (existingRows.length > 0) {
    const ownerRole = existingRows.find((row) => row.slug === 'owner');
    if (ownerRole) {
      await ensureOwnerPermissionKeysExist(connection, ownerRole.id);
    }
    console.log(
      `Owner ${ownerId}: roles already exist (${existingRows.length}), ensured missing owner permission keys`,
    );
    return;
  }

  for (const template of SYSTEM_ROLE_TEMPLATES) {
    const roleId = randomUUID();
    await connection.query(
      `INSERT INTO access_roles
        (id, owner_id, name, slug, description, is_system, hierarchy, created_at, updated_at)
       VALUES (?, ?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP(3), CURRENT_TIMESTAMP(3))`,
      [
        roleId,
        ownerId,
        template.name,
        template.slug,
        template.description,
        template.is_system ? 1 : 0,
        template.hierarchy,
      ],
    );

    await persistRolePermissions(
      connection,
      roleId,
      getDefaultPermissionMap(template.slug),
    );

    console.log(`Owner ${ownerId}: created role ${template.slug}`);
  }
}

async function main() {
  const databaseUrl = process.env.DATABASE_URL;
  let connection: mysql.Connection;

  if (databaseUrl) {
    connection = await mysql.createConnection(databaseUrl);
  } else {
    connection = await mysql.createConnection({
      host: process.env.DB_HOST || 'localhost',
      port: parseInt(process.env.DB_PORT || '') || 3306,
      user: process.env.DB_USERNAME || 'root',
      password: process.env.DB_PASSWORD || '',
      database: process.env.DB_DATABASE || 'tracker',
    });
  }

  try {
    const [owners] = await connection.query<mysql.RowDataPacket[]>(
      'SELECT id FROM owner',
    );

    if (!owners.length) {
      throw new Error('No rows found in owner table');
    }

    for (const owner of owners) {
      await ensureDefaultRolesForOwner(connection, owner.id);
    }

    const [roleCount] = await connection.query<mysql.RowDataPacket[]>(
      'SELECT COUNT(*) AS cnt FROM access_roles',
    );
    const [permCount] = await connection.query<mysql.RowDataPacket[]>(
      'SELECT COUNT(*) AS cnt FROM role_permissions',
    );

    console.log(
      `Done. access_roles=${roleCount[0]?.cnt ?? 0}, role_permissions=${permCount[0]?.cnt ?? 0}`,
    );
  } finally {
    await connection.end();
  }
}

main().catch((error) => {
  console.error(error);
  process.exit(1);
});
