import { query, tableExists } from '@zen/core/database';
import { generateId } from './password.js';
import { done, warn } from '@zen/core/shared/logger';
import { PERMISSION_DEFINITIONS } from './constants.js';
import { registerPermissions, getRegisteredPermissions } from './permissions-registry.js';

const USER_ROLE_PERMISSIONS = [];

const ROLE_TABLES = [
  {
    name: 'zen_auth_roles',
    sql: `
      CREATE TABLE zen_auth_roles (
        id text NOT NULL PRIMARY KEY,
        name text NOT NULL UNIQUE,
        description text,
        color text DEFAULT '#6b7280',
        is_system boolean NOT NULL DEFAULT false,
        created_at timestamptz NOT NULL DEFAULT CURRENT_TIMESTAMP,
        updated_at timestamptz NOT NULL DEFAULT CURRENT_TIMESTAMP
      )
    `
  },
  {
    name: 'zen_auth_permissions',
    sql: `
      CREATE TABLE zen_auth_permissions (
        key text NOT NULL PRIMARY KEY,
        name text NOT NULL,
        description text,
        group_name text NOT NULL
      )
    `
  },
  {
    name: 'zen_auth_role_permissions',
    sql: `
      CREATE TABLE zen_auth_role_permissions (
        role_id text NOT NULL REFERENCES zen_auth_roles(id) ON DELETE CASCADE,
        permission_key text NOT NULL REFERENCES zen_auth_permissions(key) ON DELETE CASCADE,
        PRIMARY KEY (role_id, permission_key)
      )
    `
  },
  {
    name: 'zen_auth_user_roles',
    sql: `
      CREATE TABLE zen_auth_user_roles (
        user_id text NOT NULL REFERENCES zen_auth_users(id) ON DELETE CASCADE,
        role_id text NOT NULL REFERENCES zen_auth_roles(id) ON DELETE CASCADE,
        PRIMARY KEY (user_id, role_id)
      )
    `
  }
];

async function dropRoleCheckConstraint() {
  await query(`
    DO $$ DECLARE cname text;
    BEGIN
      SELECT conname INTO cname FROM pg_constraint
      WHERE conrelid = 'zen_auth_users'::regclass AND contype = 'c' AND conname LIKE '%role%';
      IF cname IS NOT NULL THEN
        EXECUTE 'ALTER TABLE zen_auth_users DROP CONSTRAINT ' || quote_ident(cname);
      END IF;
    END$$;
  `);
}

async function migratePermissions() {
  // Migrate users.edit / users.delete → users.manage
  await query(`
    INSERT INTO zen_auth_role_permissions (role_id, permission_key)
      SELECT DISTINCT role_id, 'users.manage'
      FROM zen_auth_role_permissions
      WHERE permission_key IN ('users.edit', 'users.delete')
      AND EXISTS (SELECT 1 FROM zen_auth_permissions WHERE key = 'users.manage')
    ON CONFLICT DO NOTHING
  `);
  await query(`DELETE FROM zen_auth_role_permissions WHERE permission_key IN ('users.edit', 'users.delete')`);
  await query(`DELETE FROM zen_auth_permissions WHERE key IN ('users.edit', 'users.delete')`);
}

async function seedDefaultRolesAndPermissions() {
  // S'assure que les permissions core sont dans le registre, puis seed depuis
  // le registre — qui contient core + permissions enregistrées par les modules.
  registerPermissions(PERMISSION_DEFINITIONS);
  const allPermissions = getRegisteredPermissions();

  for (const perm of allPermissions) {
    await query(
      `INSERT INTO zen_auth_permissions (key, name, description, group_name)
       VALUES ($1, $2, $3, $4)
       ON CONFLICT (key) DO UPDATE SET name = EXCLUDED.name, description = EXCLUDED.description, group_name = EXCLUDED.group_name`,
      [perm.key, perm.name, perm.description, perm.group_name]
    );
  }

  await migratePermissions();

  // Admin role
  const adminRoleId = generateId();
  await query(
    `INSERT INTO zen_auth_roles (id, name, description, color, is_system) VALUES ($1, $2, $3, $4, $5) ON CONFLICT (name) DO NOTHING`,
    [adminRoleId, 'admin', 'Accès complet à toutes les fonctionnalités', '#ef4444', true]
  );
  const adminRole = await query(`SELECT id FROM zen_auth_roles WHERE name = 'admin'`);
  const adminId = adminRole.rows[0].id;

  // Toute permission présente dans le catalogue est attribuée au rôle admin —
  // y compris les permissions ajoutées par les modules après le premier init.
  await query(
    `INSERT INTO zen_auth_role_permissions (role_id, permission_key)
     SELECT $1, key FROM zen_auth_permissions
     ON CONFLICT DO NOTHING`,
    [adminId]
  );

  // User role
  const userRoleId = generateId();
  await query(
    `INSERT INTO zen_auth_roles (id, name, description, color, is_system) VALUES ($1, $2, $3, $4, $5) ON CONFLICT (name) DO NOTHING`,
    [userRoleId, 'user', 'Accès de base en lecture', '#6b7280', true]
  );
  const userRole = await query(`SELECT id FROM zen_auth_roles WHERE name = 'user'`);
  const userId = userRole.rows[0].id;

  for (const permKey of USER_ROLE_PERMISSIONS) {
    await query(
      `INSERT INTO zen_auth_role_permissions (role_id, permission_key) VALUES ($1, $2) ON CONFLICT DO NOTHING`,
      [userId, permKey]
    );
  }

  // Assign admin role to the first user if no user-roles exist yet
  const userRolesCount = await query(`SELECT COUNT(*) FROM zen_auth_user_roles`);
  if (parseInt(userRolesCount.rows[0].count) === 0) {
    const firstUser = await query(`SELECT id FROM zen_auth_users ORDER BY created_at ASC LIMIT 1`);
    if (firstUser.rows.length > 0) {
      await query(
        `INSERT INTO zen_auth_user_roles (user_id, role_id) VALUES ($1, $2) ON CONFLICT DO NOTHING`,
        [firstUser.rows[0].id, adminId]
      );
    }
  }
}

export async function createTables() {
  const created = [];
  const skipped = [];

  // Drop legacy CHECK constraint on zen_auth_users.role if it exists
  await dropRoleCheckConstraint();

  for (const table of ROLE_TABLES) {
    const exists = await tableExists(table.name);
    if (!exists) {
      await query(table.sql);
      created.push(table.name);
      done(`Created table: ${table.name}`);
    } else {
      skipped.push(table.name);
    }
  }

  await seedDefaultRolesAndPermissions();

  return { created, skipped };
}

export async function dropTables() {
  const dropOrder = [...ROLE_TABLES].reverse().map(t => t.name);
  warn('Dropping all Zen role/permission tables...');
  for (const tableName of dropOrder) {
    const exists = await tableExists(tableName);
    if (exists) {
      await query(`DROP TABLE IF EXISTS "${tableName}" CASCADE`);
      done(`Dropped table: ${tableName}`);
    }
  }
  done('All role/permission tables dropped');
}
