// ignore_for_file: unused_import, unnecessary_import

import 'dart:convert';
import '../i18n/app_language.dart';
import '../models/app_models.dart';
import 'runtime_models_profiles.dart';
import 'runtime_models_configs.dart';
import 'runtime_models_settings_snapshot.dart';
import 'runtime_models_runtime_payloads.dart';
import 'runtime_models_gateway_entities.dart';
import 'runtime_models_multi_agent.dart';

enum RuntimeConnectionMode { unconfigured, remote }

extension RuntimeConnectionModeCopy on RuntimeConnectionMode {
  String get label => switch (this) {
    RuntimeConnectionMode.unconfigured => appText('未配置', 'Unconfigured'),
    RuntimeConnectionMode.remote => appText('远程', 'Remote'),
  };

  static RuntimeConnectionMode fromJsonValue(String? value) {
    return RuntimeConnectionMode.values.firstWhere(
      (item) => item.name == value,
      orElse: () => RuntimeConnectionMode.unconfigured,
    );
  }
}

enum RuntimeConnectionStatus { offline, connecting, connected, error }

extension RuntimeConnectionStatusCopy on RuntimeConnectionStatus {
  String get label => switch (this) {
    RuntimeConnectionStatus.offline => appText('离线', 'Offline'),
    RuntimeConnectionStatus.connecting => appText('连接中', 'Connecting'),
    RuntimeConnectionStatus.connected => appText('已连接', 'Connected'),
    RuntimeConnectionStatus.error => appText('错误', 'Error'),
  };
}

bool isLegacyAutoAssistantExecutionTargetValue(String? value) {
  return value?.trim().toLowerCase() == 'auto';
}

enum AssistantExecutionTarget { agent, gateway }

extension AssistantExecutionTargetCopy on AssistantExecutionTarget {
  String get label => switch (this) {
    AssistantExecutionTarget.agent => appText('智能体', 'Agent'),
    AssistantExecutionTarget.gateway => appText('Gateway', 'Gateway'),
  };

  String get promptValue => switch (this) {
    AssistantExecutionTarget.agent => 'agent',
    AssistantExecutionTarget.gateway => 'gateway',
  };

  bool get isAgent => this == AssistantExecutionTarget.agent;
  bool get isGateway => this == AssistantExecutionTarget.gateway;

  String get compactLabel => switch (this) {
    AssistantExecutionTarget.agent => appText('智能体', 'Agent'),
    AssistantExecutionTarget.gateway => appText('Gateway', 'Gateway'),
  };

  static AssistantExecutionTarget fromJsonValue(String? value) {
    return AssistantExecutionTarget.values.firstWhere(
      (item) => item.name == value?.trim() || item.promptValue == value?.trim(),
      orElse: () => AssistantExecutionTarget.agent,
    );
  }
}

List<AssistantExecutionTarget> compactAssistantExecutionTargets(
  Iterable<AssistantExecutionTarget> targets,
) {
  final ordered = <AssistantExecutionTarget>[];
  for (final candidate in AssistantExecutionTarget.values) {
    if (targets.contains(candidate)) {
      ordered.add(candidate);
    }
  }
  return ordered.isEmpty ? AssistantExecutionTarget.values : ordered;
}

AssistantExecutionTarget collapseAssistantExecutionTargetForDisplay(
  AssistantExecutionTarget target,
) => target;

AssistantExecutionTarget resolveAssistantExecutionTargetFromVisibleTargets(
  Iterable<AssistantExecutionTarget> visibleTargets, {
  AssistantExecutionTarget? currentTarget,
}) {
  final visible = visibleTargets.toList(growable: false);
  if (currentTarget != null && visible.contains(currentTarget)) {
    return currentTarget;
  }
  if (visible.isNotEmpty) {
    return visible.first;
  }
  return AssistantExecutionTarget.agent;
}

