import { useEffect, useMemo, useState } from 'react';
import { ChevronDown, ChevronRight, Trash2 } from 'lucide-react';
import type {
  GatewayAccessEffect,
  GatewayAPIKeyAccessRuleDiagnostic,
  GatewayAccessRule,
  GatewayAccessRuleBatchRequest,
  GatewayAccessRuleResourceRequest,
  GatewayAccessResourceType,
  GatewayAccessSubjectType,
  IntegrationPlatform,
  PlatformModel,
} from '@easyai-ai-gateway/contracts';
import { Badge, Button, Card, CardContent, CardHeader, CardTitle, Checkbox, Input } from '../../components/ui';
import type { LoadState } from '../../types';

type Effect = Extract<GatewayAccessEffect, 'allow' | 'deny'>;
type ResourceType = GatewayAccessResourceType;
type ResourceKey = `${ResourceType}:${string}`;

type PlatformNode = {
  id: string;
  name: string;
  subtitle: string;
  models: Array<{
    id: string;
    name: string;
    subtitle: string;
  }>;
};

export function AccessPermissionEditor(props: {
  accessRules: GatewayAccessRule[];
  emptySubjectText?: string;
  metadataMode?: string;
  platformModels: PlatformModel[];
  platforms: IntegrationPlatform[];
  ruleDiagnostics?: GatewayAPIKeyAccessRuleDiagnostic[];
  state: LoadState;
  subjectId: string;
  subjectType: Extract<GatewayAccessSubjectType, 'user_group' | 'api_key'>;
  onBatchAccessRules: (input: GatewayAccessRuleBatchRequest) => Promise<void>;
}) {
  const [allowSearch, setAllowSearch] = useState('');
  const [denySearch, setDenySearch] = useState('');
  const [allowExpanded, setAllowExpanded] = useState<Set<string>>(() => new Set(props.platforms.map((item) => item.id)));
  const [denyExpanded, setDenyExpanded] = useState<Set<string>>(() => new Set(props.platforms.map((item) => item.id)));
  const [localError, setLocalError] = useState('');
  const [cleaningIneffectiveRules, setCleaningIneffectiveRules] = useState(false);

  const platformTree = useMemo(() => buildPlatformTree(props.platforms, props.platformModels), [props.platformModels, props.platforms]);
  const subjectRules = useMemo(
    () => props.accessRules.filter((rule) => rule.subjectType === props.subjectType && rule.subjectId === props.subjectId && rule.status === 'active'),
    [props.accessRules, props.subjectId, props.subjectType],
  );
  const ruleByEffectAndResource = useMemo(() => buildRuleIndex(subjectRules), [subjectRules]);
  const ineffectiveRules = useMemo(
    () => (props.ruleDiagnostics ?? []).filter((diagnostic) => !diagnostic.effective),
    [props.ruleDiagnostics],
  );
  const allowTree = useMemo(() => filterTree(platformTree, allowSearch), [allowSearch, platformTree]);
  const denyTree = useMemo(() => filterTree(platformTree, denySearch), [denySearch, platformTree]);

  useEffect(() => {
    setAllowExpanded((current) => mergeExpanded(current, props.platforms));
    setDenyExpanded((current) => mergeExpanded(current, props.platforms));
  }, [props.platforms]);

  async function setPermission(effect: Effect, resourceType: ResourceType, resourceId: string, enabled: boolean) {
    const resourceKey = makeResourceKey(resourceType, resourceId);
    await applyPermissionBatch(effect, enabled ? [resourceKey] : [], enabled ? [] : [resourceKey]);
  }

  async function setPlatformPermission(effect: Effect, platform: PlatformNode, enabled: boolean) {
    const modelKeys = platform.models.map((model) => makeResourceKey('platform_model', model.id));
    if (enabled && props.subjectType === 'api_key') {
      await batchApply(effect, modelKeys, true);
      return;
    }
    await batchApply(effect, [makeResourceKey('platform', platform.id), ...modelKeys], enabled);
  }

  async function selectAll(effect: Effect, nodes: PlatformNode[]) {
    await batchApply(effect, visibleResourceKeys(nodes, props.subjectType !== 'api_key'), true);
  }

  async function reverseVisible(effect: Effect, nodes: PlatformNode[]) {
    const keys = visibleResourceKeys(nodes, props.subjectType !== 'api_key');
    const upsertKeys: ResourceKey[] = [];
    const deleteKeys: ResourceKey[] = [];
    for (const key of keys) {
      const checked = Boolean(ruleByEffectAndResource.get(`${effect}:${key}`));
      if (checked) deleteKeys.push(key);
      else upsertKeys.push(key);
    }
    await applyPermissionBatch(effect, upsertKeys, deleteKeys);
  }

  async function removeIneffectiveRule(diagnostic: GatewayAPIKeyAccessRuleDiagnostic) {
    if (diagnostic.effect !== 'allow' && diagnostic.effect !== 'deny') return;
    await applyPermissionBatch(
      diagnostic.effect,
      [],
      [makeResourceKey(diagnostic.resourceType as ResourceType, diagnostic.resourceId)],
      '失效规则移除失败',
    );
  }

  async function clearIneffectiveRules() {
    if (!props.subjectId || ineffectiveRules.length === 0) return;
    setLocalError('');
    setCleaningIneffectiveRules(true);
    try {
      for (const batch of ineffectiveRuleCleanupBatches(props.subjectType, props.subjectId, ineffectiveRules)) {
        await props.onBatchAccessRules(batch);
      }
    } catch (err) {
      setLocalError(err instanceof Error ? err.message : '失效规则一键清理失败');
    } finally {
      setCleaningIneffectiveRules(false);
    }
  }

  async function clearEffect(effect: Effect) {
    const keys = subjectRules
      .filter((rule) => rule.effect === effect)
      .map(resourceKeyFromRule)
      .filter((key): key is ResourceKey => Boolean(key));
    await applyPermissionBatch(effect, [], keys, '访问权限清空失败');
  }

  async function batchApply(effect: Effect, resourceKeys: ResourceKey[], enabled: boolean) {
    await applyPermissionBatch(effect, enabled ? resourceKeys : [], enabled ? [] : resourceKeys);
  }

  async function applyPermissionBatch(
    effect: Effect,
    upsertKeys: ResourceKey[],
    deleteKeys: ResourceKey[],
    errorMessage = '访问权限更新失败',
  ) {
    if (!props.subjectId) return;
    const upsertResources = dedupeResourceKeys(upsertKeys).map((key) => createPermissionResource(effect, key, props.metadataMode));
    const deleteResources = dedupeResourceKeys(deleteKeys).map(resourceRequestFromKey);
    if (upsertResources.length === 0 && deleteResources.length === 0) return;
    setLocalError('');
    try {
      await props.onBatchAccessRules({
        subjectType: props.subjectType,
        subjectId: props.subjectId,
        effect,
        upsertResources,
        deleteResources,
      });
    } catch (err) {
      setLocalError(err instanceof Error ? err.message : errorMessage);
    }
  }

  const allowSummary = countEffectRules(subjectRules, 'allow');
  const denySummary = countEffectRules(subjectRules, 'deny');
  const allowConfigured = allowSummary.platforms + allowSummary.models > 0;

  if (!props.subjectId) {
    return (
      <div className="emptyState">
        <strong>{props.emptySubjectText ?? '请选择维护对象'}</strong>
      </div>
    );
  }

  return (
    <div className="accessEditorStack">
      {localError && <p className="formMessage">{localError}</p>}
      {ineffectiveRules.length > 0 && (
        <section className="accessRuleDiagnostics" aria-label="失效 API Key 权限规则">
          <div>
            <strong>{ineffectiveRules.length} 条规则当前不生效</strong>
            <span>规则已保留，不会扩大权限或阻断其他 API Key。</span>
            <Button
              type="button"
              className="accessRuleCleanupButton"
              variant="outline"
              size="sm"
              disabled={props.state === 'loading' || cleaningIneffectiveRules}
              onClick={() => void clearIneffectiveRules()}
            >
              <Trash2 size={14} />{cleaningIneffectiveRules ? '清理中' : '一键清理'}
            </Button>
          </div>
          {ineffectiveRules.map((diagnostic) => (
            <div className="accessRuleDiagnosticRow" key={diagnostic.ruleId}>
              <span>
                <Badge variant="outline">{diagnostic.effect === 'allow' ? '允许' : '拒绝'}</Badge>
                <strong>{diagnostic.resourceName || diagnostic.resourceId}</strong>
                <small>{accessRuleDiagnosticReason(diagnostic.reason)}</small>
              </span>
              <Button type="button" variant="outline" size="sm" disabled={props.state === 'loading' || cleaningIneffectiveRules} onClick={() => void removeIneffectiveRule(diagnostic)}>
                移除规则
              </Button>
            </div>
          ))}
        </section>
      )}
      <div className="accessGroupHint accessWhitelistHint">
        <strong>{allowConfigured ? '已配置白名单' : '未配置白名单'}</strong>
        <span>{allowConfigured ? '仅允许所选资源；拒绝规则始终优先。' : '继承上级可用范围；拒绝规则仍会从中排除资源。'}</span>
      </div>
      <section className="accessPermissionGrid">
        <PermissionTreePanel
          clearLabel="清空允许"
          emptyText="暂无可维护的平台模型"
          effect="allow"
          expanded={allowExpanded}
          modelOnlySelection={props.subjectType === 'api_key'}
          rules={ruleByEffectAndResource}
          search={allowSearch}
          state={props.state}
          summary={allowSummary}
          title="允许使用（白名单）"
          tree={allowTree}
          onClear={() => void clearEffect('allow')}
          onExpandAll={() => setAllowExpanded(new Set(platformTree.map((item) => item.id)))}
          onFoldAll={() => setAllowExpanded(new Set())}
          onReverse={() => void reverseVisible('allow', allowTree)}
          onSearchChange={setAllowSearch}
          onSelectAll={() => void selectAll('allow', allowTree)}
          onToggleExpanded={(platformId) => setAllowExpanded(toggleSet(allowExpanded, platformId))}
          onTogglePlatformPermission={setPlatformPermission}
          onTogglePermission={setPermission}
        />
        <PermissionTreePanel
          clearLabel="清空拒绝"
          emptyText="暂无可维护的平台模型"
          effect="deny"
          expanded={denyExpanded}
          modelOnlySelection={props.subjectType === 'api_key'}
          rules={ruleByEffectAndResource}
          search={denySearch}
          state={props.state}
          summary={denySummary}
          title="不允许使用（平台/模型）"
          tree={denyTree}
          onClear={() => void clearEffect('deny')}
          onExpandAll={() => setDenyExpanded(new Set(platformTree.map((item) => item.id)))}
          onFoldAll={() => setDenyExpanded(new Set())}
          onReverse={() => void reverseVisible('deny', denyTree)}
          onSearchChange={setDenySearch}
          onSelectAll={() => void selectAll('deny', denyTree)}
          onToggleExpanded={(platformId) => setDenyExpanded(toggleSet(denyExpanded, platformId))}
          onTogglePlatformPermission={setPlatformPermission}
          onTogglePermission={setPermission}
        />
      </section>
    </div>
  );
}

