import { createHmac } from "node:crypto";
import { join, resolve } from "node:path";

import { registrationNotice } from "@dada/shared-contracts";

import { createApp } from "./app.js";
import { readBrowserSupportRelease } from "./browser-support.js";
import { defaultInstanceConfigPath, ensureLocalDataRuntimeDirectories, readConfiguredLocalDataRoot, type PublicAssetResolver } from "./local-data-root.js";
import { ManagedStorage } from "./managed-storage.js";
import { LatestExportService } from "./latest-exports.js";
import { CreditService } from "./credits.js";
import { ProjectService } from "./projects.js";
import { RegistrationService } from "./registration.js";
import { RecentAssetService } from "./recent-assets.js";
import { MockResendAdapter } from "./resend-adapter.js";
import { readSecureConfigCandidate } from "./secure-config.js";
import { StructuredJsonlLogger } from "./structured-log.js";
import { attachApiSupervisorControl, initializeApiCredentialClients, receiveApiCredentials } from "./supervisor-channel.js";
import { GenerationSubmissionService } from "./generation-submission.js";
import {
  GenerationModelConfigurationCatalog,
  ModelConfigurationService,
  portableRuntimeModelCandidates,
} from "./model-configuration.js";
import { MockAmapAdapter, type AmapAdapter } from "./amap-adapter.js";
import { StickerReleaseService } from "./sticker-releases.js";
import { createAdminDiagnosticsProvider, createAdminServicesStorageProvider } from "./admin-state.js";
import { loadConfiguredRuntimeAssets, type RuntimeAssetState } from "./runtime-assets.js";

const credentialChannelEnabled = process.argv.includes("--dada-credential-stdin");
let registration: RegistrationService | undefined;
let projects: ProjectService | undefined;
let credits: CreditService | undefined;
let storage: ManagedStorage | undefined;
let latestExports: LatestExportService | undefined;
let models: ModelConfigurationService | undefined;
let generations: GenerationSubmissionService | undefined;
let recentAssets: RecentAssetService | undefined;
let stickers: StickerReleaseService | undefined;
let publicAssets: PublicAssetResolver | undefined;
let assetRootState: RuntimeAssetState | undefined;
let amap: AmapAdapter = new MockAmapAdapter();
let localTestAuth = false;
const instanceConfigPath = process.env.DADA_INSTANCE_CONFIG_PATH ?? defaultInstanceConfigPath();
if (credentialChannelEnabled) {
  const clients = initializeApiCredentialClients(await receiveApiCredentials());
  try {
    amap = clients.amap;
    localTestAuth = !clients.resendConfigured;
    const derivePepper = (purpose: string) => createHmac("sha256", clients.adminAllowlistPepper)
      .update(`Dada/P0A/${purpose}/v1`, "utf8")
      .digest();
    const dataRoot = readConfiguredLocalDataRoot(instanceConfigPath);
    ensureLocalDataRuntimeDirectories(dataRoot);
    const runtimeAssets = loadConfiguredRuntimeAssets({
      configFile: instanceConfigPath,
      dataRoot,
      trustedManifestPath: resolve("asset-metadata", "manifest.json"),
    });
    publicAssets = runtimeAssets.publicAssets;
    assetRootState = runtimeAssets.state;
    const databasePath = join(dataRoot, "db", "dada.sqlite3");
    registration = new RegistrationService({
      adminAllowlistPepper: Buffer.from(clients.adminAllowlistPepper),
      challengePepper: derivePepper("challenge-pepper"),
      currentPrivacyNoticeVersion: registrationNotice.version,
      databasePath,
      invitePepper: derivePepper("invite-pepper"),
      resend: new MockResendAdapter(),
      sessionPepper: derivePepper("session-pepper"),
    });
    projects = new ProjectService({ databasePath });
    credits = new CreditService({ databasePath });
    storage = new ManagedStorage({ dataRoot, databasePath });
    stickers = new StickerReleaseService({ databasePath, storage });
    latestExports = new LatestExportService({ databasePath, storage });
    models = new ModelConfigurationService({ database: registration.database, seedCandidates: portableRuntimeModelCandidates });
    generations = new GenerationSubmissionService({
      credits,
      models: new GenerationModelConfigurationCatalog(models),
      storage,
    });
    recentAssets = new RecentAssetService({ database: registration.database });
    registration.applySecureConfig(readSecureConfigCandidate(instanceConfigPath));
  } catch (error) {
    amap.dispose?.();
    amap = new MockAmapAdapter();
    stickers?.close();
    stickers = undefined;
    latestExports?.close();
    latestExports = undefined;
    generations?.close();
    generations = undefined;
    storage?.close();
    storage = undefined;
    credits?.close();
    credits = undefined;
    projects?.close();
    projects = undefined;
    registration?.close();
    registration = undefined;
    throw error;
  } finally {
    clients.adminAllowlistPepper.fill(0);
  }
}

const browserSupportRelease = readBrowserSupportRelease(resolve("RELEASE.json"));
const adminServicesStorage = registration
  ? createAdminServicesStorageProvider({
    database: registration.database,
    ...(models ? { models } : {}),
    ...(storage ? { storage } : {}),
    ...(assetRootState ? { assetRoot: assetRootState } : {}),
  })
  : undefined;
const adminDiagnostics = adminServicesStorage
  ? createAdminDiagnosticsProvider({
    ...(browserSupportRelease ? { browserSupportRelease, appVersion: browserSupportRelease.appVersion } : {}),
    servicesStorage: adminServicesStorage,
  })
  : undefined;
const app = await createApp({
  ...(adminServicesStorage ? { adminServicesStorage } : {}),
  ...(adminDiagnostics ? { adminDiagnostics } : {}),
  amap,
  ...(browserSupportRelease ? { browserSupportRelease } : {}),
  ...(credits ? { credits } : {}),
  ...(generations ? { generations } : {}),
  ...(latestExports ? { latestExports } : {}),
  ...(registration && localTestAuth ? { localTestAuth: true } : {}),
  ...(models ? { models } : {}),
  ...(projects ? { projects } : {}),
  ...(publicAssets ? { publicAssets } : {}),
  ...(registration ? { registration } : {}),
  ...(recentAssets ? { recentAssets } : {}),
  ...(stickers ? { stickers } : {}),
  ...(storage ? { storage } : {}),
});

await app.listen({
  host: "127.0.0.1",
  port: 43121,
});

const controlPipeIndex = process.argv.indexOf("--dada-control-pipe");
if (controlPipeIndex >= 0) {
  const controlPipe = process.argv[controlPipeIndex + 1];
  if (!controlPipe) throw new Error("Supervisor control pipe name is required.");
  const control = attachApiSupervisorControl(controlPipe, async () => {
    await app.close();
    amap.dispose?.();
    latestExports?.close();
    generations?.close();
    credits?.close();
    projects?.close();
    registration?.close();
    storage?.close();
    stickers?.close();
  });
  try {
    const dataRoot = readConfiguredLocalDataRoot(instanceConfigPath);
    if (!storage) storage = new ManagedStorage({ dataRoot, databasePath: join(dataRoot, "db", "dada.sqlite3") });
    const logger = new StructuredJsonlLogger({
      component: "api",
      directory: join(dataRoot, "logs", "api"),
      onWriteFailure: () => {
        try {
          storage?.setLogAvailability(false);
        } finally {
          control.reportStatus("storage_unavailable");
        }
      },
    });
    logger.write({ error_category: "none", status_category: "ready" });
  } catch {
    control.reportStatus("storage_unavailable");
  }
}
