/**
 * Dev-mode asset server for `/__wrnexus/*`:
 *   /__wrnexus/reactive.js         the reactive runtime
 *   /__wrnexus/theme.css           design-token themes (per resolved theme config)
 *   /__wrnexus/theme.js            client theme switcher
 *   /__wrnexus/styles.css          bundled global stylesheet (cached)
 *
 * Components are `.wrn` files rendered on the server (see runtime.ts), so there
 * are no per-component browser chunks to build or serve here. The CSS cache is
 * invalidated in-process by the file watcher so edits show without a restart.
 */

import {
  getActionRuntime,
  getComponentControllerRuntime,
  getReactiveRuntime,
  getNavRuntime,
  getRealtimeRuntime,
} from "@wrnexus/csr";
import {
  renderStyles,
  renderActiveThemeCss,
  renderThemeCss,
  renderThemeRuntime,
  type ResolvedTheme,
  type StylesConfig,
} from "@wrnexus/styles";
import { VALIDATE_RUNTIME } from "@wrnexus/validation";
import { I18N_RUNTIME } from "@wrnexus/i18n";
import { UPLOAD_RUNTIME, UPLOAD_JS_HREF, UPLOADS_PREFIX, serveStoredFile } from "@wrnexus/uploader";
import type { Mode } from "@wrnexus/core";
import type { AssetServer } from "./runtime.ts";
import { servePublicAsset } from "./public.ts";
import { servePluginAsset, type ServedPluginAsset } from "./plugin-assets.ts";
import { serveWrnBrowserArtifact } from "./pipeline.ts";

/** Style inputs the dev asset server needs to build `/__wrnexus/styles.css`. */
export interface DevStyles {
  entry: string | null;
  config?: StylesConfig;
  appRoot: string;
  publicDir?: string;
  sources?: string[];
  entries?: string[];
}

/** A dev asset server also supports invalidating its caches in-process. */
export interface DevAssetServer extends AssetServer {
  invalidateCss(): void;
  updateSchemas(code: string): void;
}

function jsResponse(code: string): Response {
  return new Response(code, {
    headers: {
      "content-type": "text/javascript; charset=utf-8",
      "cache-control": "no-store, max-age=0",
      pragma: "no-cache",
      expires: "0",
    },
  });
}

function cssResponse(code: string): Response {
  return new Response(code, {
    headers: {
      "content-type": "text/css; charset=utf-8",
      "cache-control": "no-cache",
    },
  });
}

export function createDevAssetServer(
  appDir: string,
  mode: Mode,
  styles?: DevStyles,
  theme?: ResolvedTheme,
  uiCss?: string | (() => string),
  schemasJs?: string,
  pluginAssets: readonly ServedPluginAsset[] = [],
): DevAssetServer {
  let cssCache: string | null = null;
  let schemasCode = schemasJs ?? "window.__wireSchemas={};";

  return {
    invalidateCss() {
      cssCache = null;
    },

    updateSchemas(code) {
      schemasCode = code;
    },

    async serve(pathname: string): Promise<Response | null> {
      if (pathname.startsWith("/__wrnexus/client/")) {
        return serveWrnBrowserArtifact(pathname) ?? new Response("Not Found", { status: 404 });
      }
      if (pathname === "/__wrnexus/reactive.js") return jsResponse(getReactiveRuntime(true));
      if (pathname === "/__wrnexus/controllers.js")
        return jsResponse(getComponentControllerRuntime(true));
      if (pathname === "/__wrnexus/nav.js") return jsResponse(getNavRuntime());
      if (pathname === "/__wrnexus/realtime.js") return jsResponse(getRealtimeRuntime());
      if (pathname === "/__wrnexus/actions.js") return jsResponse(getActionRuntime());
      if (pathname === "/__wrnexus/validate.js") return jsResponse(VALIDATE_RUNTIME);
      if (pathname === "/__wrnexus/i18n.js") return jsResponse(I18N_RUNTIME);
      if (pathname === UPLOAD_JS_HREF) return jsResponse(UPLOAD_RUNTIME);

      // Public local uploads served at /__wrnexus/uploads/<store>/<key>.
      if (pathname.startsWith(UPLOADS_PREFIX)) {
        return (await serveStoredFile(pathname)) ?? new Response("Not Found", { status: 404 });
      }
      if (pathname === "/__wrnexus/schemas.js") return jsResponse(schemasCode);

      if (pathname === "/__wrnexus/ui.css") {
        const currentUiCss = typeof uiCss === "function" ? uiCss() : uiCss;
        return currentUiCss
          ? cssResponse(currentUiCss)
          : new Response("Not Found", { status: 404 });
      }

      if (pathname === "/__wrnexus/theme.css") {
        return theme
          ? cssResponse(renderThemeCss(theme))
          : new Response("Not Found", { status: 404 });
      }
      const activeThemeMatch = /^\/__wrnexus\/theme\/([^/]+)\/([^/]+)\.css$/.exec(pathname);
      if (activeThemeMatch && theme) {
        try {
          const themeName = decodeURIComponent(activeThemeMatch[1]!);
          const accentPart = decodeURIComponent(activeThemeMatch[2]!);
          return cssResponse(
            renderActiveThemeCss(theme, themeName, accentPart === "_" ? undefined : accentPart),
          );
        } catch {
          return new Response("Not Found", { status: 404 });
        }
      }
      if (pathname === "/__wrnexus/theme.js") {
        return theme
          ? jsResponse(renderThemeRuntime(theme))
          : new Response("Not Found", { status: 404 });
      }

      if (pathname === "/__wrnexus/styles.css") {
        if (!styles?.entry && !styles?.entries?.length) {
          return new Response("Not Found", { status: 404 });
        }
        if (cssCache === null) {
          cssCache = await renderStyles(
            {
              entryPath: styles.entry,
              appDir,
              appRoot: styles.appRoot,
              mode,
              sources: styles.sources,
              entries: styles.entries,
            },
            styles.config,
          );
        }
        return cssResponse(cssCache);
      }

      const pluginAsset = await servePluginAsset(pluginAssets, pathname, mode);
      if (pluginAsset) return pluginAsset;

      return servePublicAsset(styles?.publicDir, pathname, mode);
    },
  };
}
