import 'dart:async';
import 'dart:io';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'auth_provider.dart';
import 'data_providers.dart';
import '../utils/sse_handler.dart';

enum MessageType { text, dataConfirm, medicationConfirm, dietAnalysis, reportAnalysis, quickOptions, agentWelcome, taskCard, onboarding }

class ChatMessage {
  final String id;
  final String role;
  String content;
  final DateTime createdAt;
  MessageType type;
  final Map<String, dynamic>? metadata;
  ChatMessage({
    required this.id,
    required this.role,
    required this.content,
    required this.createdAt,
    this.type = MessageType.text,
    this.metadata,
  });
  bool get isUser => role == 'user';
}

enum ActiveAgent { default_, consultation, health, diet, medication, report, exercise, onboarding }

class ChatState {
  final ActiveAgent activeAgent;
  final List<ChatMessage> messages;
  final String? conversationId;
  final bool isStreaming;
  final String? thinkingText;
  const ChatState({
    this.activeAgent = ActiveAgent.default_,
    this.messages = const [],
    this.conversationId,
    this.isStreaming = false,
    this.thinkingText,
  });
  ChatState copyWith({ActiveAgent? activeAgent, List<ChatMessage>? messages,
      String? conversationId, bool? isStreaming, String? thinkingText}) =>
      ChatState(
        activeAgent: activeAgent ?? this.activeAgent,
        messages: messages ?? this.messages,
        conversationId: conversationId ?? this.conversationId,
        isStreaming: isStreaming ?? this.isStreaming,
        thinkingText: thinkingText ?? this.thinkingText,
      );
}

class ConversationItem {
  final String id;
  final String title;
  final String lastMessage;
  final DateTime updatedAt;
  final ActiveAgent agent;

  ConversationItem({
    required this.id,
    required this.title,
    required this.lastMessage,
    required this.updatedAt,
    required this.agent,
  });
}

class SelectedAgentNotifier extends Notifier<ActiveAgent?> {
  @override
  ActiveAgent? build() => null;
  void select(ActiveAgent? a) => state = a;
}

final selectedAgentProvider =
    NotifierProvider<SelectedAgentNotifier, ActiveAgent?>(SelectedAgentNotifier.new);
final chatProvider = NotifierProvider<ChatNotifier, ChatState>(ChatNotifier.new);
final conversationListProvider = FutureProvider<List<ConversationItem>>((ref) async {
  final api = ref.watch(apiClientProvider);
  final token = await api.accessToken;
  if (token == null) return [];
  
  try {
    final res = await api.get('/api/ai/conversations');
    final list = res.data['data'] as List? ?? [];
    return list.map((item) {
      final data = item as Map<String, dynamic>;
      return ConversationItem(
        id: data['id']?.toString() ?? '',
        title: data['title']?.toString() ?? '对话',
        lastMessage: data['lastMessage']?.toString() ?? '',
        updatedAt: DateTime.parse(data['updatedAt']?.toString() ?? DateTime.now().toIso8601String()),
        agent: _parseAgent(data['agentType']?.toString()),
      );
    }).toList();
  } catch (_) {
    return [];
  }
});

ActiveAgent _parseAgent(String? type) {
  switch (type?.toLowerCase()) {
    case 'consultation': return ActiveAgent.consultation;
    case 'health': return ActiveAgent.health;
    case 'diet': return ActiveAgent.diet;
    case 'medication': return ActiveAgent.medication;
    case 'report': return ActiveAgent.report;
    case 'exercise': return ActiveAgent.exercise;
    default: return ActiveAgent.default_;
  }
}

class ChatNotifier extends Notifier<ChatState> {
  StreamSubscription<Map<String, dynamic>>? _subscription;

  @override
  ChatState build() {
    Future.microtask(() {
      insertTaskCard();
      _checkOnboarding();
    });
    return const ChatState();
  }

