import { expect, test } from "bun:test";
import { existsSync, mkdirSync, mkdtempSync, readFileSync, writeFileSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { generateMobile, mobileOptions } from "../src/mobile.ts";
import { runMobileCommand } from "../src/mobile-command.ts";
import { nativeCatalog, runNativeCommand } from "../src/native-command.ts";

test("generateMobile scaffolds a configurable Capacitor project", async () => {
  const dir = mkdtempSync(join(tmpdir(), "wire-mobile-"));
  writeFileSync(join(dir, "package.json"), JSON.stringify({ name: "@acme/store-front" }));

  await generateMobile(dir, {
    appId: "com.acme.store",
    appName: "Acme Store",
    serverUrl: "https://store.example.com",
  });

  const config = readFileSync(join(dir, "mobile", "capacitor.config.ts"), "utf8");
  expect(config).toContain('appId: mobile.appId ?? "com.acme.store"');
  expect(config).toContain('appName: mobile.appName ?? "Acme Store"');
  expect(config).toContain("...(mobile.capacitor ?? {})");
  expect(config).not.toContain("errorPath:");
  expect(config).toContain('"https://store.example.com"');
  expect(existsSync(join(dir, "mobile", "web", "index.html"))).toBe(true);

  const pkg = JSON.parse(readFileSync(join(dir, "mobile", "package.json"), "utf8"));
  expect(pkg.dependencies["@capacitor/android"]).toBe("^8.0.0");
  expect(pkg.scripts["open:ios"]).toBe("cap open ios");
});

test("generateMobile derives defaults and does not overwrite files", async () => {
  const dir = mkdtempSync(join(tmpdir(), "wire-mobile-"));
  writeFileSync(join(dir, "package.json"), JSON.stringify({ name: "demo-app" }));
  await generateMobile(dir);
  const path = join(dir, "mobile", "capacitor.config.ts");
  const before = readFileSync(path, "utf8");
  expect(before).toContain('appId: mobile.appId ?? "com.example.demoapp"');
  expect(before).toContain('"http://localhost:3000"');
  await generateMobile(dir, { appName: "Changed" });
  expect(readFileSync(path, "utf8")).toBe(before);
});

test("mobileOptions parses generator flags", () => {
  expect(
    mobileOptions(["--app-id=com.acme.app", "--app-name=Acme", "--url=https://app.test"]),
  ).toEqual({
    appId: "com.acme.app",
    appName: "Acme",
    serverUrl: "https://app.test",
    mode: undefined,
  });
});

test("generateMobile scaffolds a WebView-free native project from config", async () => {
  const dir = mkdtempSync(join(tmpdir(), "wire-native-"));
  writeFileSync(join(dir, "package.json"), JSON.stringify({ name: "native-demo" }));
  writeFileSync(
    join(dir, "wrnexus.config.mjs"),
    `export default { mobile: { mode: "native", appId: "com.acme.native", apiUrl: "https://api.example.com" } }`,
  );
  await generateMobile(dir);
  const pkg = JSON.parse(readFileSync(join(dir, "mobile", "package.json"), "utf8"));
  expect(pkg.wrnexus.mode).toBe("native");
  expect(pkg.dependencies.expo).toBeDefined();
  expect(existsSync(join(dir, "mobile", "app", "index.tsx"))).toBe(true);
  expect(readFileSync(join(dir, "mobile", "src", "wrnexus.ts"), "utf8")).toContain(
    "https://api.example.com",
  );
  expect(existsSync(join(dir, "mobile", "capacitor.config.ts"))).toBe(false);
});

test("mobile compile turns wrn pages into Expo routes", async () => {
  const dir = mkdtempSync(join(tmpdir(), "wire-native-compile-"));
  writeFileSync(join(dir, "package.json"), JSON.stringify({ name: "native-compile" }));
  writeFileSync(join(dir, "wrnexus.config.mjs"), `export default { mobile: { mode: "native" } }`);
  await generateMobile(dir);
  const pages = join(dir, "app", "pages");
  const nested = join(pages, "account");
  mkdirSync(nested, { recursive: true });
  writeFileSync(
    join(nested, "profile.wrn"),
    "page Profile { view { <main><h1>Profile</h1></main> } }",
  );
  await runMobileCommand(dir, "compile");
  const output = readFileSync(join(dir, "mobile", "app", "account", "profile.tsx"), "utf8");
  expect(output).toContain("function Profile()");
  expect(output).toContain("<View><Text>Profile</Text></View>");
});

test("native catalog uses built-in React Native sharing without installing expo-sharing", async () => {
  expect(nativeCatalog.share?.expo).toBeUndefined();
  const dir = mkdtempSync(join(tmpdir(), "wire-native-share-"));
  writeFileSync(join(dir, "package.json"), JSON.stringify({ name: "native-share" }));
  writeFileSync(join(dir, "wrnexus.config.mjs"), `export default { mobile: { mode: "native" } }`);
  await generateMobile(dir);
  await runNativeCommand(dir, "add", ["share"]);
  const pkg = JSON.parse(readFileSync(join(dir, "mobile", "package.json"), "utf8"));
  expect(pkg.dependencies["expo-sharing"]).toBeUndefined();
});
