import { DEFAULT_ACCOUNT_ID, normalizeAccountId } from "openclaw/plugin-sdk/account-id";
import { tryReadSecretFileSync } from "openclaw/plugin-sdk/core";
import {
  createAccountListHelpers,
  normalizeResolvedSecretInputString,
  parseOptionalDelimitedEntries,
} from "openclaw/plugin-sdk/irc";
import type { CoreConfig, IrcAccountConfig, IrcNickServConfig } from "./types.js";

const TRUTHY_ENV = new Set(["true", "1", "yes", "on"]);

export type ResolvedIrcAccount = {
  accountId: string;
  enabled: boolean;
  name?: string;
  configured: boolean;
  host: string;
  port: number;
  tls: boolean;
  nick: string;
  username: string;
  realname: string;
  password: string;
  passwordSource: "env" | "passwordFile" | "config" | "none";
  config: IrcAccountConfig;
};

function parseTruthy(value?: string): boolean {
  if (!value) {
    return false;
  }
  return TRUTHY_ENV.has(value.trim().toLowerCase());
}

function parseIntEnv(value?: string): number | undefined {
  if (!value?.trim()) {
    return undefined;
  }
  const parsed = Number.parseInt(value.trim(), 10);
  if (!Number.isFinite(parsed) || parsed <= 0 || parsed > 65535) {
    return undefined;
  }
  return parsed;
}

const { listAccountIds: listIrcAccountIds, resolveDefaultAccountId: resolveDefaultIrcAccountId } =
  createAccountListHelpers("irc", { normalizeAccountId });
export { listIrcAccountIds, resolveDefaultIrcAccountId };

function resolveAccountConfig(cfg: CoreConfig, accountId: string): IrcAccountConfig | undefined {
  const accounts = cfg.channels?.irc?.accounts;
  if (!accounts || typeof accounts !== "object") {
    return undefined;
  }
  const direct = accounts[accountId] as IrcAccountConfig | undefined;
  if (direct) {
    return direct;
  }
  const normalized = normalizeAccountId(accountId);
  const matchKey = Object.keys(accounts).find((key) => normalizeAccountId(key) === normalized);
  return matchKey ? (accounts[matchKey] as IrcAccountConfig | undefined) : undefined;
}

function mergeIrcAccountConfig(cfg: CoreConfig, accountId: string): IrcAccountConfig {
  const {
    accounts: _ignored,
    defaultAccount: _ignoredDefaultAccount,
    ...base
  } = (cfg.channels?.irc ?? {}) as IrcAccountConfig & {
    accounts?: unknown;
    defaultAccount?: unknown;
  };
  const account = resolveAccountConfig(cfg, accountId) ?? {};
  const merged: IrcAccountConfig = { ...base, ...account };
  if (base.nickserv || account.nickserv) {
    merged.nickserv = {
      ...base.nickserv,
      ...account.nickserv,
    };
  }
  return merged;
}

function resolvePassword(accountId: string, merged: IrcAccountConfig) {
  if (accountId === DEFAULT_ACCOUNT_ID) {
    const envPassword = process.env.IRC_PASSWORD?.trim();
    if (envPassword) {
      return { password: envPassword, source: "env" as const };
    }
  }

  if (merged.passwordFile?.trim()) {
    const filePassword = tryReadSecretFileSync(merged.passwordFile, "IRC password file", {
      rejectSymlink: true,
    });
    if (filePassword) {
      return { password: filePassword, source: "passwordFile" as const };
    }
  }

  const configPassword = normalizeResolvedSecretInputString({
    value: merged.password,
    path: `channels.irc.accounts.${accountId}.password`,
  });
  if (configPassword) {
    return { password: configPassword, source: "config" as const };
  }

  return { password: "", source: "none" as const };
}

