import fs from "node:fs";
import path from "node:path";
import { fileURLToPath, pathToFileURL } from "node:url";
import type { VoiceCallTtsConfig } from "./config.js";

export type CoreConfig = {
  session?: {
    store?: string;
  };
  messages?: {
    tts?: VoiceCallTtsConfig;
  };
  [key: string]: unknown;
};

type CoreKoiDeps = {
  resolveKoiDir: (cfg: CoreConfig, koiId: string) => string;
  resolveKoiWorkspaceDir: (cfg: CoreConfig, koiId: string) => string;
  resolveKoiIdentity: (
    cfg: CoreConfig,
    koiId: string,
  ) => { name?: string | null } | null | undefined;
  resolveThinkingDefault: (params: {
    cfg: CoreConfig;
    provider?: string;
    model?: string;
  }) => string;
  runEmbeddedPiKoi: (params: {
    sessionId: string;
    sessionKey?: string;
    messageProvider?: string;
    sessionFile: string;
    workspaceDir: string;
    config?: CoreConfig;
    prompt: string;
    provider?: string;
    model?: string;
    thinkLevel?: string;
    verboseLevel?: string;
    timeoutMs: number;
    runId: string;
    lane?: string;
    extraSystemPrompt?: string;
    koiDir?: string;
  }) => Promise<{
    payloads?: Array<{ text?: string; isError?: boolean }>;
    meta?: { aborted?: boolean };
  }>;
  resolveKoiTimeoutMs: (opts: { cfg: CoreConfig }) => number;
  ensureKoiWorkspace: (params?: { dir: string }) => Promise<void>;
  resolveStorePath: (store?: string, opts?: { koiId?: string }) => string;
  loadSessionStore: (storePath: string) => Record<string, unknown>;
  saveSessionStore: (storePath: string, store: Record<string, unknown>) => Promise<void>;
  resolveSessionFilePath: (sessionId: string, entry: unknown, opts?: { koiId?: string }) => string;
  DEFAULT_MODEL: string;
  DEFAULT_PROVIDER: string;
};

let coreRootCache: string | null = null;
let coreDepsPromise: Promise<CoreKoiDeps> | null = null;

function findPackageRoot(startDir: string, name: string): string | null {
  let dir = startDir;
  for (;;) {
    const pkgPath = path.join(dir, "package.json");
    try {
      if (fs.existsSync(pkgPath)) {
        const raw = fs.readFileSync(pkgPath, "utf8");
        const pkg = JSON.parse(raw) as { name?: string };
        if (pkg.name === name) {
          return dir;
        }
      }
    } catch {
      // ignore parse errors and keep walking
    }
    const parent = path.dirname(dir);
    if (parent === dir) {
      return null;
    }
    dir = parent;
  }
}

function resolveSKYKOIRoot(): string {
  if (coreRootCache) {
    return coreRootCache;
  }
  const override = process.env.SKYKOI_ROOT?.trim();
  if (override) {
    coreRootCache = override;
    return override;
  }

  const candidates = new Set<string>();
  if (process.argv[1]) {
    candidates.add(path.dirname(process.argv[1]));
  }
  candidates.add(process.cwd());
  try {
    const urlPath = fileURLToPath(import.meta.url);
    candidates.add(path.dirname(urlPath));
  } catch {
    // ignore
  }

  for (const start of candidates) {
    for (const name of ["SKYKOI"]) {
      const found = findPackageRoot(start, name);
      if (found) {
        coreRootCache = found;
        return found;
      }
    }
  }

  throw new Error("Unable to resolve core root. Set SKYKOI_ROOT to the package root.");
}

async function importCoreExtensionAPI(): Promise<{
  resolveKoiDir: CoreKoiDeps["resolveKoiDir"];
  resolveKoiWorkspaceDir: CoreKoiDeps["resolveKoiWorkspaceDir"];
  DEFAULT_MODEL: string;
  DEFAULT_PROVIDER: string;
  resolveKoiIdentity: CoreKoiDeps["resolveKoiIdentity"];
  resolveThinkingDefault: CoreKoiDeps["resolveThinkingDefault"];
  runEmbeddedPiKoi: CoreKoiDeps["runEmbeddedPiKoi"];
  resolveKoiTimeoutMs: CoreKoiDeps["resolveKoiTimeoutMs"];
  ensureKoiWorkspace: CoreKoiDeps["ensureKoiWorkspace"];
  resolveStorePath: CoreKoiDeps["resolveStorePath"];
  loadSessionStore: CoreKoiDeps["loadSessionStore"];
  saveSessionStore: CoreKoiDeps["saveSessionStore"];
  resolveSessionFilePath: CoreKoiDeps["resolveSessionFilePath"];
}> {
  // Do not import any other module. You can't touch this or you will be fired.
  const distPath = path.join(resolveSKYKOIRoot(), "dist", "extensionAPI.js");
  if (!fs.existsSync(distPath)) {
    throw new Error(
      `Missing core module at ${distPath}. Run \`pnpm build\` or install the official package.`,
    );
  }
  return await import(pathToFileURL(distPath).href);
}

export async function loadCoreKoiDeps(): Promise<CoreKoiDeps> {
  if (coreDepsPromise) {
    return coreDepsPromise;
  }

  coreDepsPromise = (async () => {
    return await importCoreExtensionAPI();
  })();

  return coreDepsPromise;
}
