import { useEffect, useMemo, useState } from 'react';
import { AlertCircle, Bot, Link2, RefreshCw } from 'lucide-react';
import type { ChannelAccountCatalogGroup, ChannelAccountsCatalogResponse } from '../../lib/channel-types';
import { onGatewayEvent } from '../../lib/gateway-client';
import { hostApiFetch } from '../../lib/host-api';
import { isRuntimeChangedGatewayEvent, runtimeEventHasTopic } from '../../lib/runtime-events';
import { agentsStore, useAgentsStore } from '../../stores';

function cn(...tokens: Array<string | false | null | undefined>): string {
  return tokens.filter(Boolean).join(' ');
}

function formatChannelLabel(channelType: string, fallback?: string): string {
  if (fallback) return fallback;

  const parts = String(channelType ?? '')
    .split(/[-_]/)
    .map((part) => part.trim())
    .filter(Boolean);

  if (parts.length === 0) return channelType;

  return parts
    .map((part) => part.charAt(0).toUpperCase() + part.slice(1))
    .join(' ');
}

function isRecord(value: unknown): value is Record<string, unknown> {
  return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
}

function normalizeGroups(payload: unknown): ChannelAccountCatalogGroup[] {
  const groups = Array.isArray(payload)
    ? payload
    : isRecord(payload) && Array.isArray(payload.channels)
      ? payload.channels
      : [];

  return groups
    .filter((group): group is ChannelAccountCatalogGroup => isRecord(group))
    .map((group) => ({
      channelType: String(group.channelType ?? '').trim(),
      channelLabel: formatChannelLabel(String(group.channelType ?? '').trim(), String(group.channelLabel ?? '').trim() || undefined),
      defaultAccountId: String(group.defaultAccountId ?? '').trim(),
      status: group.status ?? 'connected',
      accounts: Array.isArray(group.accounts)
        ? group.accounts
          .filter((account) => isRecord(account))
          .map((account) => ({
            accountId: String(account.accountId ?? '').trim(),
            name: String(account.name ?? account.accountId ?? '').trim(),
            configured: account.configured !== false,
            status: account.status ?? 'connected',
            lastError: typeof account.lastError === 'string' ? account.lastError : undefined,
            isDefault: Boolean(account.isDefault),
            agentId: typeof account.agentId === 'string' ? account.agentId : undefined,
            bindingScope: account.bindingScope === 'account' || account.bindingScope === 'channel' ? account.bindingScope : undefined,
            channelUrl: typeof account.channelUrl === 'string' ? account.channelUrl : undefined,
          }))
          .filter((account) => account.accountId)
        : [],
    }))
    .filter((group) => group.channelType)
    .sort((left, right) => left.channelLabel.localeCompare(right.channelLabel, 'zh-CN'));
}

function resolveAgentName(agentId: string | null | undefined, agentNames: Map<string, string>): string {
  const resolvedId = String(agentId ?? '').trim();
  if (!resolvedId) return '未分配';
  return agentNames.get(resolvedId) || resolvedId;
}