export function ineffectiveRuleCleanupBatches(
  subjectType: Extract<GatewayAccessSubjectType, 'user_group' | 'api_key'>,
  subjectId: string,
  diagnostics: GatewayAPIKeyAccessRuleDiagnostic[],
): GatewayAccessRuleBatchRequest[] {
  const batches: GatewayAccessRuleBatchRequest[] = [];
  for (const effect of ['allow', 'deny'] as Effect[]) {
    const keys = diagnostics
      .filter((diagnostic) => !diagnostic.effective && diagnostic.effect === effect)
      .map((diagnostic) => makeResourceKey(diagnostic.resourceType as ResourceType, diagnostic.resourceId));
    const deleteResources = dedupeResourceKeys(keys).map(resourceRequestFromKey);
    if (deleteResources.length === 0) continue;
    batches.push({ subjectType, subjectId, effect, upsertResources: [], deleteResources });
  }
  return batches;
}

function PermissionTreePanel(props: {
  clearLabel: string;
  effect: Effect;
  emptyText: string;
  expanded: Set<string>;
  modelOnlySelection: boolean;
  rules: Map<string, GatewayAccessRule>;
  search: string;
  state: LoadState;
  summary: { platforms: number; models: number };
  title: string;
  tree: PlatformNode[];
  onClear: () => void;
  onExpandAll: () => void;
  onFoldAll: () => void;
  onReverse: () => void;
  onSearchChange: (value: string) => void;
  onSelectAll: () => void;
  onToggleExpanded: (platformId: string) => void;
  onTogglePlatformPermission: (effect: Effect, platform: PlatformNode, enabled: boolean) => void;
  onTogglePermission: (effect: Effect, resourceType: ResourceType, resourceId: string, enabled: boolean) => void;
}) {
  return (
    <Card className="accessPermissionPanel">
      <CardHeader>
        <div>
          <CardTitle>{props.title}</CardTitle>
          <p className="mutedText">{props.summary.platforms} 个平台 / {props.summary.models} 个模型</p>
        </div>
      </CardHeader>
      <CardContent>
        <div className="accessTreeToolbar">
          <Input size="sm" value={props.search} placeholder="筛选平台/模型" onChange={(event) => props.onSearchChange(event.target.value)} />
          <Button type="button" variant="outline" size="sm" onClick={props.onExpandAll}>展开</Button>
          <Button type="button" variant="outline" size="sm" onClick={props.onFoldAll}>折叠</Button>
          <Button type="button" variant="outline" size="sm" disabled={props.state === 'loading'} onClick={props.onSelectAll}>全选</Button>
          <Button type="button" variant="outline" size="sm" disabled={props.state === 'loading'} onClick={props.onReverse}>反选</Button>
          <Button type="button" variant="outline" size="sm" disabled={props.state === 'loading'} onClick={props.onClear}>{props.clearLabel}</Button>
        </div>
        <div className="accessTreeBox">
          {props.tree.length ? props.tree.map((platform) => (
            <PlatformPermissionNode
              effect={props.effect}
              expanded={props.expanded.has(platform.id)}
              key={platform.id}
              platform={platform}
              rules={props.rules}
              modelOnlySelection={props.modelOnlySelection}
              onToggleExpanded={props.onToggleExpanded}
              onTogglePlatformPermission={props.onTogglePlatformPermission}
              onTogglePermission={props.onTogglePermission}
            />
          )) : <span className="accessTreeEmpty">{props.emptyText}</span>}
        </div>
      </CardContent>
    </Card>
  );
}