  void _checkOnboarding() async {
    try {
      final api = ref.read(apiClientProvider);
      final res = await api.get('/api/user/health-archive');
      final archive = res.data['data'];
      // 档案全空 → 触发建档引导
      final isEmpty = archive == null ||
          (archive['diagnosis'] == null && archive['surgeryType'] == null &&
           (archive['allergies'] == null || (archive['allergies'] as List).isEmpty) &&
           (archive['chronicDiseases'] == null || (archive['chronicDiseases'] as List).isEmpty) &&
           (archive['dietRestrictions'] == null || (archive['dietRestrictions'] as List).isEmpty));
      if (isEmpty && !state.messages.any((m) => m.type == MessageType.onboarding)) {
        final db = ref.read(localDbProvider);
        final skipped = await db.read('onboarding_skipped');
        if (skipped == null) {
          state = state.copyWith(messages: [
            ...state.messages,
            ChatMessage(id: 'onboarding_card', role: 'assistant', content: '', createdAt: DateTime.now(), type: MessageType.onboarding),
          ]);
        }
      }
    } catch (_) {}
  }

  void startOnboarding() {
    setAgent(ActiveAgent.onboarding);
    // Remove onboarding card
    state = state.copyWith(messages: state.messages.where((m) => m.type != MessageType.onboarding).toList());
    // Send trigger message
    Future.microtask(() => _sendToAI('开始建档'));
  }

  void dismissOnboarding() async {
    final db = ref.read(localDbProvider);
    await db.write('onboarding_skipped', DateTime.now().toIso8601String());
    state = state.copyWith(messages: state.messages.where((m) => m.type != MessageType.onboarding).toList());
  }

  void insertTaskCard() {
    if (state.messages.any((m) => m.type == MessageType.taskCard)) return;
    state = state.copyWith(messages: [ChatMessage(
      id: 'task_card',
      role: 'assistant',
      content: '',
      createdAt: DateTime.now(),
      type: MessageType.taskCard,
    ), ...state.messages]);
  }

  void setAgent(ActiveAgent a) {
    _subscription?.cancel();
    state = state.copyWith(activeAgent: a);
  }

  Future<void> loadConversation(String convId) async {
    _subscription?.cancel();
    try {
      final api = ref.read(apiClientProvider);
      final res = await api.get('/api/ai/conversations/$convId');
      final rawMessages = (res.data['data'] as List?) ?? [];
      final messages = rawMessages.map((m) {
        final map = m as Map<String, dynamic>;
        return ChatMessage(
          id: map['id']?.toString() ?? '',
          role: map['role']?.toString() ?? 'user',
          content: map['content']?.toString() ?? '',
          createdAt: DateTime.tryParse(map['createdAt']?.toString() ?? '') ?? DateTime.now(),
          type: MessageType.text,
        );
      }).toList();

      // 从对话列表中找到对应的 agent 类型
      final convList = ref.read(conversationListProvider).value ?? [];
      final conv = convList.firstWhere((c) => c.id == convId, orElse: () => ConversationItem(id: convId, title: '', lastMessage: '', updatedAt: DateTime.now(), agent: ActiveAgent.default_));

      state = state.copyWith(
        messages: messages,
        conversationId: convId,
        activeAgent: conv.agent,
      );
      ref.read(selectedAgentProvider.notifier).select(conv.agent);
    } catch (_) {}
  }

  void insertAgentWelcome(ActiveAgent agent) {
    state = state.copyWith(messages: [...state.messages, ChatMessage(
      id: 'welcome_${agent.name}_${DateTime.now().millisecondsSinceEpoch}',
      role: 'assistant',
      content: '',
      createdAt: DateTime.now(),
      type: MessageType.agentWelcome,
      metadata: {'agent': agent.name},
    )]);
  }

  Future<void> sendImage(String imagePath, String text) async {
    final file = File(imagePath);
    if (!await file.exists()) return;

    // 先显示用户消息（本地显示图片路径）
    final userMsg = ChatMessage(
      id: '${DateTime.now().millisecondsSinceEpoch}',
      role: 'user',
      content: text.isNotEmpty ? text : '[图片]',
      createdAt: DateTime.now(),
      metadata: {'localImagePath': imagePath},
    );
    state = state.copyWith(messages: [...state.messages, userMsg]);

    // 异步上传图片
    String? uploadedUrl;
    try {
      final api = ref.read(apiClientProvider);
      uploadedUrl = await api.uploadFile('/api/files/upload', file);
    } catch (_) {
      // 上传失败：保留本地路径，仍然可以本地显示
    }

    // 更新消息元数据（保留本地路径 + 添加远程URL）
    final updatedMsgs = state.messages.toList();
    final idx = updatedMsgs.indexWhere((m) => m.id == userMsg.id);
    if (idx >= 0) {
      final meta = <String, dynamic>{'localImagePath': imagePath};
      if (uploadedUrl != null) meta['imageUrl'] = uploadedUrl;
      updatedMsgs[idx] = ChatMessage(
        id: userMsg.id,
        role: 'user',
        content: userMsg.content,
        createdAt: userMsg.createdAt,
        metadata: meta,
      );
      state = state.copyWith(messages: updatedMsgs);
    }

    // 将图片 URL 作为消息内容发送给 AI
    final msgWithImage = text.isNotEmpty ? '$text\n[图片已上传]' : '[图片已上传]';
    await _sendToAI(msgWithImage);
  }

