import { isChannelVisibleInConfiguredLists } from "../channels/plugins/exposure.js";
import { resolveChannelDefaultAccountId } from "../channels/plugins/helpers.js";
import {
  getChannelPlugin,
  listChannelPlugins,
  normalizeChannelId,
} from "../channels/plugins/index.js";
import type { ChannelId } from "../channels/plugins/types.js";
import type { OpenClawConfig } from "../config/config.js";
import type { AgentBinding } from "../config/types.js";
import { DEFAULT_ACCOUNT_ID } from "../routing/session-key.js";

type ProviderAccountStatus = {
  provider: ChannelId;
  accountId: string;
  name?: string;
  state: "linked" | "not linked" | "configured" | "not configured" | "enabled" | "disabled";
  enabled?: boolean;
  configured?: boolean;
};

function providerAccountKey(provider: ChannelId, accountId?: string) {
  return `${provider}:${accountId ?? DEFAULT_ACCOUNT_ID}`;
}

function formatChannelAccountLabel(params: {
  provider: ChannelId;
  accountId: string;
  name?: string;
}): string {
  const label = getChannelPlugin(params.provider)?.meta.label ?? params.provider;
  const account = params.name?.trim()
    ? `${params.accountId} (${params.name.trim()})`
    : params.accountId;
  return `${label} ${account}`;
}

function formatProviderState(entry: ProviderAccountStatus): string {
  const parts = [entry.state];
  if (entry.enabled === false && entry.state !== "disabled") {
    parts.push("disabled");
  }
  return parts.join(", ");
}

export async function buildProviderStatusIndex(
  cfg: OpenClawConfig,
): Promise<Map<string, ProviderAccountStatus>> {
  const map = new Map<string, ProviderAccountStatus>();

  for (const plugin of listChannelPlugins()) {
    const accountIds = plugin.config.listAccountIds(cfg);
    for (const accountId of accountIds) {
      const account = plugin.config.resolveAccount(cfg, accountId);
      const snapshot = plugin.config.describeAccount?.(account, cfg);
      const enabled = plugin.config.isEnabled
        ? plugin.config.isEnabled(account, cfg)
        : typeof snapshot?.enabled === "boolean"
          ? snapshot.enabled
          : (account as { enabled?: boolean }).enabled;
      const configured = plugin.config.isConfigured
        ? await plugin.config.isConfigured(account, cfg)
        : snapshot?.configured;
      const resolvedEnabled = typeof enabled === "boolean" ? enabled : true;
      const resolvedConfigured = typeof configured === "boolean" ? configured : true;
      const state =
        plugin.status?.resolveAccountState?.({
          account,
          cfg,
          configured: resolvedConfigured,
          enabled: resolvedEnabled,
        }) ??
        (typeof snapshot?.linked === "boolean"
          ? snapshot.linked
            ? "linked"
            : "not linked"
          : resolvedConfigured
            ? "configured"
            : "not configured");
      const name = snapshot?.name ?? (account as { name?: string }).name;
      map.set(providerAccountKey(plugin.id, accountId), {
        provider: plugin.id,
        accountId,
        name,
        state,
        enabled,
        configured,
      });
    }
  }

  return map;
}

function resolveDefaultAccountId(cfg: OpenClawConfig, provider: ChannelId): string {
  const plugin = getChannelPlugin(provider);
  if (!plugin) {
    return DEFAULT_ACCOUNT_ID;
  }
  return resolveChannelDefaultAccountId({ plugin, cfg });
}

function shouldShowProviderEntry(entry: ProviderAccountStatus, cfg: OpenClawConfig): boolean {
  const plugin = getChannelPlugin(entry.provider);
  if (!plugin) {
    return Boolean(entry.configured);
  }
  if (!isChannelVisibleInConfiguredLists(plugin.meta)) {
    const providerConfig = (cfg as Record<string, unknown>)[plugin.id];
    return Boolean(entry.configured) || Boolean(providerConfig);
  }
  return Boolean(entry.configured);
}

function formatProviderEntry(entry: ProviderAccountStatus): string {
  const label = formatChannelAccountLabel({
    provider: entry.provider,
    accountId: entry.accountId,
    name: entry.name,
  });
  return `${label}: ${formatProviderState(entry)}`;
}

export function summarizeBindings(cfg: OpenClawConfig, bindings: AgentBinding[]): string[] {
  if (bindings.length === 0) {
    return [];
  }
  const seen = new Map<string, string>();
  for (const binding of bindings) {
    const channel = normalizeChannelId(binding.match.channel);
    if (!channel) {
      continue;
    }
    const accountId = binding.match.accountId ?? resolveDefaultAccountId(cfg, channel);
    const key = providerAccountKey(channel, accountId);
    if (!seen.has(key)) {
      const label = formatChannelAccountLabel({
        provider: channel,
        accountId,
      });
      seen.set(key, label);
    }
  }
  return [...seen.values()];
}

export function listProvidersForAgent(params: {
  summaryIsDefault: boolean;
  cfg: OpenClawConfig;
  bindings: AgentBinding[];
  providerStatus: Map<string, ProviderAccountStatus>;
}): string[] {
  const allProviderEntries = [...params.providerStatus.values()];
  const providerLines: string[] = [];
  if (params.bindings.length > 0) {
    const seen = new Set<string>();
    for (const binding of params.bindings) {
      const channel = normalizeChannelId(binding.match.channel);
      if (!channel) {
        continue;
      }
      const accountId = binding.match.accountId ?? resolveDefaultAccountId(params.cfg, channel);
      const key = providerAccountKey(channel, accountId);
      if (seen.has(key)) {
        continue;
      }
      seen.add(key);
      const status = params.providerStatus.get(key);
      if (status) {
        providerLines.push(formatProviderEntry(status));
      } else {
        providerLines.push(
          `${formatChannelAccountLabel({ provider: channel, accountId })}: unknown`,
        );
      }
    }
    return providerLines;
  }

  if (params.summaryIsDefault) {
    for (const entry of allProviderEntries) {
      if (shouldShowProviderEntry(entry, params.cfg)) {
        providerLines.push(formatProviderEntry(entry));
      }
    }
  }

  return providerLines;
}