function PlatformPermissionNode(props: {
  effect: Effect;
  expanded: boolean;
  platform: PlatformNode;
  rules: Map<string, GatewayAccessRule>;
  modelOnlySelection: boolean;
  onToggleExpanded: (platformId: string) => void;
  onTogglePlatformPermission: (effect: Effect, platform: PlatformNode, enabled: boolean) => void;
  onTogglePermission: (effect: Effect, resourceType: ResourceType, resourceId: string, enabled: boolean) => void;
}) {
  const platformRuleKey = `${props.effect}:${makeResourceKey('platform', props.platform.id)}`;
  const checkedModels = props.platform.models.filter((model) => props.rules.has(`${props.effect}:${makeResourceKey('platform_model', model.id)}`)).length;
  const platformChecked = props.rules.has(platformRuleKey);
  const platformState = props.modelOnlySelection
    ? checkedModels > 0 && checkedModels === props.platform.models.length
      ? true
      : checkedModels > 0 || platformChecked
        ? 'indeterminate'
        : false
    : platformChecked
      ? true
      : checkedModels > 0
        ? 'indeterminate'
        : false;
  return (
    <div className="accessTreeNode">
      <div className="accessTreeRow">
        <button type="button" className="accessTreeExpand" onClick={() => props.onToggleExpanded(props.platform.id)}>
          {props.expanded ? <ChevronDown size={14} /> : <ChevronRight size={14} />}
        </button>
        <Checkbox
          checked={platformState}
          onCheckedChange={(checked) => props.onTogglePlatformPermission(props.effect, props.platform, checked === true)}
        />
        <span title={props.platform.subtitle || props.platform.name}>{props.platform.name}</span>
        {checkedModels > 0 && <Badge variant="secondary">{checkedModels}</Badge>}
      </div>
      {props.expanded && props.platform.models.length > 0 && (
        <div className="accessTreeChildren">
          {props.platform.models.map((model) => {
            const modelKey = `${props.effect}:${makeResourceKey('platform_model', model.id)}`;
            return (
              <label className="accessTreeRow accessTreeModel" key={model.id}>
                <span />
                <Checkbox
                  checked={props.rules.has(modelKey)}
                  onCheckedChange={(checked) => props.onTogglePermission(props.effect, 'platform_model', model.id, checked === true)}
                />
                <span title={model.subtitle || model.name}>{model.name}</span>
              </label>
            );
          })}
        </div>
      )}
    </div>
  );
}

