import { mkdir, readFile, rm, stat, writeFile } from "node:fs/promises";
import http from "node:http";
import path from "node:path";
import { once } from "node:events";
import { fileURLToPath } from "node:url";
import { AppConfigService } from "../../apps/desktop/src/main/services/app-config.js";
import { ProjectBundleService } from "../../apps/desktop/src/main/services/project-bundle.js";
import { ProjectStoreService } from "../../apps/desktop/src/main/services/project-store.js";
import type { RemoteSkillAsset } from "../../apps/desktop/src/main/services/skill-store.js";

function assert(condition: unknown, message: string): asserts condition {
  if (!condition) {
    throw new Error(message);
  }
}

async function pathExists(targetPath: string): Promise<boolean> {
  try {
    await stat(targetPath);
    return true;
  } catch {
    return false;
  }
}

async function readJsonFile<T>(filePath: string): Promise<T | null> {
  try {
    const raw = await readFile(filePath, "utf8");
    return JSON.parse(raw) as T;
  } catch {
    return null;
  }
}

async function main(): Promise<void> {
  const __filename = fileURLToPath(import.meta.url);
  const __dirname = path.dirname(__filename);
  const repoRoot = path.resolve(__dirname, "..", "..");
  const resultPath = path.resolve(process.argv[2] ?? path.join(repoRoot, ".tmp", "project-bundle-freshness-smoke", "result.json"));
  const bundleZipPathA = path.resolve(process.argv[3] ?? path.join(path.dirname(resultPath), "bundle-a.zip"));
  const bundleZipPathB = path.resolve(process.argv[4] ?? path.join(path.dirname(resultPath), "bundle-b.zip"));
  const tempRoot = path.dirname(resultPath);
  const userDataPath = path.join(tempRoot, "user-data");
  const projectId = "bundle-freshness-smoke";
  const configVersion = "freshness-config-v1";
  let activeVariant: "a" | "b" = "a";

  assert(await pathExists(bundleZipPathA), `Bundle zip fixture A was not found: ${bundleZipPathA}`);
  assert(await pathExists(bundleZipPathB), `Bundle zip fixture B was not found: ${bundleZipPathB}`);
  await rm(userDataPath, { recursive: true, force: true });
  await mkdir(userDataPath, { recursive: true });

  const server = http.createServer(async (request, response) => {
    try {
      if (request.url !== "/bundle.zip") {
        response.statusCode = 404;
        response.end("not found");
        return;
      }

      const variantPath = activeVariant === "a" ? bundleZipPathA : bundleZipPathB;
      const payload = await readFile(variantPath);
      const etag = activeVariant === "a" ? '"bundle-freshness-a"' : '"bundle-freshness-b"';
      const lastModified = activeVariant === "a"
        ? "Tue, 31 Mar 2026 13:00:00 GMT"
        : "Tue, 31 Mar 2026 13:05:00 GMT";

      if (request.method === "HEAD") {
        response.writeHead(200, {
          ETag: etag,
          "Last-Modified": lastModified,
          "Content-Length": "4096",
          "Cache-Control": "no-store"
        });
        response.end();
        return;
      }

      if (request.method === "GET") {
        response.writeHead(200, {
          "Content-Type": "application/zip",
          "Cache-Control": "no-store"
        });
        response.end(payload);
        return;
      }

      response.statusCode = 405;
      response.end("method not allowed");
    } catch (error) {
      response.statusCode = 500;
      response.end(error instanceof Error ? error.message : String(error));
    }
  });

  server.listen(0, "127.0.0.1");
  await once(server, "listening");
  const address = server.address();
  assert(address && typeof address === "object", "Failed to start local freshness smoke server.");
  const downloadUrl = `http://127.0.0.1:${address.port}/bundle.zip`;

  try {
    const configService = new AppConfigService(userDataPath);
    await configService.load();

    const projectStore = new ProjectStoreService(configService);
    await projectStore.initialize();
    const projectBundleService = new ProjectBundleService(configService, projectStore);

    const remoteAsset: RemoteSkillAsset = {
      bindingId: "bundle-freshness-binding",
      skillId: "bundle-freshness-skill-asset",
      name: "Bundle Freshness Smoke Asset",
      description: "Remote bundle smoke asset for freshness validation.",
      category: "project",
      fileName: "bundle.zip",
      downloadUrl
    };

    await projectBundleService.syncRemoteBundles([remoteAsset], configVersion, "sync");

    const workspaceRoot = await projectStore.getWorkspaceRoot();
    const manifestPath = path.join(workspaceRoot, "manifests", "project-bundles.json");
    const readmePath = path.join(workspaceRoot, "projects", projectId, "README.md");
    const manifestAfterFirstSync = (await readJsonFile<Record<string, {
      checksum?: string;
      remoteEtag?: string;
      remoteLastModified?: string;
      remoteContentLength?: number;
    }>>(manifestPath)) ?? {};
    const firstRecord = manifestAfterFirstSync[projectId];
    assert(firstRecord, "First bundle sync did not persist a manifest record.");
    assert(firstRecord.remoteEtag === '"bundle-freshness-a"', `Unexpected first remoteEtag: ${String(firstRecord.remoteEtag || "")}`);
    assert(firstRecord.remoteLastModified === "Tue, 31 Mar 2026 13:00:00 GMT", `Unexpected first remoteLastModified: ${String(firstRecord.remoteLastModified || "")}`);
    assert(firstRecord.remoteContentLength === 4096, `Unexpected first remoteContentLength: ${String(firstRecord.remoteContentLength ?? "")}`);
    const readmeAfterFirstSync = await readFile(readmePath, "utf8");
    assert(readmeAfterFirstSync.includes("Freshness variant A"), "First bundle sync did not materialize README variant A.");

    activeVariant = "b";
    await projectBundleService.syncRemoteBundles([remoteAsset], configVersion, "sync");

    const manifestAfterSecondSync = (await readJsonFile<Record<string, {
      checksum?: string;
      remoteEtag?: string;
      remoteLastModified?: string;
      remoteContentLength?: number;
    }>>(manifestPath)) ?? {};
    const secondRecord = manifestAfterSecondSync[projectId];
    assert(secondRecord, "Second bundle sync did not persist a manifest record.");
    assert(secondRecord.remoteEtag === '"bundle-freshness-b"', `Unexpected second remoteEtag: ${String(secondRecord.remoteEtag || "")}`);
    assert(secondRecord.remoteLastModified === "Tue, 31 Mar 2026 13:05:00 GMT", `Unexpected second remoteLastModified: ${String(secondRecord.remoteLastModified || "")}`);
    assert(secondRecord.remoteContentLength === 4096, `Unexpected second remoteContentLength: ${String(secondRecord.remoteContentLength ?? "")}`);
    assert(String(secondRecord.checksum || "") !== String(firstRecord.checksum || ""), "Bundle checksum did not change after remote freshness metadata changed.");

    const readmeAfterSecondSync = await readFile(readmePath, "utf8");
    assert(readmeAfterSecondSync.includes("Freshness variant B"), "Second bundle sync did not materialize README variant B.");
    assert(!readmeAfterSecondSync.includes("Freshness variant A"), "Second bundle sync still contains README variant A content.");

    const summary = {
      ok: true,
      userDataPath,
      workspaceRoot,
      bundleZipPathA,
      bundleZipPathB,
      downloadUrl,
      configVersion,
      firstChecksum: firstRecord.checksum ?? null,
      secondChecksum: secondRecord.checksum ?? null,
      firstRemoteEtag: firstRecord.remoteEtag ?? null,
      secondRemoteEtag: secondRecord.remoteEtag ?? null,
      firstRemoteLastModified: firstRecord.remoteLastModified ?? null,
      secondRemoteLastModified: secondRecord.remoteLastModified ?? null,
      readmePath
    };

    await mkdir(path.dirname(resultPath), { recursive: true });
    await writeFile(resultPath, JSON.stringify(summary, null, 2), "utf8");
    console.log(JSON.stringify(summary, null, 2));
  } finally {
    await new Promise<void>((resolve) => {
      server.close(() => resolve());
    });
  }
}

main().catch(async (error) => {
  const __filename = fileURLToPath(import.meta.url);
  const __dirname = path.dirname(__filename);
  const repoRoot = path.resolve(__dirname, "..", "..");
  const resultPath = path.resolve(process.argv[2] ?? path.join(repoRoot, ".tmp", "project-bundle-freshness-smoke", "result.json"));
  const failure = {
    ok: false,
    error: error instanceof Error ? error.stack ?? error.message : String(error)
  };

  await mkdir(path.dirname(resultPath), { recursive: true });
  await writeFile(resultPath, JSON.stringify(failure, null, 2), "utf8");
  console.error(failure.error);
  process.exitCode = 1;
});