String normalizeSingleAgentProviderId(String value) {
  final trimmed = value.trim().toLowerCase();
  if (trimmed.isEmpty) {
    return '';
  }
  final normalizedWhitespace = trimmed.replaceAll(RegExp(r'\s+'), '-');
  final buffer = StringBuffer();
  var previousWasSeparator = false;
  var hasOutput = false;
  for (final rune in normalizedWhitespace.runes) {
    final char = String.fromCharCode(rune);
    final isAlphaNumeric =
        (rune >= 97 && rune <= 122) || (rune >= 48 && rune <= 57);
    final isSeparator = char == '-' || char == '_' || char == '.';
    if (isAlphaNumeric) {
      buffer.write(char);
      previousWasSeparator = false;
      hasOutput = true;
      continue;
    }
    if (isSeparator && !previousWasSeparator && hasOutput) {
      buffer.write('-');
      previousWasSeparator = true;
    }
  }
  return buffer.toString().replaceAll(RegExp(r'^[-_.]+|[-_.]+$'), '');
}

String providerFallbackLabelInternal(String providerId) {
  final normalized = normalizeSingleAgentProviderId(providerId);
  if (normalized.isEmpty) {
    return appText('Bridge Provider', 'Bridge Provider');
  }
  return normalized
      .split(RegExp(r'[-_.]+'))
      .where((item) => item.isNotEmpty)
      .map((item) => '${item[0].toUpperCase()}${item.substring(1)}')
      .join(' ');
}

String providerFallbackBadgeInternal({
  required String providerId,
  required String label,
}) {
  final normalized = normalizeSingleAgentProviderId(providerId);
  final known = <String, String>{
    'codex': 'C',
    'opencode': 'O',
    'claude': 'Cl',
    'gemini': 'G',
  };
  final explicit = known[normalized];
  if (explicit != null) {
    return explicit;
  }
  final stripped = label.replaceAll(RegExp(r'\s+'), '');
  if (stripped.isEmpty) {
    return '?';
  }
  final length = stripped.length >= 2 ? 2 : 1;
  return stripped.substring(0, length).toUpperCase();
}

const Set<String> kSupportedExternalAcpEndpointSchemes = <String>{
  'ws',
  'wss',
  'http',
  'https',
};

bool isSupportedExternalAcpEndpoint(String endpoint) {
  final trimmed = endpoint.trim();
  if (trimmed.isEmpty) {
    return false;
  }
  final uri = Uri.tryParse(trimmed);
  final scheme = uri?.scheme.trim().toLowerCase() ?? '';
  return kSupportedExternalAcpEndpointSchemes.contains(scheme);
}

class SingleAgentProvider {
  const SingleAgentProvider({
    required this.providerId,
    required this.label,
    required this.badge,
    this.logoEmoji = '',
    this.supportedTargets = const <AssistantExecutionTarget>[],
    this.enabled = true,
    this.unavailableReason = '',
    this.source = SingleAgentProviderSource.externalExtension,
  });

  static const SingleAgentProvider unspecified = SingleAgentProvider(
    providerId: '',
    label: '',
    badge: '',
  );

  static const SingleAgentProvider codex = SingleAgentProvider(
    providerId: 'codex',
    label: 'Codex',
    badge: 'C',
  );

  static const SingleAgentProvider opencode = SingleAgentProvider(
    providerId: 'opencode',
    label: 'OpenCode',
    badge: 'O',
  );

  static const SingleAgentProvider claude = SingleAgentProvider(
    providerId: 'claude',
    label: 'Claude',
    badge: 'Cl',
  );

  static const SingleAgentProvider gemini = SingleAgentProvider(
    providerId: 'gemini',
    label: 'Gemini',
    badge: 'G',
  );

  static const SingleAgentProvider openclaw = SingleAgentProvider(
    providerId: kCanonicalGatewayProviderId,
    label: kCanonicalGatewayProviderLabel,
    badge: 'OC',
  );

  final String providerId;
  final String label;
  final String badge;
  final String logoEmoji;
  final List<AssistantExecutionTarget> supportedTargets;
  final bool enabled;
  final String unavailableReason;
  final SingleAgentProviderSource source;

