// ignore_for_file: unused_import, unnecessary_import

import 'dart:convert';
import '../i18n/app_language.dart';
import '../models/app_models.dart';
import 'runtime_models_connection.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';

String normalizeAuthorizedSkillDirectoryPath(String path) {
  var trimmed = path.trim();
  if (trimmed.isEmpty) {
    return trimmed;
  }
  trimmed = trimmed.replaceFirst(RegExp(r'[\\/]+$'), '');
  trimmed = trimmed.replaceFirst(
    RegExp(r'([\\/])SKILL\.md$', caseSensitive: false),
    '',
  );
  if (trimmed.length <= 1) {
    return trimmed;
  }
  return trimmed.replaceFirst(RegExp(r'[\\/]+$'), '');
}

class AuthorizedSkillDirectory {
  const AuthorizedSkillDirectory({required this.path, this.bookmark = ''});

  final String path;
  final String bookmark;

  AuthorizedSkillDirectory copyWith({String? path, String? bookmark}) {
    return AuthorizedSkillDirectory(
      path: normalizeAuthorizedSkillDirectoryPath(path ?? this.path),
      bookmark: bookmark ?? this.bookmark,
    );
  }

  Map<String, dynamic> toJson() {
    return <String, dynamic>{
      'path': path,
      if (bookmark.trim().isNotEmpty) 'bookmark': bookmark,
    };
  }

  factory AuthorizedSkillDirectory.fromJson(Map<String, dynamic> json) {
    return AuthorizedSkillDirectory(
      path: normalizeAuthorizedSkillDirectoryPath(
        json['path']?.toString() ?? '',
      ),
      bookmark: json['bookmark']?.toString().trim() ?? '',
    );
  }
}

List<AuthorizedSkillDirectory> normalizeAuthorizedSkillDirectories({
  Iterable<AuthorizedSkillDirectory>? directories,
}) {
  final incoming =
      directories?.toList(growable: false) ??
      const <AuthorizedSkillDirectory>[];
  final normalized = <AuthorizedSkillDirectory>[];
  final seen = <String>{};
  for (final item in incoming) {
    final path = normalizeAuthorizedSkillDirectoryPath(item.path);
    if (path.isEmpty || !seen.add(path)) {
      continue;
    }
    normalized.add(
      AuthorizedSkillDirectory(path: path, bookmark: item.bookmark.trim()),
    );
  }
  normalized.sort((left, right) => left.path.compareTo(right.path));
  return List<AuthorizedSkillDirectory>.unmodifiable(normalized);
}

class AssistantThreadConnectionState {
  const AssistantThreadConnectionState({
    required this.executionTarget,
    required this.status,
    required this.primaryLabel,
    required this.detailLabel,
    required this.ready,
    required this.gatewayTokenMissing,
    required this.lastError,
  });

  final AssistantExecutionTarget executionTarget;
  final RuntimeConnectionStatus status;
  final String primaryLabel;
  final String detailLabel;
  final bool ready;
  final bool gatewayTokenMissing;
  final String? lastError;

  bool get isSingleAgent => false;

  bool get connected => ready;

  bool get connecting =>
      !isSingleAgent && status == RuntimeConnectionStatus.connecting;
}

enum AssistantMessageViewMode { rendered, raw }

extension AssistantMessageViewModeCopy on AssistantMessageViewMode {
  String get label => switch (this) {
    AssistantMessageViewMode.rendered => appText('渲染', 'Rendered'),
    AssistantMessageViewMode.raw => 'RAW',
  };

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

enum WorkspaceRefKind { localPath, remotePath, objectStore }

extension WorkspaceRefKindCopy on WorkspaceRefKind {
  static WorkspaceRefKind fromJsonValue(String? value) {
    return WorkspaceRefKind.values.firstWhere(
      (item) => item.name == value,
      orElse: () => WorkspaceRefKind.localPath,
    );
  }
}

enum AssistantPermissionLevel { defaultAccess, fullAccess }

extension AssistantPermissionLevelCopy on AssistantPermissionLevel {
  String get label => switch (this) {
    AssistantPermissionLevel.defaultAccess => appText('默认权限', 'Default Access'),
    AssistantPermissionLevel.fullAccess => appText('完全访问权限', 'Full Access'),
  };

