import type { OpenClawConfig, PluginRuntime, ResolvedLineAccount } from "openclaw/plugin-sdk/line";
import { beforeEach, describe, expect, it, vi } from "vitest";
import { createRuntimeEnv } from "../../test-utils/runtime-env.js";
import { linePlugin } from "./channel.js";
import { setLineRuntime } from "./runtime.js";

const DEFAULT_ACCOUNT_ID = "default";

type LineRuntimeMocks = {
  writeConfigFile: ReturnType<typeof vi.fn>;
  resolveLineAccount: ReturnType<typeof vi.fn>;
};

function createRuntime(): { runtime: PluginRuntime; mocks: LineRuntimeMocks } {
  const writeConfigFile = vi.fn(async () => {});
  const resolveLineAccount = vi.fn(
    ({ cfg, accountId }: { cfg: OpenClawConfig; accountId?: string }) => {
      const lineConfig = (cfg.channels?.line ?? {}) as {
        tokenFile?: string;
        secretFile?: string;
        channelAccessToken?: string;
        channelSecret?: string;
        accounts?: Record<string, Record<string, unknown>>;
      };
      const entry =
        accountId && accountId !== DEFAULT_ACCOUNT_ID
          ? (lineConfig.accounts?.[accountId] ?? {})
          : lineConfig;
      const hasToken =
        // oxlint-disable-next-line typescript/no-explicit-any
        Boolean((entry as any).channelAccessToken) || Boolean((entry as any).tokenFile);
      // oxlint-disable-next-line typescript/no-explicit-any
      const hasSecret = Boolean((entry as any).channelSecret) || Boolean((entry as any).secretFile);
      return { tokenSource: hasToken && hasSecret ? "config" : "none" };
    },
  );

  const runtime = {
    config: { writeConfigFile },
    channel: { line: { resolveLineAccount } },
  } as unknown as PluginRuntime;

  return { runtime, mocks: { writeConfigFile, resolveLineAccount } };
}

function resolveAccount(
  resolveLineAccount: LineRuntimeMocks["resolveLineAccount"],
  cfg: OpenClawConfig,
  accountId: string,
): ResolvedLineAccount {
  const resolver = resolveLineAccount as unknown as (params: {
    cfg: OpenClawConfig;
    accountId?: string;
  }) => ResolvedLineAccount;
  return resolver({ cfg, accountId });
}

async function runLogoutScenario(params: { cfg: OpenClawConfig; accountId: string }): Promise<{
  result: Awaited<ReturnType<NonNullable<NonNullable<typeof linePlugin.gateway>["logoutAccount"]>>>;
  mocks: LineRuntimeMocks;
}> {
  const { runtime, mocks } = createRuntime();
  setLineRuntime(runtime);
  const account = resolveAccount(mocks.resolveLineAccount, params.cfg, params.accountId);
  const result = await linePlugin.gateway!.logoutAccount!({
    accountId: params.accountId,
    cfg: params.cfg,
    account,
    runtime: createRuntimeEnv(),
  });
  return { result, mocks };
}

describe("linePlugin gateway.logoutAccount", () => {
  beforeEach(() => {
    setLineRuntime(createRuntime().runtime);
  });

  it("clears tokenFile/secretFile on default account logout", async () => {
    const cfg: OpenClawConfig = {
      channels: {
        line: {
          tokenFile: "/tmp/token",
          secretFile: "/tmp/secret",
        },
      },
    };
    const { result, mocks } = await runLogoutScenario({
      cfg,
      accountId: DEFAULT_ACCOUNT_ID,
    });

    expect(result.cleared).toBe(true);
    expect(result.loggedOut).toBe(true);
    expect(mocks.writeConfigFile).toHaveBeenCalledWith({});
  });

  it("clears tokenFile/secretFile on account logout", async () => {
    const cfg: OpenClawConfig = {
      channels: {
        line: {
          accounts: {
            primary: {
              tokenFile: "/tmp/token",
              secretFile: "/tmp/secret",
            },
          },
        },
      },
    };
    const { result, mocks } = await runLogoutScenario({
      cfg,
      accountId: "primary",
    });

    expect(result.cleared).toBe(true);
    expect(result.loggedOut).toBe(true);
    expect(mocks.writeConfigFile).toHaveBeenCalledWith({});
  });

  it("does not write config when account has no token/secret fields", async () => {
    const cfg: OpenClawConfig = {
      channels: {
        line: {
          accounts: {
            primary: {
              name: "Primary",
            },
          },
        },
      },
    };
    const { result, mocks } = await runLogoutScenario({
      cfg,
      accountId: "primary",
    });

    expect(result.cleared).toBe(false);
    expect(result.loggedOut).toBe(true);
    expect(mocks.writeConfigFile).not.toHaveBeenCalled();
  });
});