function resolveNickServConfig(accountId: string, nickserv?: IrcNickServConfig): IrcNickServConfig {
  const base = nickserv ?? {};
  const envPassword =
    accountId === DEFAULT_ACCOUNT_ID ? process.env.IRC_NICKSERV_PASSWORD?.trim() : undefined;
  const envRegisterEmail =
    accountId === DEFAULT_ACCOUNT_ID ? process.env.IRC_NICKSERV_REGISTER_EMAIL?.trim() : undefined;

  const passwordFile = base.passwordFile?.trim();
  let resolvedPassword =
    normalizeResolvedSecretInputString({
      value: base.password,
      path: `channels.irc.accounts.${accountId}.nickserv.password`,
    }) ||
    envPassword ||
    "";
  if (!resolvedPassword && passwordFile) {
    resolvedPassword =
      tryReadSecretFileSync(passwordFile, "IRC NickServ password file", {
        rejectSymlink: true,
      }) ?? "";
  }

  const merged: IrcNickServConfig = {
    ...base,
    service: base.service?.trim() || undefined,
    passwordFile: passwordFile || undefined,
    password: resolvedPassword || undefined,
    registerEmail: base.registerEmail?.trim() || envRegisterEmail || undefined,
  };
  return merged;
}

export function resolveIrcAccount(params: {
  cfg: CoreConfig;
  accountId?: string | null;
}): ResolvedIrcAccount {
  const hasExplicitAccountId = Boolean(params.accountId?.trim());
  const baseEnabled = params.cfg.channels?.irc?.enabled !== false;

  const resolve = (accountId: string) => {
    const merged = mergeIrcAccountConfig(params.cfg, accountId);
    const accountEnabled = merged.enabled !== false;
    const enabled = baseEnabled && accountEnabled;

    const tls =
      typeof merged.tls === "boolean"
        ? merged.tls
        : accountId === DEFAULT_ACCOUNT_ID && process.env.IRC_TLS
          ? parseTruthy(process.env.IRC_TLS)
          : true;

    const envPort =
      accountId === DEFAULT_ACCOUNT_ID ? parseIntEnv(process.env.IRC_PORT) : undefined;
    const port = merged.port ?? envPort ?? (tls ? 6697 : 6667);
    const envChannels =
      accountId === DEFAULT_ACCOUNT_ID
        ? parseOptionalDelimitedEntries(process.env.IRC_CHANNELS)
        : undefined;

    const host = (
      merged.host?.trim() ||
      (accountId === DEFAULT_ACCOUNT_ID ? process.env.IRC_HOST?.trim() : "") ||
      ""
    ).trim();
    const nick = (
      merged.nick?.trim() ||
      (accountId === DEFAULT_ACCOUNT_ID ? process.env.IRC_NICK?.trim() : "") ||
      ""
    ).trim();
    const username = (
      merged.username?.trim() ||
      (accountId === DEFAULT_ACCOUNT_ID ? process.env.IRC_USERNAME?.trim() : "") ||
      nick ||
      "openclaw"
    ).trim();
    const realname = (
      merged.realname?.trim() ||
      (accountId === DEFAULT_ACCOUNT_ID ? process.env.IRC_REALNAME?.trim() : "") ||
      "OpenClaw"
    ).trim();

    const passwordResolution = resolvePassword(accountId, merged);
    const nickserv = resolveNickServConfig(accountId, merged.nickserv);

    const config: IrcAccountConfig = {
      ...merged,
      channels: merged.channels ?? envChannels,
      tls,
      port,
      host,
      nick,
      username,
      realname,
      nickserv,
    };

    return {
      accountId,
      enabled,
      name: merged.name?.trim() || undefined,
      configured: Boolean(host && nick),
      host,
      port,
      tls,
      nick,
      username,
      realname,
      password: passwordResolution.password,
      passwordSource: passwordResolution.source,
      config,
    } satisfies ResolvedIrcAccount;
  };

  const normalized = normalizeAccountId(params.accountId);
  const primary = resolve(normalized);
  if (hasExplicitAccountId) {
    return primary;
  }
  if (primary.configured) {
    return primary;
  }

  const fallbackId = resolveDefaultIrcAccountId(params.cfg);
  if (fallbackId === primary.accountId) {
    return primary;
  }
  const fallback = resolve(fallbackId);
  if (!fallback.configured) {
    return primary;
  }
  return fallback;
}

export function listEnabledIrcAccounts(cfg: CoreConfig): ResolvedIrcAccount[] {
  return listIrcAccountIds(cfg)
    .map((accountId) => resolveIrcAccount({ cfg, accountId }))
    .filter((account) => account.enabled);
}
