// ignore_for_file: unused_import, unnecessary_import

import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:flutter/foundation.dart';
import 'gateway_runtime.dart';
import 'runtime_models.dart';
import 'secure_config_store.dart';
import 'runtime_controllers_settings.dart';
import 'runtime_controllers_entities.dart';
import 'runtime_controllers_derived_tasks.dart';

class AiGatewayResponseExceptionInternal implements Exception {
  const AiGatewayResponseExceptionInternal({
    required this.statusCode,
    required this.message,
  });

  final int statusCode;
  final String message;
}

class GatewayAgentsController extends ChangeNotifier {
  GatewayAgentsController(this.runtimeInternal);

  final GatewayRuntime runtimeInternal;

  List<GatewayAgentSummary> agentsInternal = const <GatewayAgentSummary>[];
  String selectedAgentIdInternal = '';
  bool loadingInternal = false;
  String? errorInternal;

  List<GatewayAgentSummary> get agents => agentsInternal;
  String get selectedAgentId => selectedAgentIdInternal;
  bool get loading => loadingInternal;
  String? get error => errorInternal;

  GatewayAgentSummary? get selectedAgent {
    final selected = selectedAgentIdInternal.trim();
    if (selected.isEmpty) {
      return null;
    }
    for (final agent in agentsInternal) {
      if (agent.id == selected) {
        return agent;
      }
    }
    return null;
  }

  String get activeAgentName => selectedAgent?.name ?? 'Main';

  void restoreSelection(String agentId) {
    selectedAgentIdInternal = agentId.trim();
    notifyListeners();
  }

  void selectAgent(String? agentId) {
    selectedAgentIdInternal = agentId?.trim() ?? '';
    notifyListeners();
  }

  Future<void> refresh() async {
    if (!runtimeInternal.isConnected) {
      agentsInternal = const <GatewayAgentSummary>[];
      errorInternal = null;
      notifyListeners();
      return;
    }
    loadingInternal = true;
    errorInternal = null;
    notifyListeners();
    try {
      agentsInternal = await runtimeInternal.listAgents();
      if (selectedAgentIdInternal.isNotEmpty &&
          !agentsInternal.any((item) => item.id == selectedAgentIdInternal)) {
        selectedAgentIdInternal = '';
      }
    } catch (error) {
      errorInternal = error.toString();
    } finally {
      loadingInternal = false;
      notifyListeners();
    }
  }
}

class GatewaySessionsController extends ChangeNotifier {
  GatewaySessionsController(this.runtimeInternal);

  final GatewayRuntime runtimeInternal;

  List<GatewaySessionSummary> sessionsInternal =
      const <GatewaySessionSummary>[];
  String currentSessionKeyInternal = '';
  String selectedAgentIdInternal = '';
  String defaultAgentIdInternal = '';
  bool loadingInternal = false;
  String? errorInternal;

  List<GatewaySessionSummary> get sessions => sessionsInternal;
  String get currentSessionKey => currentSessionKeyInternal;
  bool get loading => loadingInternal;
  String? get error => errorInternal;

  void configure({
    required String selectedAgentId,
    required String defaultAgentId,
  }) {
    selectedAgentIdInternal = selectedAgentId.trim();
    defaultAgentIdInternal = defaultAgentId.trim();
    notifyListeners();
  }

  Future<void> refresh() async {
    if (!runtimeInternal.isConnected) {
      sessionsInternal = const <GatewaySessionSummary>[];
      errorInternal = null;
      notifyListeners();
      return;
    }
    loadingInternal = true;
    errorInternal = null;
    notifyListeners();
    try {
      sessionsInternal = await runtimeInternal.listSessions(limit: 50);
    } catch (error) {
      errorInternal = error.toString();
    } finally {
      loadingInternal = false;
      notifyListeners();
    }
  }

  Future<void> switchSession(String sessionKey) async {
    final trimmed = sessionKey.trim();
    if (trimmed.isEmpty || trimmed == currentSessionKeyInternal) {
      return;
    }
    currentSessionKeyInternal = trimmed;
    notifyListeners();
  }
}

class GatewayChatController extends ChangeNotifier {
  GatewayChatController(this.runtimeInternal);

  final GatewayRuntime runtimeInternal;

  List<GatewayChatMessage> messagesInternal = const <GatewayChatMessage>[];
  String sessionKeyInternal = 'main';
  bool loadingInternal = false;
  String? errorInternal;
  String? streamingAssistantTextInternal;
  final Set<String> pendingRunsInternal = <String>{};

