// ignore_for_file: unused_import, unnecessary_import, invalid_use_of_visible_for_testing_member, invalid_use_of_protected_member

import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:flutter/material.dart';
import 'app_metadata.dart';
import 'app_capabilities.dart';
import 'app_store_policy.dart';
import 'ui_feature_manifest.dart';
import '../i18n/app_language.dart';
import '../models/app_models.dart';
import '../runtime/device_identity_store.dart';

import '../runtime/runtime_bootstrap.dart';
import '../runtime/desktop_platform_service.dart';
import '../runtime/gateway_runtime.dart';
import '../runtime/runtime_controllers.dart';
import '../runtime/runtime_models.dart';
import '../runtime/secure_config_store.dart';
import '../runtime/embedded_agent_launch_policy.dart';
import '../runtime/runtime_coordinator.dart';
import '../runtime/gateway_acp_client.dart';
import '../runtime/codex_runtime.dart';
import '../runtime/codex_config_bridge.dart';
import '../runtime/code_agent_node_orchestrator.dart';
import '../runtime/assistant_artifacts.dart';
import '../runtime/desktop_thread_artifact_service.dart';
import '../runtime/mode_switcher.dart';
import '../runtime/agent_registry.dart';
import '../runtime/platform_environment.dart';
import 'app_controller_desktop_core.dart';
import 'app_controller_desktop_navigation.dart';
import 'app_controller_desktop_gateway.dart';
import 'app_controller_desktop_settings.dart';
import 'app_controller_desktop_thread_sessions.dart';
import 'app_controller_desktop_thread_actions.dart';
import 'app_controller_desktop_workspace_execution.dart';
import 'app_controller_desktop_settings_runtime.dart';
import 'app_controller_desktop_thread_storage.dart';
import 'app_controller_desktop_skill_permissions.dart';
import 'app_controller_desktop_external_acp_routing.dart';
import 'app_controller_desktop_runtime_helpers.dart';

Future<void> refreshAcpCapabilitiesRuntimeInternal(
  AppController controller, {
  bool forceRefresh = false,
  bool persistMountTargets = false,
}) async {
  GatewayAcpCapabilities? capabilities;
  Object? refreshError;
  try {
    capabilities = await controller.gatewayAcpClientInternal.loadCapabilities(
      forceRefresh: forceRefresh,
    );
  } catch (error) {
    refreshError = error;
    // Keep mount refresh resilient when ACP is temporarily unavailable.
  }
  controller.bridgeCapabilitiesRefreshAttemptedInternal = true;
  if (capabilities != null) {
    controller.bridgeCapabilitiesRefreshErrorInternal = '';
    controller.bridgeAgentProviderCatalogInternal =
        normalizeSingleAgentProviderList(capabilities.providerCatalog);
    controller.bridgeGatewayProviderCatalogInternal =
        normalizeSingleAgentProviderList(capabilities.gatewayProviderCatalog);
    controller.bridgeAvailableExecutionTargetsInternal =
        capabilities.availableExecutionTargets;
  } else if (refreshError != null) {
    controller.bridgeCapabilitiesRefreshErrorInternal = refreshError
        .toString()
        .trim();
  }
  if (!controller.disposedInternal) {
    controller.notifyListeners();
  }
}

Future<void> refreshSingleAgentCapabilitiesRuntimeInternal(
  AppController controller, {
  bool forceRefresh = false,
}) async {
  try {
    final capabilities = await controller.gatewayAcpClientInternal
        .loadCapabilities(forceRefresh: forceRefresh);
    controller.bridgeAgentProviderCatalogInternal =
        normalizeSingleAgentProviderList(capabilities.providerCatalog);
    controller.bridgeGatewayProviderCatalogInternal =
        normalizeSingleAgentProviderList(capabilities.gatewayProviderCatalog);
    controller.bridgeAvailableExecutionTargetsInternal =
        capabilities.availableExecutionTargets;
    controller.bridgeCapabilitiesRefreshAttemptedInternal = true;
    controller.bridgeCapabilitiesRefreshErrorInternal = '';
  } catch (error) {
    controller.bridgeCapabilitiesRefreshAttemptedInternal = true;
    controller.bridgeCapabilitiesRefreshErrorInternal = error.toString().trim();
  }
  if (!controller.disposedInternal) {
    controller.notifyListeners();
  }
}

String? assistantWorkingDirectoryForSessionRuntimeInternal(
  AppController controller,
  String sessionKey,
) {
  final localWorkingDirectory =
      resolveLocalAssistantWorkingDirectoryForSessionRuntimeInternal(
        controller,
        sessionKey,
        requireLocalExistence: false,
      );
  if (localWorkingDirectory?.trim().isNotEmpty == true) {
    return localWorkingDirectory;
  }
  final record = controller.taskThreadForSessionInternal(sessionKey);
  if (record?.workspaceKind != WorkspaceKind.remoteFs) {
    return null;
  }
  final remoteWorkingDirectory = record?.workspaceBinding.workspacePath.trim();
  if (remoteWorkingDirectory?.isNotEmpty != true) {
    return null;
  }
  return remoteWorkingDirectory;
}

