import * as fs from "node:fs/promises";
import * as path from "node:path";
import { execFile } from "node:child_process";
import { promisify } from "node:util";
import { expect } from "e2e";
import { T3_HOME } from "../e2e.config.ts";
import { test, WEB } from "./t3.ts";

const exec = promisify(execFile);

test("Pi usage mapped onto a Pi model retains the target model pricing", WEB, async ({ t3, app, screen, browser }) => {
  const isolatedHome = path.join(T3_HOME, "provider-home");
  const sessions = path.join(isolatedHome, "sessions");
  await fs.mkdir(sessions, { recursive: true });
  const settingsPath = path.join(T3_HOME, "userdata/settings.json");
  const settings = JSON.parse(await fs.readFile(settingsPath, "utf8"));
  for (const instance of Object.values(settings.providerInstances) as any[]) {
    instance.environment = [{ name: "HOME", value: isolatedHome, sensitive: false }];
  }
  settings.providerInstances.pi.environment.push({ name: "PI_CODING_AGENT_SESSION_DIR", value: sessions, sensitive: false });
  await fs.writeFile(settingsPath, JSON.stringify(settings));

  // A real Pi turn through the configured proxy, with Pi's custom-model price set to zero.
  // PROOF_PI_CONFIG is an isolated Pi config containing only the proxy model and credentials.
  const runPi = async (provider: string) => {
  const piTurn = exec("pi", ["--provider", provider, "--model", "gpt-5.6-luna", "--no-extensions", "--no-skills", "--no-prompt-templates", "--no-context-files", "--no-tools", "--system-prompt", "Reply briefly.", "--session-dir", sessions, "-p", "Reply exactly: usage pricing evidence."], {
    cwd: isolatedHome,
    env: { ...process.env, PI_CODING_AGENT_DIR: process.env.PROOF_PI_CONFIG },
  });
  piTurn.child.stdin?.end();
  await piTurn;
  };
  await runPi("cliproxyapi");
  const project = await t3.project("Usage pricing");
  const thread = await t3.thread({ project, title: "Pricing evidence", messages: ["Hello"] });
  await t3.open(thread);
  await t3.open("/usage");
  await screen.getByRole("button", "Cost", { exact: true }).tap();
  await screen.getByRole("button", "All providers", { exact: true }).tap();
  await screen.getByRole("menuitemcheckbox", "All providers", { exact: true }).tap();
  await screen.getByRole("menuitemcheckbox", "Pi", { exact: true }).tap();
  await browser.keyboard.press("Escape");
  await screen.getByRole("button", "cliproxyapi/gpt-5.6-luna", { exact: true }).tap();
  await app.screenshot("source-model-priced");
  await browser.keyboard.press("Escape");
  await screen.getByRole("button", "All environments", { exact: true }).tap();
  await screen.getByRole("menuitem", "Model prices", { exact: true }).tap();
  await screen.getByRole("button", "Add model price").tap();
  await screen.getByRole("combobox", "New model ID").fill("cliproxyapi/gpt-5.6-luna");
  await screen.getByRole("combobox", "Map cliproxyapi/gpt-5.6-luna to model").fill("openai/gpt-5.6-luna");
  await app.screenshot("mapping-to-target");
  await screen.getByRole("button", "Save changes", { exact: true }).tap();
  await screen.getByText("Saved", { exact: true }).waitFor();
  await screen.getByRole("button", "Close", { exact: true }).tap();
  await screen.getByRole("button", "openai/gpt-5.6-luna", { exact: true }).tap();
  await app.screenshot("mapped-model-pricing");
  const mappedHasCostSplit = await screen.getByRole("dialog").getByText("Cost by type", { exact: true }).isVisible();
  await browser.keyboard.press("Escape");

  await runPi("openai");
  let totalTokens = 0;
  for (const file of await fs.readdir(sessions)) {
    for (const line of (await fs.readFile(path.join(sessions, file), "utf8")).trim().split("\n")) {
      const entry = JSON.parse(line);
      if (entry.type === "message" && entry.message?.role === "assistant") totalTokens += entry.message.usage.totalTokens;
    }
  }
  await screen.getByRole("button", "Refresh usage", { exact: true }).tap();
  await screen.getByRole("cell", String(totalTokens), { exact: true }).waitFor();
  await screen.getByRole("button", "openai/gpt-5.6-luna", { exact: true }).tap();
  await app.screenshot("mapped-and-native-model-pricing");
  const mergedHasUnknownTokens = await screen.getByText(/tokens have no known price/).isVisible();
  await browser.keyboard.press("Escape");

  const openPrices = async () => {
    await screen.getByRole("button", "All environments", { exact: true }).tap();
    await screen.getByRole("menuitem", "Model prices", { exact: true }).tap();
  };
  const savePrices = async () => {
    await screen.getByRole("button", "Save changes", { exact: true }).tap();
    await screen.getByText("Saved", { exact: true }).waitFor();
    await screen.getByRole("button", "Close", { exact: true }).tap();
  };
  await openPrices();
  await screen.getByRole("button", "Add model price").tap();
  await screen.getByRole("combobox", "New model ID").fill("openai/gpt-5.6-luna");
  await screen.getByRole("textbox", "Input price for openai/gpt-5.6-luna").fill("100");
  await screen.getByRole("textbox", "Output price for openai/gpt-5.6-luna").fill("200");
  await savePrices();
  await screen.getByRole("button", "openai/gpt-5.6-luna", { exact: true }).tap();
  await app.screenshot("target-price-override");
  const overrideHasUnknownTokens = await screen.getByText(/tokens have no known price/).isVisible();
  await browser.keyboard.press("Escape");

  await openPrices();
  await screen.getByRole("button", "Reset price for openai/gpt-5.6-luna to automatic", { exact: true }).tap();
  await savePrices();
  await screen.getByRole("button", "openai/gpt-5.6-luna", { exact: true }).tap();
  await app.screenshot("target-override-reset");
  const resetHasUnknownTokens = await screen.getByText(/tokens have no known price/).isVisible();
  await browser.keyboard.press("Escape");

  await openPrices();
  await screen.getByRole("button", "Reset price for cliproxyapi/gpt-5.6-luna to automatic", { exact: true }).tap();
  await savePrices();
  await screen.getByRole("button", "cliproxyapi/gpt-5.6-luna", { exact: true }).tap();
  await app.screenshot("mapping-cleared");

  await expect(mappedHasCostSplit).toBe(true);
  await expect(mergedHasUnknownTokens).toBe(false);
  await expect(overrideHasUnknownTokens).toBe(false);
  await expect(resetHasUnknownTokens).toBe(false);
});
