import { NextRequest, NextResponse } from 'next/server';
import { requireAdminAuth } from '@/lib/admin/session';
import { logger } from '@/lib/logger';
import {
  getPluginRegistry,
  getThemeRegistry,
} from '@/lib/admin/plugin-registry';
import JSZip from 'jszip';
import { MAX_PLUGIN_SIZE, MAX_THEME_SIZE } from '@/lib/plugin-types';

const DIRECTORY_URL = process.env.EXTENSION_DIRECTORY_URL || 'https://extensions.bulwarkmail.org';

const MAX_PREVIEW_SOURCE_LEN = 100_000;

/**
 * GET /api/admin/marketplace/[slug]
 * Returns full preview info for an extension: directory metadata,
 * the bundle's manifest, a (truncated) source preview, and install status.
 * Lets admins audit what they're about to install before pressing the button.
 */
export async function GET(
  _request: NextRequest,
  { params }: { params: Promise<{ slug: string }> },
) {
  try {
    const result = await requireAdminAuth();
    if ('error' in result) return result.error;

    const { slug } = await params;

    // 1. Extension metadata + screenshots + theme previews from the directory
    const detailUrl = new URL(`/api/v1/extension/${encodeURIComponent(slug)}`, DIRECTORY_URL);
    const detailRes = await fetch(detailUrl.toString(), {
      headers: { Accept: 'application/json' },
      signal: AbortSignal.timeout(10000),
    });

    if (!detailRes.ok) {
      const status = detailRes.status === 404 ? 404 : 502;
      return NextResponse.json(
        { error: status === 404 ? 'Extension not found' : 'Directory request failed' },
        { status },
      );
    }

    const detailJson = await detailRes.json();
    const extension = detailJson.data as Record<string, unknown> | undefined;
    if (!extension) {
      return NextResponse.json({ error: 'Extension not found' }, { status: 404 });
    }

    const type = extension.type as 'plugin' | 'theme';
    const latestVersion = (extension.latestVersion as { version?: string } | null)?.version
      ?? null;

    // 2. Pull the bundle so we can show what's actually inside.
    let manifest: Record<string, unknown> | null = null;
    let sourcePreview: { name: string; content: string; truncated: boolean } | null = null;
    let bundleError: string | null = null;
    let bundleSize = 0;

    if (latestVersion) {
      try {
        const bundleUrl = new URL(
          `/api/v1/bundle/${encodeURIComponent(slug)}/${encodeURIComponent(latestVersion)}`,
          DIRECTORY_URL,
        );
        const bundleRes = await fetch(bundleUrl.toString(), {
          signal: AbortSignal.timeout(30000),
        });

        if (!bundleRes.ok) {
          bundleError = `Bundle download failed (${bundleRes.status})`;
        } else {
          const buffer = await bundleRes.arrayBuffer();
          bundleSize = buffer.byteLength;
          const maxSize = type === 'theme' ? MAX_THEME_SIZE : MAX_PLUGIN_SIZE;
          if (buffer.byteLength > maxSize) {
            bundleError = `Bundle exceeds ${type === 'theme' ? '1 MB' : '5 MB'} size limit`;
          } else {
            const zip = await JSZip.loadAsync(buffer);

            // Detect optional root directory inside the ZIP.
            const entries = Object.keys(zip.files);
            const topDirs = new Set(entries.map((e) => e.split('/')[0]));
            let root = '';
            if (topDirs.size === 1) {
              const dir = [...topDirs][0];
              if (zip.files[dir + '/'] || entries.some((e) => e.startsWith(dir + '/'))) {
                root = dir + '/';
              }
            }

            const manifestFile = zip.file(root + 'manifest.json');
            if (!manifestFile) {
              bundleError = 'Bundle missing manifest.json';
            } else {
              try {
                manifest = JSON.parse(await manifestFile.async('string'));
              } catch {
                bundleError = 'Invalid manifest.json in bundle';
              }
            }

            if (manifest) {
              if (type === 'theme') {
                const cssFile = zip.file(root + 'theme.css');
                if (cssFile) {
                  const css = await cssFile.async('string');
                  sourcePreview = {
                    name: 'theme.css',
                    content: css.length > MAX_PREVIEW_SOURCE_LEN
                      ? css.slice(0, MAX_PREVIEW_SOURCE_LEN)
                      : css,
                    truncated: css.length > MAX_PREVIEW_SOURCE_LEN,
                  };
                }
              } else {
                const entrypoint = (manifest.entrypoint as string) || 'index.js';
                const jsFile = zip.file(root + entrypoint);
                if (jsFile) {
                  const code = await jsFile.async('string');
                  sourcePreview = {
                    name: entrypoint,
                    content: code.length > MAX_PREVIEW_SOURCE_LEN
                      ? code.slice(0, MAX_PREVIEW_SOURCE_LEN)
                      : code,
                    truncated: code.length > MAX_PREVIEW_SOURCE_LEN,
                  };
                }
              }
            }
          }
        }
      } catch (err) {
        bundleError = err instanceof Error ? err.message : 'Failed to read bundle';
      }
    } else {
      bundleError = 'Extension has no published version';
    }

    // 3. Install status (slug is used as the registry id at install time)
    const [pluginRegistry, themeRegistry] = await Promise.all([
      getPluginRegistry(),
      getThemeRegistry(),
    ]);
    const installed = type === 'theme'
      ? themeRegistry.themes.some((t) => t.id === slug)
      : pluginRegistry.plugins.some((p) => p.id === slug);

    // 4. Build screenshot URLs (proxy through the directory's public files endpoint).
    const screenshots = Array.isArray(extension.screenshots)
      ? (extension.screenshots as Array<{ path: string; altText?: string | null }>).map((s) => ({
          url: new URL(`/api/v1/files/${s.path}`, DIRECTORY_URL).toString(),
          altText: s.altText ?? null,
        }))
      : [];

    // Strip the heavy `manifest` blob from versions when echoing the directory data.
    const versions = Array.isArray(extension.versions)
      ? (extension.versions as Array<Record<string, unknown>>).map((v) => ({
          version: v.version,
          changelog: v.changelog,
          bundleSize: v.bundleSize,
          minAppVersion: v.minAppVersion,
          publishedAt: v.publishedAt,
          permissions: v.permissions,
        }))
      : [];

    const fileUrl = (path: unknown): string | null =>
      typeof path === 'string' && path
        ? new URL(`/api/v1/files/${path}`, DIRECTORY_URL).toString()
        : null;

    return NextResponse.json(
      {
        extension: {
          slug: extension.slug,
          name: extension.name,
          type: extension.type,
          pluginType: extension.pluginType ?? null,
          description: extension.description,
          longDescription: extension.longDescription ?? null,
          tags: extension.tags ?? [],
          permissions: extension.permissions ?? [],
          totalDownloads: extension.totalDownloads ?? 0,
          featured: extension.featured ?? false,
          githubRepo: extension.githubRepo ?? null,
          license: extension.license ?? null,
          minAppVersion: extension.minAppVersion ?? null,
          iconUrl: fileUrl(extension.iconPath),
          bannerUrl: fileUrl(extension.bannerPath),
          author: extension.author ?? null,
          latestVersion,
          versions,
          screenshots,
          themePreviews: extension.themePreviews ?? [],
          createdAt: extension.createdAt ?? null,
          updatedAt: extension.updatedAt ?? null,
        },
        bundle: {
          manifest,
          source: sourcePreview,
          size: bundleSize,
          error: bundleError,
        },
        installed,
      },
      { headers: { 'Cache-Control': 'no-store' } },
    );
  } catch (error) {
    logger.error('Marketplace preview error', {
      error: error instanceof Error ? error.message : 'Unknown error',
    });
    return NextResponse.json(
      { error: 'Failed to load preview' },
      { status: 502 },
    );
  }
}