String? assistantRemoteWorkingDirectoryHintForSessionRuntimeInternal(
  AppController controller,
  String sessionKey,
) {
  final normalizedSessionKey = controller.normalizedAssistantSessionKeyInternal(
    sessionKey,
  );
  final candidate =
      controller
          .taskThreadForSessionInternal(normalizedSessionKey)
          ?.lastRemoteWorkingDirectory
          ?.trim() ??
      '';
  if (candidate.isEmpty) {
    return null;
  }
  return candidate;
}

String? resolveLocalAssistantWorkingDirectoryForSessionRuntimeInternal(
  AppController controller,
  String sessionKey, {
  bool requireLocalExistence = true,
}) {
  final record = controller.taskThreadForSessionInternal(sessionKey);
  if (record?.workspaceKind != WorkspaceKind.localFs) {
    return null;
  }
  final candidate = record?.workspaceBinding.workspacePath.trim() ?? '';
  if (candidate.isEmpty) {
    return null;
  }
  final directory = Directory(candidate);
  if (directory.existsSync()) {
    return directory.path;
  }
  if (requireLocalExistence) {
    return null;
  }
  return candidate;
}

CodeAgentNodeState buildCodeAgentNodeStateRuntimeInternal(
  AppController controller, {
  AssistantExecutionTarget? executionTarget,
}) {
  return CodeAgentNodeState(
    selectedAgentId: controller.agentsControllerInternal.selectedAgentId,
    gatewayConnected: controller.runtimeInternal.isConnected,
    executionTarget:
        executionTarget ?? controller.currentAssistantExecutionTarget,
    runtimeMode: controller.effectiveCodeAgentRuntimeMode,
    bridgeEnabled: controller.isCodexBridgeEnabledInternal,
    bridgeState: controller.codexCooperationStateInternal.name,
    preferredProviderId: 'codex',
  );
}

GatewayMode bridgeGatewayModeRuntimeInternal(AppController controller) {
  if (!controller.runtimeInternal.isConnected) {
    return GatewayMode.offline;
  }
  return switch (controller.currentAssistantExecutionTarget) {
    AssistantExecutionTarget.agent => GatewayMode.remote,
    AssistantExecutionTarget.gateway => GatewayMode.remote,
  };
}

Future<void> ensureCodexGatewayRegistrationRuntimeInternal(
  AppController controller,
) async {
  if (!controller.isCodexBridgeEnabledInternal) {
    return;
  }

  if (!controller.runtimeInternal.isConnected) {
    controller.codexCooperationStateInternal = CodexCooperationState.bridgeOnly;
    controller.codeAgentBridgeRegistryInternal.clearRegistration();
    controller.notifyListeners();
    return;
  }

  if (controller.codeAgentBridgeRegistryInternal.isRegistered) {
    controller.codexCooperationStateInternal = CodexCooperationState.registered;
    controller.notifyListeners();
    return;
  }

  try {
    final dispatch = controller.codeAgentNodeOrchestratorInternal
        .buildGatewayDispatch(
          buildCodeAgentNodeStateRuntimeInternal(controller),
        );
    final resolvedDispatch = await dispatch;
    await controller.codeAgentBridgeRegistryInternal.register(
      agentType: 'code-agent-bridge',
      name: 'XWorkmate Codex Bridge',
      version: kAppVersion,
      transport: 'stdio-bridge',
      capabilities: const <AgentCapability>[
        AgentCapability(
          name: 'chat',
          description: 'Bridge external Codex CLI chat turns.',
        ),
        AgentCapability(
          name: 'code-edit',
          description: 'Bridge code editing tasks through Codex CLI.',
        ),
        AgentCapability(
          name: 'memory-sync',
          description: 'Coordinate memory sync through OpenClaw Gateway.',
        ),
      ],
      metadata: <String, dynamic>{
        ...resolvedDispatch.metadata,
        'providerId': 'codex',
        'runtimeMode': controller.effectiveCodeAgentRuntimeMode.name,
        'gatewayMode': bridgeGatewayModeRuntimeInternal(controller).name,
        'capabilities': const <String>[
          'chat',
          'code-edit',
          'gateway-bridge',
          'memory-sync',
        ],
      },
    );
    controller.codexCooperationStateInternal = CodexCooperationState.registered;
    controller.codexBridgeErrorInternal = null;
  } catch (error) {
    controller.codexCooperationStateInternal = CodexCooperationState.bridgeOnly;
    controller.codexBridgeErrorInternal = error.toString();
  }

  controller.notifyListeners();
}

void clearCodexGatewayRegistrationRuntimeInternal(AppController controller) {
  controller.codeAgentBridgeRegistryInternal.clearRegistration();
  if (controller.isCodexBridgeEnabledInternal) {
    controller.codexCooperationStateInternal = CodexCooperationState.bridgeOnly;
  } else {
    controller.codexCooperationStateInternal = CodexCooperationState.notStarted;
  }
  controller.notifyListeners();
}

void recomputeTasksRuntimeInternal(AppController controller) {
  controller.tasksControllerInternal.recompute(
    sessions: controller.assistantSessions,
    cronJobs: controller.cronJobsControllerInternal.items,
    currentSessionKey: controller.sessionsControllerInternal.currentSessionKey,
    hasPendingRun: controller.hasAssistantPendingRun,
    activeAgentName: controller.agentsControllerInternal.activeAgentName,
  );
}
