/**
 * Media Feature — API Routes
 *
 * Routes admin (gated par permission) pour piloter le gestionnaire depuis
 * l'interface, plus une route "publique" qui sert le contenu binaire des
 * médias selon leur visibilité.
 *
 * La route de service de fichier est `/zen/api/media/file/:slug` (préfixe
 * dédié pour ne pas entrer en collision avec les endpoints admin `/media/:id`) :
 *   - visibilité = 'public'  → servie sans authentification
 *   - visibilité = 'private' → requiert une session avec media.view
 *
 * On garde tout sous /zen/api/ pour profiter du file-response builder du core
 * qui détecte `{ success: true, file: {...} }` et stream le binaire.
 */

import { defineApiRoutes } from '@zen/core/api';
import { apiError, apiSuccess } from '@zen/core/api';
import { getFile } from '@zen/core/storage';
import { hasPermission } from '@zen/core/users';
import { getSessionCookieName } from '@zen/core/shared/config';
import { fail } from '@zen/core/shared/logger';
import {
  uploadMedia,
  deleteMedia,
  updateMedia,
  getMediaById,
  getMediaBySlug,
  listMedia,
  countReferences,
  listReferences,
} from './api.server.js';
import { MEDIA_PERMISSIONS } from './permissions.js';

const COOKIE_NAME = getSessionCookieName();

// ─── Admin handlers ────────────────────────────────────────────────────────

async function handleListMedia(request) {
  const url = new URL(request.url);
  const result = await listMedia({
    page:       parseInt(url.searchParams.get('page') || '1', 10),
    limit:      parseInt(url.searchParams.get('limit') || '24', 10),
    search:     url.searchParams.get('search') || undefined,
    kind:       url.searchParams.get('kind') || undefined,
    visibility: url.searchParams.get('visibility') || undefined,
  });
  return apiSuccess(result);
}

async function handleUploadMedia(request, _params, { session }) {
  try {
    const formData = await request.formData();
    const file = formData.get('file');
    if (!file) return apiError('Bad Request', 'No file provided');

    const visibility = formData.get('visibility') === 'public' ? 'public' : 'private';
    const altText    = formData.get('altText') || null;
    const caption    = formData.get('caption') || null;

    const result = await uploadMedia({
      file,
      uploadedBy: session.user.id,
      visibility,
      altText,
      caption,
    });

    if (!result.success) return apiError('Bad Request', result.error);
    return apiSuccess({ media: result.media });
  } catch (error) {
    fail(`handleUploadMedia: ${error.message}`);
    return apiError('Internal Server Error', 'Échec du téléversement');
  }
}

async function handleGetMediaAdmin(_request, { id }) {
  const media = await getMediaById(id);
  if (!media) return apiError('Not Found', 'Média introuvable');
  const referenceCount = await countReferences(id);
  return apiSuccess({ media, referenceCount });
}

// Variante par slug : utile depuis les composants qui ne connaissent que le
// slug (ex. bloc image du BlockEditor où `mediaSlug` est le seul lien
// persistant vers le média).
async function handleGetMediaBySlugAdmin(_request, { slug }) {
  if (!slug || /[^A-Za-z0-9_-]/.test(slug)) {
    return apiError('Bad Request', 'Invalid slug');
  }
  const media = await getMediaBySlug(slug);
  if (!media) return apiError('Not Found', 'Média introuvable');
  const referenceCount = await countReferences(media.id);
  return apiSuccess({ media, referenceCount });
}

async function handleListReferencesAdmin(_request, { id }) {
  const media = await getMediaById(id);
  if (!media) return apiError('Not Found', 'Média introuvable');
  const references = await listReferences(id);
  return apiSuccess({ references });
}