  List<GatewayChatMessage> get messages => messagesInternal;
  String get sessionKey => sessionKeyInternal;
  bool get loading => loadingInternal;
  String? get error => errorInternal;
  String? get streamingAssistantText => streamingAssistantTextInternal;
  bool get hasPendingRun => pendingRunsInternal.isNotEmpty;
  String? get activeRunId =>
      pendingRunsInternal.isEmpty ? null : pendingRunsInternal.first;

  Future<void> loadSession(String sessionKey) async {
    final next = sessionKey.trim().isEmpty ? 'main' : sessionKey.trim();
    sessionKeyInternal = next;
    messagesInternal = const <GatewayChatMessage>[];
    streamingAssistantTextInternal = null;
    if (!runtimeInternal.isConnected) {
      errorInternal = null;
      notifyListeners();
      return;
    }
    loadingInternal = true;
    errorInternal = null;
    notifyListeners();
    try {
      messagesInternal = await runtimeInternal.loadHistory(next);
    } catch (error) {
      messagesInternal = const <GatewayChatMessage>[];
      errorInternal = error.toString();
    } finally {
      loadingInternal = false;
      notifyListeners();
    }
  }

  void handleEvent(GatewayPushEvent event) {
    if (event.event == 'chat.run') {
      handleChatRunEventInternal(asMap(event.payload));
      return;
    }
    if (event.event == 'chat') {
      handleChatEventInternal(asMap(event.payload));
      return;
    }
    if (event.event == 'agent') {
      handleAgentEventInternal(asMap(event.payload));
    }
  }

  void clear() {
    messagesInternal = const <GatewayChatMessage>[];
    pendingRunsInternal.clear();
    streamingAssistantTextInternal = null;
    errorInternal = null;
    notifyListeners();
  }

  void resetSession(String sessionKey) {
    sessionKeyInternal = sessionKey.trim().isEmpty ? 'main' : sessionKey.trim();
    messagesInternal = const <GatewayChatMessage>[];
    pendingRunsInternal.clear();
    streamingAssistantTextInternal = null;
    errorInternal = null;
    notifyListeners();
  }

  void handleChatRunEventInternal(Map<String, dynamic> payload) {
    final runId = stringValue(payload['runId']);
    final state = stringValue(payload['state']) ?? '';
    final incomingSessionKey =
        stringValue(payload['sessionKey']) ?? sessionKeyInternal;
    final isOurRun = runId != null && pendingRunsInternal.contains(runId);
    if (!matchesSessionKey(incomingSessionKey, sessionKeyInternal) &&
        !isOurRun) {
      return;
    }

    final assistantText = stringValue(payload['assistantText']) ?? '';
    if (assistantText.isNotEmpty && (state == 'delta' || state == 'final')) {
      streamingAssistantTextInternal = assistantText;
    }
    if (state == 'error') {
      errorInternal = stringValue(payload['errorMessage']) ?? 'Chat failed';
    }
    final terminal =
        boolValue(payload['terminal']) ??
        false || state == 'final' || state == 'aborted' || state == 'error';
    if (terminal) {
      if (runId != null) {
        pendingRunsInternal.remove(runId);
      } else {
        pendingRunsInternal.clear();
      }
      unawaited(loadSession(sessionKeyInternal));
      notifyListeners();
      return;
    }
    notifyListeners();
  }

  void handleChatEventInternal(Map<String, dynamic> payload) {
    final message = asMap(payload['message']);
    final role = (stringValue(message['role']) ?? '').toLowerCase();
    handleChatRunEventInternal(<String, dynamic>{
      'runId': payload['runId'],
      'sessionKey': payload['sessionKey'],
      'state': payload['state'],
      if (role == 'assistant') 'assistantText': extractMessageText(message),
      'errorMessage': payload['errorMessage'],
      'terminal': false,
    });
  }

  void handleAgentEventInternal(Map<String, dynamic> payload) {
    final runId = stringValue(payload['runId']);
    if (runId == null || !pendingRunsInternal.contains(runId)) {
      return;
    }
    final stream = stringValue(payload['stream']);
    final data = asMap(payload['data']);
    if (stream == 'assistant') {
      final nextText = stringValue(data['text']) ?? extractMessageText(data);
      if (nextText.isNotEmpty) {
        handleChatRunEventInternal(<String, dynamic>{
          'runId': runId,
          'sessionKey': payload['sessionKey'] ?? data['sessionKey'],
          'state': 'delta',
          'assistantText': nextText,
          'source': 'agent',
          'terminal': false,
        });
      }
    }
  }
}