function buildPlatformTree(platforms: IntegrationPlatform[], platformModels: PlatformModel[]): PlatformNode[] {
  const modelsByPlatform = new Map<string, PlatformModel[]>();
  for (const model of platformModels) {
    const current = modelsByPlatform.get(model.platformId) ?? [];
    current.push(model);
    modelsByPlatform.set(model.platformId, current);
  }
  return platforms.map((platform) => ({
    id: platform.id,
    name: platform.internalName || platform.name,
    subtitle: `${platform.provider} / ${platform.platformKey}`,
    models: (modelsByPlatform.get(platform.id) ?? [])
      .slice()
      .sort((a, b) => modelLabel(a).localeCompare(modelLabel(b)))
      .map((model) => ({
        id: model.id,
        name: modelLabel(model),
        subtitle: `${model.modelType.join(', ')} / ${model.modelName}`,
      })),
  })).sort((a, b) => a.name.localeCompare(b.name));
}

function filterTree(tree: PlatformNode[], keyword: string): PlatformNode[] {
  const normalized = keyword.trim().toLowerCase();
  if (!normalized) return tree;
  return tree.flatMap((platform) => {
    const platformMatched = `${platform.name} ${platform.subtitle}`.toLowerCase().includes(normalized);
    const models = platformMatched
      ? platform.models
      : platform.models.filter((model) => `${model.name} ${model.subtitle}`.toLowerCase().includes(normalized));
    return platformMatched || models.length ? [{ ...platform, models }] : [];
  });
}