  String get promptValue => switch (this) {
    AssistantPermissionLevel.defaultAccess => 'default',
    AssistantPermissionLevel.fullAccess => 'full-access',
  };

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

enum CodeAgentRuntimeMode { externalCli }

extension CodeAgentRuntimeModeCopy on CodeAgentRuntimeMode {
  String get label => switch (this) {
    CodeAgentRuntimeMode.externalCli => appText(
      '外部 Codex CLI',
      'External Codex CLI',
    ),
  };

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

enum VpnMode { tunnel, proxy }

extension VpnModeCopy on VpnMode {
  String get label => switch (this) {
    VpnMode.tunnel => appText('隧道', 'Tunnel'),
    VpnMode.proxy => appText('代理', 'Proxy'),
  };

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

enum DesktopEnvironment { unknown, gnome, kde }

extension DesktopEnvironmentCopy on DesktopEnvironment {
  String get label => switch (this) {
    DesktopEnvironment.unknown => appText('未知桌面', 'Unknown Desktop'),
    DesktopEnvironment.gnome => 'GNOME',
    DesktopEnvironment.kde => 'KDE Plasma',
  };

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

class LinuxDesktopConfig {
  const LinuxDesktopConfig({
    required this.preferredMode,
    required this.vpnConnectionName,
    required this.proxyHost,
    required this.proxyPort,
    required this.trayEnabled,
  });

  final VpnMode preferredMode;
  final String vpnConnectionName;
  final String proxyHost;
  final int proxyPort;
  final bool trayEnabled;

  factory LinuxDesktopConfig.defaults() {
    return const LinuxDesktopConfig(
      preferredMode: VpnMode.proxy,
      vpnConnectionName: 'XWorkmate Tunnel',
      proxyHost: '127.0.0.1',
      proxyPort: 7890,
      trayEnabled: true,
    );
  }

  LinuxDesktopConfig copyWith({
    VpnMode? preferredMode,
    String? vpnConnectionName,
    String? proxyHost,
    int? proxyPort,
    bool? trayEnabled,
  }) {
    return LinuxDesktopConfig(
      preferredMode: preferredMode ?? this.preferredMode,
      vpnConnectionName: vpnConnectionName ?? this.vpnConnectionName,
      proxyHost: proxyHost ?? this.proxyHost,
      proxyPort: proxyPort ?? this.proxyPort,
      trayEnabled: trayEnabled ?? this.trayEnabled,
    );
  }

  Map<String, dynamic> toJson() {
    return {
      'preferredMode': preferredMode.name,
      'vpnConnectionName': vpnConnectionName,
      'proxyHost': proxyHost,
      'proxyPort': proxyPort,
      'trayEnabled': trayEnabled,
    };
  }

  factory LinuxDesktopConfig.fromJson(Map<String, dynamic> json) {
    final defaults = LinuxDesktopConfig.defaults();
    return LinuxDesktopConfig(
      preferredMode: VpnModeCopy.fromJsonValue(
        json['preferredMode'] as String?,
      ),
      vpnConnectionName:
          json['vpnConnectionName'] as String? ?? defaults.vpnConnectionName,
      proxyHost: json['proxyHost'] as String? ?? defaults.proxyHost,
      proxyPort: json['proxyPort'] as int? ?? defaults.proxyPort,
      trayEnabled: json['trayEnabled'] as bool? ?? defaults.trayEnabled,
    );
  }
}

class SystemProxyState {
  const SystemProxyState({
    required this.enabled,
    required this.host,
    required this.port,
    required this.backend,
    required this.lastAppliedMode,
  });

  final bool enabled;
  final String host;
  final int port;
  final String backend;
  final VpnMode lastAppliedMode;

