import { afterEach, expect, test } from "bun:test";
import {
  MobileUnavailableError,
  PushNotifications,
  SecureStorage,
  invoke,
  isNative,
  platform,
  plugin,
  registerPlugin,
  whenNative,
} from "../src/index.ts";

const root = globalThis as typeof globalThis & { Capacitor?: unknown };
const original = root.Capacitor;

afterEach(() => {
  if (original === undefined) delete root.Capacitor;
  else root.Capacitor = original;
});

test("falls back safely without Capacitor", async () => {
  delete root.Capacitor;
  expect(isNative()).toBe(false);
  expect(platform()).toBe("web");
  expect(plugin("Camera")).toBeUndefined();
  expect(
    await whenNative(
      () => "native",
      () => "browser",
    ),
  ).toBe("browser");
});

test("exposes and invokes injected plugins", async () => {
  root.Capacitor = {
    isNativePlatform: () => true,
    getPlatform: () => "android",
    Plugins: { Haptics: { impact: async (options: unknown) => ({ options, completed: true }) } },
  };
  expect(isNative()).toBe(true);
  expect(platform()).toBe("android");
  expect(plugin("Haptics")).toBeDefined();
  expect(
    await invoke<{ options: { style: string }; completed: boolean }>("Haptics", "impact", {
      style: "MEDIUM",
    }),
  ).toEqual({
    options: { style: "MEDIUM" },
    completed: true,
  });
});

test("registered JavaScript plugin proxies take precedence", async () => {
  root.Capacitor = { isNativePlatform: () => true, Plugins: {} };
  registerPlugin("Device", { getInfo: async () => ({ model: "test" }) });
  expect(await invoke<{ model: string }>("Device", "getInfo")).toEqual({ model: "test" });
});

test("explains missing plugins", async () => {
  root.Capacitor = { isNativePlatform: () => true, Plugins: {} };
  expect(invoke("Camera", "getPhoto")).rejects.toBeInstanceOf(MobileUnavailableError);
});

test("normalizes push permission and rejects empty registrations", async () => {
  const push = new PushNotifications({
    permission: async () => "prompt",
    requestPermission: async () => "granted",
    register: async () => ({ token: "device-token", platform: "ios" }),
  });
  expect(await push.register()).toEqual({ token: "device-token", platform: "ios" });
});

test("namespaces and validates secure-storage keys", async () => {
  const values = new Map<string, string>();
  const storage = new SecureStorage({
    get: async (key) => values.get(key) ?? null,
    set: async (key, value) => void values.set(key, value),
    remove: async (key) => void values.delete(key),
  });
  await storage.set("session", "encrypted-value");
  expect(values.get("wrnexus:session")).toBe("encrypted-value");
  expect(() => storage.get("bad key")).toThrow(TypeError);
});