  bool get isUnspecified => providerId.trim().isEmpty;
  bool get isExternalExtension =>
      source == SingleAgentProviderSource.externalExtension;

  SingleAgentProvider copyWith({
    String? providerId,
    String? label,
    String? badge,
    String? logoEmoji,
    List<AssistantExecutionTarget>? supportedTargets,
    bool? enabled,
    String? unavailableReason,
    SingleAgentProviderSource? source,
  }) {
    final resolvedProviderId = normalizeSingleAgentProviderId(
      providerId ?? this.providerId,
    );
    final resolvedLabel = (label ?? this.label).trim();
    final resolvedBadge = (badge ?? this.badge).trim();
    return SingleAgentProvider(
      providerId: resolvedProviderId,
      label: resolvedLabel.isEmpty
          ? providerFallbackLabelInternal(resolvedProviderId)
          : resolvedLabel,
      badge: resolvedBadge.isEmpty
          ? providerFallbackBadgeInternal(
              providerId: resolvedProviderId,
              label: resolvedLabel,
            )
          : resolvedBadge,
      logoEmoji: (logoEmoji ?? this.logoEmoji).trim(),
      supportedTargets:
          supportedTargets ??
          List<AssistantExecutionTarget>.from(this.supportedTargets),
      enabled: enabled ?? this.enabled,
      unavailableReason:
          (unavailableReason ?? this.unavailableReason).trim(),
      source: source ?? this.source,
    );
  }

  static SingleAgentProvider fromJsonValue(
    String? value, {
    String? label,
    String? badge,
    String? logoEmoji,
    List<AssistantExecutionTarget>? supportedTargets,
    bool? enabled,
    String? unavailableReason,
  }) {
    final normalized = normalizeSingleAgentProviderId(value ?? '');
    final base = switch (normalized) {
      'codex' => codex,
      'opencode' => opencode,
      'claude' => claude,
      'gemini' => gemini,
      kCanonicalGatewayProviderId => openclaw,
      'auto' || '' => unspecified,
      _ => SingleAgentProvider(
        providerId: normalized,
        label: providerFallbackLabelInternal(normalized),
        badge: providerFallbackBadgeInternal(
          providerId: normalized,
          label: providerFallbackLabelInternal(normalized),
        ),
      ),
    };
    return base.copyWith(
      label: label,
      badge: badge,
      logoEmoji: logoEmoji,
      supportedTargets: supportedTargets,
      enabled: enabled,
      unavailableReason: unavailableReason,
    );
  }

  @override
  bool operator ==(Object other) =>
      identical(this, other) ||
      (other is SingleAgentProvider && other.providerId == providerId);

  @override
  int get hashCode => providerId.hashCode;
}

extension SingleAgentProviderCopy on SingleAgentProvider {
  static SingleAgentProvider fromJsonValue(
    String? value, {
    String? label,
    String? badge,
    String? logoEmoji,
    List<AssistantExecutionTarget>? supportedTargets,
    bool? enabled,
    String? unavailableReason,
  }) => SingleAgentProvider.fromJsonValue(
    value,
    label: label,
    badge: badge,
    logoEmoji: logoEmoji,
    supportedTargets: supportedTargets,
    enabled: enabled,
    unavailableReason: unavailableReason,
  );
}

enum SingleAgentProviderSource { externalExtension }

List<SingleAgentProvider> normalizeSingleAgentProviderList(
  Iterable<SingleAgentProvider> providers,
) {
  final normalized = <SingleAgentProvider>[];
  final seen = <String>{};
  for (final provider in providers) {
    if (seen.add(provider.providerId)) {
      normalized.add(provider);
    }
  }
  return normalized;
}

const String kCanonicalGatewayProviderId = 'openclaw';
const String kCanonicalGatewayProviderLabel = 'OpenClaw';

bool isBridgeOwnedSingleAgentProviderId(String providerId) {
  final normalized = normalizeSingleAgentProviderId(providerId);
  return normalized == 'codex' ||
      normalized == 'opencode' ||
      normalized == 'gemini' ||
      normalized == 'hermes';
}