function buildRuleIndex(rules: GatewayAccessRule[]) {
  const index = new Map<string, GatewayAccessRule>();
  for (const rule of rules) {
    if (rule.resourceType !== 'platform' && rule.resourceType !== 'platform_model') continue;
    if (rule.effect !== 'allow' && rule.effect !== 'deny') continue;
    index.set(`${rule.effect}:${makeResourceKey(rule.resourceType, rule.resourceId)}`, rule);
  }
  return index;
}

export function countAccessPermissionRules(
  rules: GatewayAccessRule[],
  subjectType: Extract<GatewayAccessSubjectType, 'user_group' | 'api_key'>,
  subjectId: string,
) {
  const subjectRules = rules.filter((rule) => rule.subjectType === subjectType && rule.subjectId === subjectId && rule.status === 'active');
  return {
    allow: countEffectRules(subjectRules, 'allow'),
    deny: countEffectRules(subjectRules, 'deny'),
  };
}

function countEffectRules(rules: GatewayAccessRule[], effect: Effect) {
  return rules.reduce((summary, rule) => {
    if (rule.effect !== effect) return summary;
    if (rule.resourceType === 'platform') summary.platforms += 1;
    if (rule.resourceType === 'platform_model') summary.models += 1;
    return summary;
  }, { platforms: 0, models: 0 });
}

function visibleResourceKeys(nodes: PlatformNode[], includePlatforms: boolean): ResourceKey[] {
  return nodes.flatMap((platform) => [
    ...(includePlatforms ? [makeResourceKey('platform', platform.id)] : []),
    ...platform.models.map((model) => makeResourceKey('platform_model', model.id)),
  ]);
}

function createPermissionResource(
  effect: Effect,
  resourceKey: ResourceKey,
  metadataMode = 'access_permission_tree',
): GatewayAccessRuleResourceRequest {
  const [resourceType, resourceId] = splitResourceKey(resourceKey);
  return {
    resourceType,
    resourceId,
    priority: effect === 'deny' ? 10 : 100,
    minPermissionLevel: 0,
    conditions: {},
    metadata: { uiMode: metadataMode },
    status: 'active',
  };
}

function resourceRequestFromKey(resourceKey: ResourceKey): GatewayAccessRuleResourceRequest {
  const [resourceType, resourceId] = splitResourceKey(resourceKey);
  return { resourceType, resourceId };
}

function resourceKeyFromRule(rule: GatewayAccessRule): ResourceKey | undefined {
  if (rule.resourceType !== 'platform' && rule.resourceType !== 'platform_model' && rule.resourceType !== 'base_model') return undefined;
  return makeResourceKey(rule.resourceType, rule.resourceId);
}

function accessRuleDiagnosticReason(reason?: string) {
  switch (reason) {
    case 'resource_unavailable':
      return '平台或模型当前未启用';
    case 'owner_access_revoked':
      return '已被用户组、租户或用户权限收回';
    case 'scope_not_allowed':
      return '不在当前 API Key 的能力范围内';
    default:
      return '当前规则不生效';
  }
}

function dedupeResourceKeys(keys: ResourceKey[]) {
  return Array.from(new Set(keys.filter(Boolean)));
}

function mergeExpanded(current: Set<string>, platforms: IntegrationPlatform[]) {
  if (current.size > 0) return current;
  return new Set(platforms.map((item) => item.id));
}

function toggleSet(set: Set<string>, value: string) {
  const next = new Set(set);
  if (next.has(value)) next.delete(value);
  else next.add(value);
  return next;
}

function makeResourceKey(resourceType: ResourceType, resourceId: string): ResourceKey {
  return `${resourceType}:${resourceId}`;
}

function splitResourceKey(key: ResourceKey): [ResourceType, string] {
  const [resourceType, ...rest] = key.split(':');
  return [resourceType as ResourceType, rest.join(':')];
}

function modelLabel(model: PlatformModel) {
  return model.displayName || model.modelAlias || model.modelName;
}