  factory SystemProxyState.defaults({LinuxDesktopConfig? config}) {
    final resolvedConfig = config ?? LinuxDesktopConfig.defaults();
    return SystemProxyState(
      enabled: resolvedConfig.preferredMode == VpnMode.proxy,
      host: resolvedConfig.proxyHost,
      port: resolvedConfig.proxyPort,
      backend: '',
      lastAppliedMode: resolvedConfig.preferredMode,
    );
  }

  SystemProxyState copyWith({
    bool? enabled,
    String? host,
    int? port,
    String? backend,
    VpnMode? lastAppliedMode,
  }) {
    return SystemProxyState(
      enabled: enabled ?? this.enabled,
      host: host ?? this.host,
      port: port ?? this.port,
      backend: backend ?? this.backend,
      lastAppliedMode: lastAppliedMode ?? this.lastAppliedMode,
    );
  }

  Map<String, dynamic> toJson() {
    return {
      'enabled': enabled,
      'host': host,
      'port': port,
      'backend': backend,
      'lastAppliedMode': lastAppliedMode.name,
    };
  }

  factory SystemProxyState.fromJson(
    Map<String, dynamic> json, {
    LinuxDesktopConfig? config,
  }) {
    final defaults = SystemProxyState.defaults(config: config);
    return SystemProxyState(
      enabled: json['enabled'] as bool? ?? defaults.enabled,
      host: json['host'] as String? ?? defaults.host,
      port: json['port'] as int? ?? defaults.port,
      backend: json['backend'] as String? ?? defaults.backend,
      lastAppliedMode: VpnModeCopy.fromJsonValue(
        json['lastAppliedMode'] as String?,
      ),
    );
  }
}

class TunnelSessionState {
  const TunnelSessionState({
    required this.available,
    required this.connected,
    required this.connectionName,
    required this.backend,
    required this.lastError,
  });

  final bool available;
  final bool connected;
  final String connectionName;
  final String backend;
  final String lastError;

  factory TunnelSessionState.defaults({LinuxDesktopConfig? config}) {
    final resolvedConfig = config ?? LinuxDesktopConfig.defaults();
    return TunnelSessionState(
      available: false,
      connected: false,
      connectionName: resolvedConfig.vpnConnectionName,
      backend: '',
      lastError: '',
    );
  }

  TunnelSessionState copyWith({
    bool? available,
    bool? connected,
    String? connectionName,
    String? backend,
    String? lastError,
  }) {
    return TunnelSessionState(
      available: available ?? this.available,
      connected: connected ?? this.connected,
      connectionName: connectionName ?? this.connectionName,
      backend: backend ?? this.backend,
      lastError: lastError ?? this.lastError,
    );
  }

  Map<String, dynamic> toJson() {
    return {
      'available': available,
      'connected': connected,
      'connectionName': connectionName,
      'backend': backend,
      'lastError': lastError,
    };
  }

  factory TunnelSessionState.fromJson(
    Map<String, dynamic> json, {
    LinuxDesktopConfig? config,
  }) {
    final defaults = TunnelSessionState.defaults(config: config);
    return TunnelSessionState(
      available: json['available'] as bool? ?? defaults.available,
      connected: json['connected'] as bool? ?? defaults.connected,
      connectionName:
          json['connectionName'] as String? ?? defaults.connectionName,
      backend: json['backend'] as String? ?? defaults.backend,
      lastError: json['lastError'] as String? ?? defaults.lastError,
    );
  }
}

class DesktopIntegrationState {
  const DesktopIntegrationState({
    required this.isSupported,
    required this.environment,
    required this.mode,
    required this.trayAvailable,
    required this.trayEnabled,
    required this.autostartEnabled,
    required this.networkManagerAvailable,
    required this.systemProxy,
    required this.tunnel,
    required this.statusMessage,
  });

  final bool isSupported;
  final DesktopEnvironment environment;
  final VpnMode mode;
  final bool trayAvailable;
  final bool trayEnabled;
  final bool autostartEnabled;
  final bool networkManagerAvailable;
  final SystemProxyState systemProxy;
  final TunnelSessionState tunnel;
  final String statusMessage;

