/**
 * Media Feature — Database Schema
 *
 * Tables :
 *   - zen_media : registre central des médias (slug, clé S3, visibilité, owner, metadata).
 *   - zen_media_references : tracking des usages pour empêcher la suppression d'un
 *     média référencé (ON DELETE RESTRICT côté FK).
 *
 * Les permissions media sont enregistrées au module-load pour qu'elles soient
 * disponibles avant le seed des rôles (ce qui les ajoute automatiquement au rôle
 * admin lors d'un `npx zen-db init`).
 */

import { query, tableExists } from '@zen/core/database';
import { done, warn } from '@zen/core/shared/logger';
import { registerPermissions } from '../../core/users/permissions-registry.js';
import { MEDIA_PERMISSION_DEFINITIONS } from './permissions.js';

// Side effect : enregistre les permissions dès l'import du module.
registerPermissions(MEDIA_PERMISSION_DEFINITIONS);

const MEDIA_TABLES = [
  {
    name: 'zen_media',
    sql: `
      CREATE TABLE zen_media (
        id text NOT NULL PRIMARY KEY,
        slug text NOT NULL UNIQUE,
        storage_key text NOT NULL UNIQUE,
        original_name text NOT NULL,
        mime_type text NOT NULL,
        size_bytes bigint NOT NULL,
        visibility text NOT NULL DEFAULT 'private' CHECK (visibility IN ('private', 'public')),
        uploaded_by text REFERENCES zen_auth_users(id) ON DELETE SET NULL,
        alt_text text,
        caption text,
        created_at timestamptz NOT NULL DEFAULT CURRENT_TIMESTAMP,
        updated_at timestamptz NOT NULL DEFAULT CURRENT_TIMESTAMP
      )
    `,
    indexes: [
      `CREATE INDEX zen_media_visibility_idx ON zen_media (visibility)`,
      `CREATE INDEX zen_media_mime_type_idx  ON zen_media (mime_type)`,
      `CREATE INDEX zen_media_uploaded_by_idx ON zen_media (uploaded_by)`,
      `CREATE INDEX zen_media_created_at_idx ON zen_media (created_at DESC)`,
    ],
  },
  {
    name: 'zen_media_references',
    sql: `
      CREATE TABLE zen_media_references (
        media_id text NOT NULL REFERENCES zen_media(id) ON DELETE RESTRICT,
        source_type text NOT NULL,
        source_id text NOT NULL,
        field text NOT NULL DEFAULT '',
        created_at timestamptz NOT NULL DEFAULT CURRENT_TIMESTAMP,
        PRIMARY KEY (media_id, source_type, source_id, field)
      )
    `,
    indexes: [
      `CREATE INDEX zen_media_references_source_idx ON zen_media_references (source_type, source_id)`,
    ],
  },
];

/**
 * Idempotent : insère les permissions media dans le catalogue BD et les attribue
 * au rôle admin. Permet d'activer le module après un init initial sans recréer
 * la BD entière.
 */
async function syncMediaPermissions() {
  for (const perm of MEDIA_PERMISSION_DEFINITIONS) {
    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 query(
    `INSERT INTO zen_auth_role_permissions (role_id, permission_key)
     SELECT r.id, p.key
     FROM zen_auth_roles r
     CROSS JOIN zen_auth_permissions p
     WHERE r.name = 'admin' AND p.key = ANY($1::text[])
     ON CONFLICT DO NOTHING`,
    [MEDIA_PERMISSION_DEFINITIONS.map(p => p.key)]
  );
}

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

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

  await syncMediaPermissions();

  return { created, skipped };
}

export async function dropTables() {
  const dropOrder = [...MEDIA_TABLES].reverse().map(t => t.name);
  warn('Dropping all Zen media 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 media tables dropped');
}