  Future<void> sendMessage(String text) async {
    if (text.trim().isEmpty || state.isStreaming) return;

    final userMsg = ChatMessage(
      id: '${DateTime.now().millisecondsSinceEpoch}',
      role: 'user',
      content: text,
      createdAt: DateTime.now(),
    );
    state = state.copyWith(
        messages: [...state.messages, userMsg], isStreaming: true);

    await _sendToAI(text);
  }

  Future<void> _sendToAI(String text) async {
    final aiMsg = ChatMessage(
      id: '${DateTime.now().millisecondsSinceEpoch}_ai',
      role: 'assistant',
      content: '',
      createdAt: DateTime.now(),
    );

    state = state.copyWith(isStreaming: true);

    try {
      final token = await ref.read(apiClientProvider).accessToken;
      if (token == null) {
        _addError(aiMsg, '未登录，请重新登录');
        return;
      }

      final agentPath =
          state.activeAgent.name.replaceFirst('default_', 'default');
      final stream = SseHandler.connect(
        agentType: agentPath,
        message: text,
        conversationId: state.conversationId,
        token: token,
      );

      await for (final event in stream) {
        _processEvent(event, aiMsg);
      }
    } catch (e) {
      _addError(aiMsg, '网络异常，请稍后重试');
    }
  }

  void _addError(ChatMessage aiMsg, String errorText) {
    state = state.copyWith(
      messages: [
        ...state.messages,
        ChatMessage(
          id: 'err_${DateTime.now().millisecondsSinceEpoch}',
          role: 'assistant',
          content: errorText,
          createdAt: DateTime.now(),
        ),
      ],
      isStreaming: false,
      thinkingText: null,
    );
  }

  void _processEvent(Map<String, dynamic> j, ChatMessage aiMsg) {
    final a = j['action'] as String?;
    switch (a) {
      case 'conversation_id':
        state = state.copyWith(conversationId: j['data']?.toString());
      case 'answer':
        aiMsg.content += (j['data'] as String?) ?? '';
        final messageType = j['type'] as String? ?? 'text';
        aiMsg.type = _parseMessageType(messageType);
        state = state.copyWith(thinkingText: null);
        _update(aiMsg);
      case 'notice':
        state = state.copyWith(thinkingText: j['message'] as String?);
      case 'tool_result':
        final tool = j['tool'] as String? ?? '';
        if (tool == 'record_health_data') {
          ref.invalidate(latestHealthProvider);
        }
      case 'status':
        _done(aiMsg);
      case 'error':
        _done(aiMsg);
    }
  }

  MessageType _parseMessageType(String type) {
    switch (type) {
      case 'data_confirm': return MessageType.dataConfirm;
      case 'medication_confirm': return MessageType.medicationConfirm;
      case 'diet_analysis': return MessageType.dietAnalysis;
      case 'report_analysis': return MessageType.reportAnalysis;
      case 'quick_options': return MessageType.quickOptions;
      case 'agent_welcome': return MessageType.agentWelcome;
      default: return MessageType.text;
    }
  }

  void _update(ChatMessage m) {
    final u = state.messages.toList();
    final i = u.indexWhere((x) => x.id == m.id);
    if (i >= 0) {
      u[i] = m;
    } else if (m.content.isNotEmpty) {
      u.add(m);
    }
    state = state.copyWith(messages: u);
  }

  void _done(ChatMessage m) {
    final u = state.messages.toList();
    if (!u.any((x) => x.id == m.id) && m.content.isNotEmpty) u.add(m);
    state = state.copyWith(messages: u, isStreaming: false, thinkingText: null);
  }
}
