import 'dart:async';
import 'dart:convert';

import 'package:social_app/core/config/env.dart';

import '../ai/ai_decision_engine.dart';
import '../models/ag_ui_event.dart';
import '../models/tool_result.dart';
import '../tools/tool_registry.dart';
import 'mock_history_service.dart';

/// Mock ID 前缀常量
const _threadIdPrefix = 'thread_';
const _runIdPrefix = 'run_';
const _toolCallIdPrefix = 'tc_';
const _messageIdPrefix = 'msg_';

/// 流式输出延迟 (毫秒)
const _streamChunkDelayMs = 50;

/// 文本块大小
const _textChunkSize = 10;

typedef EventCallback = void Function(AgUiEvent event);

class AgUiService {
  EventCallback onEvent;
  final AiDecisionEngine _decisionEngine;
  final MockHistoryService _historyService;

  AgUiService({EventCallback? onEvent})
    : onEvent = onEvent ?? ((_) {}),
      _decisionEngine = AiDecisionEngine(),
      _historyService = MockHistoryService();

  Future<void> sendMessage(String content) async {
    if (Env.isMockApi) {
      await _mockEventStream(content);
    } else {
      throw UnimplementedError('Real API not implemented');
    }
  }

  Future<void> loadHistory({DateTime? beforeDate}) async {
    if (Env.isMockApi) {
      await _mockLoadHistory(beforeDate: beforeDate);
    }
  }

  bool hasEarlierHistory(DateTime fromDate) {
    return _historyService.hasEarlierHistory(fromDate);
  }

  Future<void> _mockLoadHistory({DateTime? beforeDate}) async {
    final threadId = '$_threadIdPrefix${DateTime.now().millisecondsSinceEpoch}';
    final runId = '$_runIdPrefix${DateTime.now().millisecondsSinceEpoch}';

    onEvent(RunStartedEvent(threadId: threadId, runId: runId));
    await Future.delayed(const Duration(milliseconds: 10));

    // Determine target date, end early if no earlier history
    final DateTime targetDate;
    if (beforeDate != null) {
      final prevDate = _historyService.getPreviousDay(beforeDate);
      if (prevDate == null) {
        onEvent(RunFinishedEvent(threadId: threadId, runId: runId));
        return;
      }
      targetDate = prevDate;
    } else {
      targetDate = _historyService.getLatestHistoryDate() ?? DateTime.now();
    }

    final messages = _historyService.getHistoryForDay(targetDate);
    onEvent(MessagesSnapshotEvent(messages: messages));
    await Future.delayed(const Duration(milliseconds: 10));
    onEvent(RunFinishedEvent(threadId: threadId, runId: runId));
  }

  Future<void> _mockEventStream(String content) async {
    final threadId = '$_threadIdPrefix${DateTime.now().millisecondsSinceEpoch}';
    final runId = '$_runIdPrefix${DateTime.now().millisecondsSinceEpoch}';

    onEvent(RunStartedEvent(threadId: threadId, runId: runId));

    final forceTrigger = _decisionEngine.tryForceTrigger(content);
    if (forceTrigger != null) {
      await _mockToolCallFlowWithArgs(forceTrigger.toolName, forceTrigger.args);
    } else if (_decisionEngine.shouldTriggerToolCall(content)) {
      await _mockToolCallFlow(content);
    }

    final replies = _generateReplies(content);
    if (replies.isNotEmpty) {
      await _mockTextMessageStream(replies);
    }

    onEvent(RunFinishedEvent(threadId: threadId, runId: runId));
  }

  Future<void> _mockToolCallFlow(String content) async {
    final args = _decisionEngine.getToolCallArgs(content);
    if (args == null) return;

    await _mockToolCallFlowWithArgs('create_calendar_event', args);
  }

  Future<void> _mockToolCallFlowWithArgs(
    String toolName,
    Map<String, dynamic> args,
  ) async {
    final toolCallId =
        '$_toolCallIdPrefix${DateTime.now().millisecondsSinceEpoch}';

    onEvent(ToolCallStartEvent(toolCallId: toolCallId, toolCallName: toolName));

    onEvent(ToolCallArgsEvent(toolCallId: toolCallId, delta: jsonEncode(args)));

    onEvent(ToolCallEndEvent(toolCallId: toolCallId));

    final validation = ToolRegistry.validateArgs(toolName, args);
    if (!validation.ok) {
      onEvent(
        ToolCallErrorEvent(
          toolCallId: toolCallId,
          error: validation.error ?? 'Validation failed',
          code: 'VALIDATION_ERROR',
        ),
      );
      return;
    }

    try {
      ToolRegistry.initialize();
      final result = await ToolRegistry.execute(toolName, args);
      final ui = _buildUiCard(toolName, result);
      final messageId =
          '$_messageIdPrefix${DateTime.now().millisecondsSinceEpoch}';

      onEvent(
        ToolCallResultEvent(
          messageId: messageId,
          toolCallId: toolCallId,
          result: result,
          ui: ui,
        ),
      );
    } catch (e) {
      onEvent(
        ToolCallErrorEvent(
          toolCallId: toolCallId,
          error: e.toString(),
          code: 'EXECUTION_ERROR',
        ),
      );
    }
  }

  UiCard? _buildUiCard(String toolName, Map<String, dynamic> result) {
    if (toolName == 'create_calendar_event') {
      return UiCard(
        cardType: 'calendar',
        data: CalendarCardData(
          id: result['eventId'] ?? '',
          title: result['title'] ?? '',
          description: result['description'],
          startAt: result['startAt'] ?? '',
          endAt: result['endAt'],
          timezone: result['timezone'],
          location: result['location'],
          color: result['color'],
          sourceType: result['sourceType'],
        ).toJson(),
        actions: [
          CardAction(
            type: 'link',
            label: '查看详情',
            target: '/calendar/${result['eventId']}',
          ),
        ],
      );
    }
    return null;
  }

  List<String> _generateReplies(String content) {
    final intent = _decisionEngine.matchIntent(content);

    switch (intent) {
      case Intent.createEvent:
        return ['好的，我已经为您创建了日程安排。'];
      case Intent.searchEvent:
        return ['您今天有以下日程：\n- 10:00 团队会议\n- 14:00 产品评审'];
      case Intent.unknown:
        return ['我理解了您的问题，让我来帮您处理。'];
    }
  }

  Future<void> _mockTextMessageStream(List<String> replies) async {
    for (final reply in replies) {
      final messageId =
          '$_messageIdPrefix${DateTime.now().millisecondsSinceEpoch}';

      onEvent(TextMessageStartEvent(messageId: messageId, role: 'assistant'));

      for (var i = 0; i < reply.length; i += _textChunkSize) {
        final end = (i + _textChunkSize < reply.length)
            ? i + _textChunkSize
            : reply.length;
        final chunk = reply.substring(i, end);

        onEvent(TextMessageContentEvent(messageId: messageId, delta: chunk));

        await Future.delayed(const Duration(milliseconds: _streamChunkDelayMs));
      }

      onEvent(TextMessageEndEvent(messageId: messageId));
    }
  }
}
