import { mkdir, rm, writeFile } from "node:fs/promises";
import path from "node:path";
import { fileURLToPath } from "node:url";
import { AppConfigService } from "../../apps/desktop/src/main/services/app-config.js";
import { ProjectStoreService } from "../../apps/desktop/src/main/services/project-store.js";
import { ProjectContextService } from "../../apps/desktop/src/main/services/project-context.js";

function delay(ms: number): Promise<void> {
  return new Promise((resolve) => setTimeout(resolve, ms));
}

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

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-context-refresh-smoke", "result.json"));
  const tempRoot = path.dirname(resultPath);
  const userDataPath = path.join(tempRoot, "user-data");
  const readmeMarkerBefore = "README marker before refresh smoke";
  const readmeMarkerAfter = "README marker after refresh smoke";

  await rm(tempRoot, { recursive: true, force: true });
  await mkdir(userDataPath, { recursive: true });

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

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

  const project = await projectStore.upsertProject({
    id: "context-refresh-smoke",
    name: "Context Refresh Smoke",
    description: "Verifies project context snapshot invalidation and refresh.",
    ready: true,
    boundSkillIds: []
  });
  await projectStore.setActiveProject(project.id);

  const projectRoot = await projectStore.getProjectRoot(project.id);
  const readmePath = path.join(projectRoot, "README.md");
  await writeFile(readmePath, `# Context Refresh Smoke\n\n${readmeMarkerBefore}\n`, "utf8");

  const projectContextService = new ProjectContextService(projectStore);
  const session = await projectStore.createSession("Context Refresh Smoke", project.id);

  const initialSnapshot = await projectContextService.getSnapshot(project.id);
  const cachedSnapshot = await projectContextService.getSnapshot(project.id);
  assert(initialSnapshot.snapshotId === cachedSnapshot.snapshotId, "Expected unchanged project context to reuse cached snapshotId.");
  assert(cachedSnapshot.readme?.includes(readmeMarkerBefore), "Initial snapshot did not include README marker.");

  await delay(25);
  projectContextService.invalidateSnapshot(project.id);
  const forcedRefreshSnapshot = await projectContextService.refreshSnapshot(project.id);
  assert(forcedRefreshSnapshot.snapshotId === initialSnapshot.snapshotId, "Refresh without file changes unexpectedly changed snapshotId.");

  await projectStore.bindSessionContextSnapshot(session.id, initialSnapshot.snapshotId);
  const boundBeforeChange = await projectStore.getSessionState(session.id);
  assert(boundBeforeChange.contextSnapshotId === initialSnapshot.snapshotId, "Session binding did not persist initial snapshotId.");

  await delay(25);
  await writeFile(readmePath, `# Context Refresh Smoke\n\n${readmeMarkerAfter}\n`, "utf8");

  const staleSessionState = await projectStore.getSessionState(session.id);
  assert(staleSessionState.contextSnapshotId === initialSnapshot.snapshotId, "Session binding changed before refresh smoke step executed.");

  projectContextService.invalidateSnapshot(project.id);
  const refreshedSnapshot = await projectContextService.refreshSnapshot(project.id);
  assert(refreshedSnapshot.snapshotId !== initialSnapshot.snapshotId, "Tracked file change did not produce a new snapshotId.");
  assert(refreshedSnapshot.readme?.includes(readmeMarkerAfter), "Refreshed snapshot did not include updated README marker.");

  const renderedContext = await projectContextService.buildSystemContext(project.id);
  assert(renderedContext.includes(readmeMarkerAfter), "Rendered system context did not include refreshed README content.");

  if (staleSessionState.contextSnapshotId !== refreshedSnapshot.snapshotId) {
    await projectStore.bindSessionContextSnapshot(session.id, refreshedSnapshot.snapshotId);
  }

  const reboundSessionState = await projectStore.getSessionState(session.id);
  assert(reboundSessionState.contextSnapshotId === refreshedSnapshot.snapshotId, "Session contextSnapshotId was not rebound to refreshed snapshotId.");

  const finalSnapshot = await projectContextService.getSnapshot(project.id);
  assert(finalSnapshot.snapshotId === refreshedSnapshot.snapshotId, "Post-refresh getSnapshot did not reuse refreshed snapshotId.");

  const summary = {
    ok: true,
    userDataPath,
    projectId: project.id,
    sessionId: session.id,
    initialSnapshotId: initialSnapshot.snapshotId,
    forcedRefreshSnapshotId: forcedRefreshSnapshot.snapshotId,
    refreshedSnapshotId: refreshedSnapshot.snapshotId,
    finalSnapshotId: finalSnapshot.snapshotId,
    staleSessionSnapshotId: staleSessionState.contextSnapshotId,
    reboundSessionSnapshotId: reboundSessionState.contextSnapshotId,
    cacheStable: initialSnapshot.snapshotId === cachedSnapshot.snapshotId,
    fileChangeDetected: refreshedSnapshot.snapshotId !== initialSnapshot.snapshotId,
    rebindApplied: reboundSessionState.contextSnapshotId === refreshedSnapshot.snapshotId,
    renderedContextIncludesUpdatedReadme: renderedContext.includes(readmeMarkerAfter),
    readmePath
  };

  await mkdir(path.dirname(resultPath), { recursive: true });
  await writeFile(resultPath, JSON.stringify(summary, null, 2), "utf8");
  console.log(JSON.stringify(summary, null, 2));
}

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-context-refresh-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;
});