async function handleUpdateMedia(request, { id }) {
  try {
    const body = await request.json();
    const result = await updateMedia(id, {
      visibility: body.visibility,
      altText:    body.altText,
      caption:    body.caption,
    });
    if (!result.success) {
      const code = result.error === 'Media not found' ? 'Not Found' : 'Bad Request';
      return apiError(code, result.error);
    }
    return apiSuccess({ media: result.media });
  } catch (error) {
    fail(`handleUpdateMedia: ${error.message}`);
    return apiError('Internal Server Error', 'Échec de la mise à jour');
  }
}

async function handleDeleteMedia(_request, { id }) {
  const result = await deleteMedia(id);
  if (!result.success) {
    if (result.error === 'Media not found') return apiError('Not Found', result.error);
    if (result.referenceCount > 0)          return apiError('Bad Request', result.error);
    return apiError('Internal Server Error', result.error);
  }
  return apiSuccess({ success: true });
}

// ─── Public file serving ──────────────────────────────────────────────────

async function handleServeMediaBySlug(_request, { slug }) {
  try {
    if (!slug || /[^A-Za-z0-9_-]/.test(slug)) {
      return apiError('Bad Request', 'Invalid slug');
    }

    const media = await getMediaBySlug(slug);
    if (!media) return apiError('Not Found', 'Média introuvable');

    // Visibilité privée : exiger une session avec media.view.
    if (media.visibility !== 'public') {
      const { cookies } = await import('next/headers');
      const cookieStore = await cookies();
      const sessionToken = cookieStore.get(COOKIE_NAME)?.value;
      if (!sessionToken) return apiError('Not Found', 'Média introuvable');

      const { getSessionResolver } = await import('@zen/core/api');
      const session = await getSessionResolver()(sessionToken);
      if (!session) return apiError('Not Found', 'Média introuvable');

      const allowed = await hasPermission(session.user.id, MEDIA_PERMISSIONS.VIEW);
      if (!allowed) return apiError('Not Found', 'Média introuvable');
    }

    const fetched = await getFile(media.storage_key);
    if (!fetched.success) {
      fail(`handleServeMediaBySlug: storage miss for ${media.storage_key}: ${fetched.error}`);
      return apiError('Not Found', 'Média introuvable');
    }

    return {
      success: true,
      file: {
        body:          fetched.data.body,
        contentType:   media.mime_type,
        contentLength: fetched.data.contentLength,
        lastModified:  fetched.data.lastModified,
      },
    };
  } catch (error) {
    fail(`handleServeMediaBySlug: ${error.message}`);
    return apiError('Internal Server Error', 'Failed to serve media');
  }
}

// ─── Routes ───────────────────────────────────────────────────────────────

export const routes = defineApiRoutes([
  { path: '/media',                  method: 'GET',    handler: handleListMedia,         auth: 'admin', permission: MEDIA_PERMISSIONS.VIEW   },
  { path: '/media',                  method: 'POST',   handler: handleUploadMedia,       auth: 'admin', permission: MEDIA_PERMISSIONS.UPLOAD },
  { path: '/media/:id',              method: 'GET',    handler: handleGetMediaAdmin,     auth: 'admin', permission: MEDIA_PERMISSIONS.VIEW   },
  { path: '/media/by-slug/:slug',    method: 'GET',    handler: handleGetMediaBySlugAdmin, auth: 'admin', permission: MEDIA_PERMISSIONS.VIEW   },
  { path: '/media/:id',              method: 'PATCH',  handler: handleUpdateMedia,       auth: 'admin', permission: MEDIA_PERMISSIONS.UPLOAD },
  { path: '/media/:id',              method: 'DELETE', handler: handleDeleteMedia,       auth: 'admin', permission: MEDIA_PERMISSIONS.DELETE },
  { path: '/media/:id/references',   method: 'GET',    handler: handleListReferencesAdmin, auth: 'admin', permission: MEDIA_PERMISSIONS.VIEW   },
  // Public file-serving route — préfixe dédié `/file/` pour éviter la collision
  // avec `/media/:id`. Le handler enforce la visibilité en interne.
  { path: '/media/file/:slug',       method: 'GET',    handler: handleServeMediaBySlug,  auth: 'public' },
]);