export default function ChannelsPage() {
  const agents = useAgentsStore((state) => state.agents);
  const agentsLoading = useAgentsStore((state) => state.loading);
  const agentsError = useAgentsStore((state) => state.error);
  const channelOwners = useAgentsStore((state) => state.channelOwners);
  const channelAccountOwners = useAgentsStore((state) => state.channelAccountOwners);

  const [groups, setGroups] = useState<ChannelAccountCatalogGroup[]>([]);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<string | null>(null);
  const [feedback, setFeedback] = useState<string | null>(null);
  const [pendingKey, setPendingKey] = useState<string | null>(null);

  const agentNames = useMemo(
    () => new Map(agents.map((agent) => [agent.id, agent.name])),
    [agents],
  );

  async function loadChannels(): Promise<void> {
    setLoading(true);
    setError(null);

    try {
      const response = await hostApiFetch<ChannelAccountsCatalogResponse>('/api/channels/accounts');
      setGroups(normalizeGroups(response));
    } catch (requestError) {
      setError(requestError instanceof Error ? requestError.message : String(requestError));
      setGroups([]);
    } finally {
      setLoading(false);
    }
  }

  useEffect(() => {
    void Promise.all([agentsStore.init(), loadChannels()]);
  }, []);

  useEffect(() => {
    return onGatewayEvent((event) => {
      if (!isRuntimeChangedGatewayEvent(event)) return;
      if (!runtimeEventHasTopic(event, 'channels', 'agents', 'providers', 'channel-targets')) return;
      void loadChannels();
    });
  }, []);

  async function handleRefresh(): Promise<void> {
    setFeedback(null);
    await Promise.allSettled([agentsStore.refresh(), loadChannels()]);
  }

  async function handleChannelOwnerChange(channelType: string, nextOwnerId: string): Promise<void> {
    const currentOwnerId = channelOwners[channelType] || '';
    if (currentOwnerId === nextOwnerId) return;

    setPendingKey(`channel:${channelType}`);
    setFeedback(null);
    try {
      if (nextOwnerId) {
        await agentsStore.assignChannel(nextOwnerId, channelType);
        setFeedback(`${formatChannelLabel(channelType)} 的频道级归属已更新。`);
      } else if (currentOwnerId) {
        await agentsStore.removeChannel(currentOwnerId, channelType);
        setFeedback(`${formatChannelLabel(channelType)} 的频道级归属已清除。`);
      }
    } catch (requestError) {
      setFeedback(requestError instanceof Error ? requestError.message : String(requestError));
    } finally {
      setPendingKey(null);
    }
  }

  async function handleAccountOwnerChange(channelType: string, accountId: string, nextOwnerId: string): Promise<void> {
    const bindingKey = `${channelType}:${accountId}`;
    const currentOwnerId = channelAccountOwners[bindingKey] || '';
    if (currentOwnerId === nextOwnerId) return;

    setPendingKey(bindingKey);
    setFeedback(null);
    try {
      if (nextOwnerId) {
        await agentsStore.assignChannelAccount(nextOwnerId, channelType, accountId);
        setFeedback(`${formatChannelLabel(channelType)} / ${accountId} 的账号归属已更新。`);
      } else {
        await agentsStore.clearChannelBinding(channelType, accountId);
        setFeedback(`${formatChannelLabel(channelType)} / ${accountId} 的账号归属已清除。`);
      }
    } catch (requestError) {
      setFeedback(requestError instanceof Error ? requestError.message : String(requestError));
    } finally {
      setPendingKey(null);
    }
  }

  return (
    <section className="h-full w-full min-h-0">
      <div className="flex h-full w-full min-h-0 flex-col rounded-[16px] bg-white p-[20px] dark:bg-[#1b1b1d]">
        <div className="mb-[20px] flex flex-col gap-4 border-b border-[#E5E8EE] pb-[20px] dark:border-[#2a2a2d] md:flex-row md:items-end md:justify-between">
          <div className="min-w-0">
            <span className="text-[24px] font-medium leading-[32px] text-[#171717] dark:text-gray-100">
              Channels
            </span>
            <span className="block max-w-3xl pt-[3px] text-[12px] leading-[16px] text-[#99A0AE] dark:text-gray-500">
              在这里统一管理 channel/account 到 Agent 的归属，让 Agents 页面只负责模型与摘要展示，更接近 ClawX 的职责边界。
            </span>
          </div>

          <button
            type="button"
            className="inline-flex h-9 items-center gap-2 rounded-full border border-[#E5E8EE] px-4 text-[12px] text-[#525866] transition-colors hover:border-[#2B7FFF] hover:text-[#2B7FFF] disabled:cursor-not-allowed disabled:opacity-60 dark:border-[#2a2a2d] dark:text-gray-300 dark:hover:border-[#3b82f6] dark:hover:text-white"
            disabled={loading || agentsLoading}
            onClick={() => {
              void handleRefresh();
            }}
          >
            <RefreshCw className={cn('h-3.5 w-3.5', (loading || agentsLoading) && 'animate-spin')} />
            刷新
          </button>
        </div>

        <div className="min-h-0 flex-1 overflow-y-auto pb-10 pr-2">
          {feedback ? (
            <div className="mb-4 rounded-[14px] border border-[#DCE5F1] bg-[#F7FAFF] px-4 py-3 text-sm text-[#525866] dark:border-[#2a2a2d] dark:bg-[#20243a] dark:text-gray-300">
              {feedback}
            </div>
          ) : null}

          {error ? (
            <div className="mb-4 rounded-[14px] border border-red-500/30 bg-red-500/10 px-4 py-3 text-sm text-red-700 dark:text-red-300">
              <div className="flex items-start gap-3">
                <AlertCircle className="mt-0.5 h-4 w-4 shrink-0" />
                <span>{error}</span>
              </div>
            </div>
          ) : null}

          {agentsError ? (
            <div className="mb-4 rounded-[14px] border border-amber-500/30 bg-amber-500/10 px-4 py-3 text-sm text-amber-700 dark:text-amber-300">
              <div className="flex items-start gap-3">
                <AlertCircle className="mt-0.5 h-4 w-4 shrink-0" />
                <span>{`Agents 数据加载失败：${agentsError}`}</span>
              </div>
            </div>
          ) : null}

          {loading && groups.length === 0 ? (
            <div className="flex h-full items-center justify-center rounded-[16px] bg-[#FAFBFC] text-sm text-[#99A0AE] dark:bg-[#202024] dark:text-gray-500">
              正在加载渠道目录...
            </div>
          ) : groups.length === 0 ? (
            <div className="rounded-[16px] border border-dashed border-[#DCE5F1] bg-[#FAFBFC] px-4 py-6 text-sm text-[#525866] dark:border-[#2a2a2d] dark:bg-[#202024] dark:text-gray-300">
              <div className="text-[15px] font-medium text-[#171717] dark:text-gray-100">暂无已配置渠道</div>
              <div className="mt-1 text-[13px] leading-[20px] text-[#99A0AE] dark:text-gray-500">
                先在首页任务中心里配置可用渠道，这里就会出现对应的 channel/account 归属入口。
              </div>
            </div>
          ) : (
            <div className="space-y-4">
              {groups.map((group) => {
                const channelOwnerId = channelOwners[group.channelType] || '';
                const channelPending = pendingKey === `channel:${group.channelType}`;

                return (
                  <article
                    key={group.channelType}
                    className="rounded-[18px] border border-[#E5E8EE] bg-[#FAFBFC] p-5 shadow-[0_14px_40px_rgba(15,23,42,0.04)] dark:border-[#2a2a2d] dark:bg-[#1f1f22]"
                  >
                    <div className="flex flex-col gap-4 lg:flex-row lg:items-start lg:justify-between">
                      <div>
                        <div className="flex items-center gap-2">
                          <div className="flex h-10 w-10 items-center justify-center rounded-full bg-[#EFF6FF] text-[#2B7FFF] dark:bg-[#1d2633] dark:text-[#93c5fd]">
                            <Link2 className="h-4 w-4" />
                          </div>
                          <div>
                            <div className="text-[16px] font-semibold text-[#171717] dark:text-gray-100">
                              {group.channelLabel}
                            </div>
                            <div className="mt-1 text-[12px] text-[#99A0AE] dark:text-gray-500">
                              {group.channelType}
                            </div>
                          </div>
                        </div>
                        <div className="mt-3 rounded-[12px] border border-dashed border-[#DCE5F1] bg-white px-3 py-2 text-[12px] leading-[18px] text-[#525866] dark:border-[#2a2a2d] dark:bg-[#17171a] dark:text-gray-300">
                          频道级兜底归属会在账号没有单独指定 Agent 时生效。
                        </div>
                      </div>

                      <div className="w-full lg:max-w-[280px]">
                        <div className="text-[12px] font-medium text-[#525866] dark:text-gray-300">频道级归属</div>
                        <select
                          value={channelOwnerId}
                          disabled={channelPending || agents.length === 0}
                          onChange={(event) => {
                            void handleChannelOwnerChange(group.channelType, event.target.value);
                          }}
                          className="mt-2 w-full rounded-[12px] border border-[#E5E8EE] bg-white px-3 py-2 text-[13px] text-[#171717] outline-none transition-colors focus:border-[#2B7FFF] disabled:cursor-not-allowed disabled:opacity-60 dark:border-[#2a2a2d] dark:bg-[#101013] dark:text-gray-100"
                        >
                          <option value="">未分配</option>
                          {agents.map((agent) => (
                            <option key={agent.id} value={agent.id}>
                              {agent.name}
                            </option>
                          ))}
                        </select>
                      </div>
                    </div>

                    <div className="mt-4 space-y-3">
                      {group.accounts.map((account) => {
                        const bindingKey = `${group.channelType}:${account.accountId}`;
                        const explicitOwnerId = channelAccountOwners[bindingKey] || '';
                        const effectiveOwnerId = explicitOwnerId || channelOwnerId;
                        const bindingScope = explicitOwnerId ? '账号归属' : channelOwnerId ? '继承频道' : '未分配';
                        const accountPending = pendingKey === bindingKey;

                        return (
                          <div
                            key={bindingKey}
                            className="rounded-[14px] border border-[#E5E8EE] bg-white px-4 py-4 dark:border-[#2a2a2d] dark:bg-[#17171a]"
                          >
                            <div className="flex flex-col gap-4 lg:flex-row lg:items-start lg:justify-between">
                              <div className="min-w-0">
                                <div className="flex flex-wrap items-center gap-2">
                                  <div className="truncate text-[14px] font-semibold text-[#171717] dark:text-gray-100">
                                    {account.name || account.accountId}
                                  </div>
                                  {account.isDefault ? (
                                    <span className="rounded-full bg-[#EFF6FF] px-2.5 py-1 text-[11px] font-medium text-[#2B7FFF] dark:bg-[#1d2633] dark:text-[#93c5fd]">
                                      默认账号
                                    </span>
                                  ) : null}
                                  <span className="rounded-full border border-[#E5E8EE] px-2.5 py-1 text-[11px] text-[#525866] dark:border-[#2a2a2d] dark:text-gray-300">
                                    {bindingScope}
                                  </span>
                                </div>
                                <div className="mt-2 flex flex-wrap items-center gap-3 text-[12px] text-[#99A0AE] dark:text-gray-500">
                                  <span>{`当前归属：${resolveAgentName(effectiveOwnerId, agentNames)}`}</span>
                                  {account.channelUrl ? <span className="truncate">{account.channelUrl}</span> : null}
                                </div>
                              </div>

                              <div className="w-full lg:max-w-[280px]">
                                <div className="flex items-center gap-2 text-[12px] font-medium text-[#525866] dark:text-gray-300">
                                  <Bot className="h-3.5 w-3.5" />
                                  账号归属
                                </div>
                                <select
                                  value={explicitOwnerId}
                                  disabled={accountPending || agents.length === 0}
                                  onChange={(event) => {
                                    void handleAccountOwnerChange(group.channelType, account.accountId, event.target.value);
                                  }}
                                  className="mt-2 w-full rounded-[12px] border border-[#E5E8EE] bg-white px-3 py-2 text-[13px] text-[#171717] outline-none transition-colors focus:border-[#2B7FFF] disabled:cursor-not-allowed disabled:opacity-60 dark:border-[#2a2a2d] dark:bg-[#101013] dark:text-gray-100"
                                >
                                  <option value="">跟随频道级归属</option>
                                  {agents.map((agent) => (
                                    <option key={agent.id} value={agent.id}>
                                      {agent.name}
                                    </option>
                                  ))}
                                </select>
                              </div>
                            </div>
                          </div>
                        );
                      })}
                    </div>
                  </article>
                );
              })}
            </div>
          )}
        </div>
      </div>
    </section>
  );
}