  factory DesktopIntegrationState.loading() {
    final config = LinuxDesktopConfig.defaults();
    return DesktopIntegrationState(
      isSupported: true,
      environment: DesktopEnvironment.unknown,
      mode: config.preferredMode,
      trayAvailable: false,
      trayEnabled: config.trayEnabled,
      autostartEnabled: false,
      networkManagerAvailable: false,
      systemProxy: SystemProxyState.defaults(config: config),
      tunnel: TunnelSessionState.defaults(config: config),
      statusMessage: '',
    );
  }

  factory DesktopIntegrationState.unsupported({
    LinuxDesktopConfig? config,
    String message = '',
  }) {
    final resolvedConfig = config ?? LinuxDesktopConfig.defaults();
    return DesktopIntegrationState(
      isSupported: false,
      environment: DesktopEnvironment.unknown,
      mode: resolvedConfig.preferredMode,
      trayAvailable: false,
      trayEnabled: false,
      autostartEnabled: false,
      networkManagerAvailable: false,
      systemProxy: SystemProxyState.defaults(config: resolvedConfig),
      tunnel: TunnelSessionState.defaults(config: resolvedConfig),
      statusMessage: message,
    );
  }

  DesktopIntegrationState copyWith({
    bool? isSupported,
    DesktopEnvironment? environment,
    VpnMode? mode,
    bool? trayAvailable,
    bool? trayEnabled,
    bool? autostartEnabled,
    bool? networkManagerAvailable,
    SystemProxyState? systemProxy,
    TunnelSessionState? tunnel,
    String? statusMessage,
  }) {
    return DesktopIntegrationState(
      isSupported: isSupported ?? this.isSupported,
      environment: environment ?? this.environment,
      mode: mode ?? this.mode,
      trayAvailable: trayAvailable ?? this.trayAvailable,
      trayEnabled: trayEnabled ?? this.trayEnabled,
      autostartEnabled: autostartEnabled ?? this.autostartEnabled,
      networkManagerAvailable:
          networkManagerAvailable ?? this.networkManagerAvailable,
      systemProxy: systemProxy ?? this.systemProxy,
      tunnel: tunnel ?? this.tunnel,
      statusMessage: statusMessage ?? this.statusMessage,
    );
  }

  Map<String, dynamic> toJson() {
    return {
      'isSupported': isSupported,
      'environment': environment.name,
      'mode': mode.name,
      'trayAvailable': trayAvailable,
      'trayEnabled': trayEnabled,
      'autostartEnabled': autostartEnabled,
      'networkManagerAvailable': networkManagerAvailable,
      'systemProxy': systemProxy.toJson(),
      'tunnel': tunnel.toJson(),
      'statusMessage': statusMessage,
    };
  }

  factory DesktopIntegrationState.fromJson(
    Map<String, dynamic> json, {
    LinuxDesktopConfig? fallbackConfig,
  }) {
    final config = fallbackConfig ?? LinuxDesktopConfig.defaults();
    return DesktopIntegrationState(
      isSupported: json['isSupported'] as bool? ?? true,
      environment: DesktopEnvironmentCopy.fromJsonValue(
        json['environment'] as String?,
      ),
      mode: VpnModeCopy.fromJsonValue(json['mode'] as String?),
      trayAvailable: json['trayAvailable'] as bool? ?? false,
      trayEnabled: json['trayEnabled'] as bool? ?? config.trayEnabled,
      autostartEnabled: json['autostartEnabled'] as bool? ?? false,
      networkManagerAvailable:
          json['networkManagerAvailable'] as bool? ?? false,
      systemProxy: SystemProxyState.fromJson(
        (json['systemProxy'] as Map?)?.cast<String, dynamic>() ?? const {},
        config: config,
      ),
      tunnel: TunnelSessionState.fromJson(
        (json['tunnel'] as Map?)?.cast<String, dynamic>() ?? const {},
        config: config,
      ),
      statusMessage: json['statusMessage'] as String? ?? '',
    );
  }
}
