import 'dart:async';

import 'package:votianlt_app/services/developer.dart' as developer;

import '../app_state.dart';
import '../models/chat.dart';
import '../models/chat_message.dart';
import 'database_service.dart';

class ChatService {
  ChatService._internal();
  static final ChatService _instance = ChatService._internal();
  factory ChatService() => _instance;

  static const _jobIdPrefix = 'job:';
  static const _jobNumberPrefix = 'job_number:';
  static const _generalPrefix = 'general:';
  static const _defaultGeneralConversationKey =
      'general:allgemeine-nachrichten';

  final DatabaseService _databaseService = DatabaseService();
  final AppState _appState = AppState();

  final List<Chat> _chats = <Chat>[];
  final StreamController<List<Chat>> _chatsController =
      StreamController<List<Chat>>.broadcast();
  final StreamController<int> _unreadCountController =
      StreamController<int>.broadcast();

  bool _initialized = false;
  Completer<void>? _initCompleter;
  int _unreadCount = 0;

  Stream<List<Chat>> get chatsStream => _chatsController.stream;
  List<Chat> get currentChats => List<Chat>.unmodifiable(_chats);
  Stream<int> get unreadCountStream => _unreadCountController.stream;
  int get unreadCount => _unreadCount;

  Future<void> initialize() async {
    if (_initialized) {
      await _loadChatsFromDatabase();
      developer.log(
        'ChatService already initialized, refreshed chats/unread count: $_unreadCount',
        name: 'ChatService',
      );
      return;
    }
    if (_initCompleter != null) {
      return _initCompleter!.future;
    }

    _initCompleter = Completer<void>();
    developer.log('Initializing ChatService...', name: 'ChatService');

    await _loadChatsFromDatabase();

    _initialized = true;
    _initCompleter!.complete();
    developer.log(
      'ChatService initialized with unread count: $_unreadCount',
      name: 'ChatService',
    );
  }

  Future<void> dispose() async {
    await _chatsController.close();
    await _unreadCountController.close();
    _initialized = false;
    _initCompleter = null;
  }

  Future<List<ChatMessage>> loadMessagesForChat(String conversationKey) async {
    await initialize();
    return _databaseService.loadChatMessages(conversationKey: conversationKey);
  }

  Future<void> markConversationRead(String conversationKey) async {
    await initialize();
    await _databaseService.markConversationRead(conversationKey);
    await _refreshConversation(conversationKey);
    // Note: _refreshConversation already calls _updateUnreadCount,
    // so we don't need to call it again here
  }

  Future<void> deleteJobChats(String jobId, {String? jobNumber}) async {
    if (!_initialized) {
      await initialize();
    }

    final trimmedJobId = jobId.trim();
    final lowerJobId = trimmedJobId.toLowerCase();
    final trimmedJobNumber = jobNumber?.trim() ?? '';
    final lowerJobNumber = trimmedJobNumber.toLowerCase();

    final conversationKeys = <String>[
      if (trimmedJobId.isNotEmpty) '$_jobIdPrefix$lowerJobId',
      if (trimmedJobNumber.isNotEmpty) '$_jobNumberPrefix$lowerJobNumber',
    ];

    await _databaseService.deleteChatMessagesForJob(
      jobId: trimmedJobId.isNotEmpty ? trimmedJobId : null,
      jobNumber: trimmedJobNumber.isNotEmpty ? trimmedJobNumber : null,
      conversationKeys: conversationKeys,
    );

    _chats.removeWhere((chat) {
      final matchesKey = conversationKeys.contains(chat.id);
      final matchesId =
          trimmedJobId.isNotEmpty &&
          (chat.jobId?.trim().toLowerCase() == lowerJobId);
      final matchesNumber =
          trimmedJobNumber.isNotEmpty &&
          (chat.jobNumber?.trim().toLowerCase() == lowerJobNumber);
      return matchesKey || matchesId || matchesNumber;
    });

    _ensureDefaultGeneralChat();
    _sortChats();
    _emitChats();
    await _updateUnreadCount();

    developer.log(
      'Removed chat conversations for jobId=$jobId jobNumber=$jobNumber',
      name: 'ChatService',
    );
  }

  String conversationKeyForMessage(ChatMessage message) {
    developer.log(
      '[DEBUG_LOG] conversationKeyForMessage called for message ${message.id}, messageType=${message.messageType}, direction=${message.direction}',
      name: 'ChatService',
    );

    // Messages with GENERAL messageType should always go to the default general chat
    if (message.messageType == ChatMessageType.general) {
      developer.log(
        '[DEBUG_LOG] GENERAL message detected, routing to conversation key: $_defaultGeneralConversationKey',
        name: 'ChatService',
      );
      return _defaultGeneralConversationKey;
    }

    // Job-related messages go to job-specific chats
    final jobId = message.jobId?.trim();
    if (jobId != null && jobId.isNotEmpty) {
      final normalizedJobId = jobId.toLowerCase();
      final key = '$_jobIdPrefix$normalizedJobId';
      developer.log(
        '[DEBUG_LOG] Job-related message (by jobId), routing to conversation key: $key',
        name: 'ChatService',
      );
      return key;
    }
    final jobNumber = message.jobNumber?.trim();
    if (jobNumber != null && jobNumber.isNotEmpty) {
      final normalizedJobNumber = jobNumber.toLowerCase();
      final key = '$_jobNumberPrefix$normalizedJobNumber';
      developer.log(
        '[DEBUG_LOG] Job-related message (by jobNumber), routing to conversation key: $key',
        name: 'ChatService',
      );
      return key;
    }

    developer.log(
      '[DEBUG_LOG] No job context available, routing to default general chat',
      name: 'ChatService',
    );
    return _defaultGeneralConversationKey;
  }

  Future<void> saveIncomingMessage(ChatMessage message) async {
    if (!_initialized) {
      await initialize();
    }
    await _persistMessage(message.copyWith(pendingSync: false));
  }

  Future<void> saveOutgoingMessage(ChatMessage message) async {
    if (!_initialized) {
      await initialize();
    }
    await _persistMessage(message);
  }

  Future<void> markOutgoingMessageSynced(String messageId) async {
    if (!_initialized) {
      await initialize();
    }

    final conversationKey = await _databaseService.updateChatMessagePendingSync(
      messageId,
      false,
    );
    if (conversationKey != null && conversationKey.isNotEmpty) {
      await _refreshConversation(conversationKey);
    }
  }

  Future<void> _persistMessage(ChatMessage message) async {
    final conversationKey = conversationKeyForMessage(message);

    final jobId = message.jobId?.trim();
    if (jobId != null && jobId.isNotEmpty) {
      final legacyKey = '$_jobIdPrefix$jobId';
      if (legacyKey != conversationKey) {
        await _databaseService.migrateConversationKey(
          legacyKey,
          conversationKey,
        );
      }
    } else {
      final jobNumber = message.jobNumber?.trim();
      if (jobNumber != null && jobNumber.isNotEmpty) {
        final legacyKey = '$_jobNumberPrefix$jobNumber';
        if (legacyKey != conversationKey) {
          await _databaseService.migrateConversationKey(
            legacyKey,
            conversationKey,
          );
        }
      }
    }

    await _databaseService.upsertChatMessage(message, conversationKey);
    if (!message.pendingSync) {
      await _databaseService.removePendingDuplicates(conversationKey, message);
    }
    await _refreshConversation(conversationKey);
  }

  Future<void> _loadChatsFromDatabase() async {
    await _databaseService.ensureInitialized();
    final grouped = await _loadNormalizedChatGroups();
    _chats.clear();
    grouped.forEach((conversationKey, messages) {
      final chat = _buildChat(conversationKey, messages);
      if (chat != null) {
        _chats.add(chat);
      }
    });
    _ensureDefaultGeneralChat();
    _sortChats();
    _emitChats();
    await _updateUnreadCount();
  }

  Future<void> _refreshConversation(String conversationKey) async {
    if (_isLegacyGeneralConversationKey(conversationKey)) {
      await _databaseService.migrateConversationKey(
        conversationKey,
        _defaultGeneralConversationKey,
      );
      conversationKey = _defaultGeneralConversationKey;
    }

    final messages = await _databaseService.loadChatMessages(
      conversationKey: conversationKey,
    );
    final index = _chats.indexWhere((chat) => chat.id == conversationKey);

    if (messages.isEmpty) {
      if (index != -1) {
        _chats.removeAt(index);
      }
      _ensureDefaultGeneralChat();
      _sortChats();
      _emitChats();
      await _updateUnreadCount();
      return;
    }

    final chat = _buildChat(conversationKey, messages);
    if (chat == null) {
      return;
    }

    if (index == -1) {
      _chats.add(chat);
    } else {
      _chats[index] = chat;
    }
    _ensureDefaultGeneralChat();
    _sortChats();
    _emitChats();
    await _updateUnreadCount();
  }

  Chat? _buildChat(String conversationKey, List<ChatMessage> messages) {
    if (messages.isEmpty) {
      return null;
    }

    messages.sort((a, b) => a.createdAt.compareTo(b.createdAt));
    final lastMessage = messages.last;

    final jobId = messages
        .map((m) => m.jobId)
        .firstWhere(
          (value) => value != null && value.isNotEmpty,
          orElse: () => null,
        );
    final jobNumber = messages
        .map((m) => m.jobNumber)
        .firstWhere(
          (value) => value != null && value.isNotEmpty,
          orElse: () => null,
        );
    final counterpart = _determineCounterpart(conversationKey, messages);

    final isJobChat =
        conversationKey.startsWith(_jobIdPrefix) ||
        conversationKey.startsWith(_jobNumberPrefix) ||
        messages.any((m) => m.messageType == ChatMessageType.jobRelated);

    final chatType = isJobChat ? ChatType.jobSpecific : ChatType.general;

    final counterpartNormalized =
        counterpart != null &&
                counterpart.toLowerCase() ==
                    _appState.loggedInEmail!.toLowerCase()
            ? _appState.loggedInEmail!
            : counterpart;

    final bool isDefaultGeneral =
        !isJobChat && conversationKey == _defaultGeneralConversationKey;

    final title =
        isJobChat
            ? _buildJobTitle(jobNumber, jobId)
            : (isDefaultGeneral
                ? 'Allgemeine Nachrichten'
                : (counterpart ?? 'Allgemeiner Chat'));

    return Chat(
      id: conversationKey,
      title: title,
      receiver: counterpartNormalized,
      type: chatType,
      jobId: jobId,
      jobNumber: jobNumber,
      messages: List<ChatMessage>.unmodifiable(messages),
      lastMessageTime: lastMessage.createdAt,
      lastMessagePreview:
          lastMessage.contentType == ChatContentType.image
              ? '[Bild]'
              : lastMessage.content,
    );
  }

  String _buildJobTitle(String? jobNumber, String? jobId) {
    if (jobNumber != null && jobNumber.isNotEmpty) {
      return 'Job $jobNumber';
    }
    if (jobId != null && jobId.length >= 6) {
      return 'Job ${jobId.substring(0, 6).toUpperCase()}';
    }
    return 'Job-Chat';
  }

  String? _determineCounterpart(
    String conversationKey,
    List<ChatMessage> messages,
  ) {
    // Receiver is always the userId for general chats
    return _appState.loggedInEmail;
  }

  void _sortChats() {
    _chats.sort((a, b) => b.lastMessageTime.compareTo(a.lastMessageTime));
  }

  void _emitChats() {
    if (_chatsController.isClosed) {
      return;
    }
    _chatsController.add(List<Chat>.unmodifiable(_chats));
  }

  Future<void> _updateUnreadCount() async {
    try {
      await _databaseService.ensureInitialized();
      final count = await _databaseService.getTotalUnreadMessageCount();
      developer.log(
        '[DEBUG_LOG] Unread count from database: $count',
        name: 'ChatService',
      );
      _unreadCount = count;
      if (!_unreadCountController.isClosed) {
        _unreadCountController.add(count);
        developer.log(
          '[DEBUG_LOG] Emitted unread count to stream: $count',
          name: 'ChatService',
        );
      } else {
        developer.log(
          '[DEBUG_LOG] Unread count controller is closed, cannot emit',
          name: 'ChatService',
        );
      }
    } catch (e, st) {
      developer.log('Error updating unread count: $e', name: 'ChatService');
      developer.log('Stack trace: $st', name: 'ChatService');
    }
  }

  Future<Map<String, List<ChatMessage>>> _loadNormalizedChatGroups() async {
    var grouped = await _databaseService.loadAllChatMessagesGrouped();
    final legacyGeneralKeys =
        grouped.keys.where(_isLegacyGeneralConversationKey).toList();
    if (legacyGeneralKeys.isEmpty) {
      return grouped;
    }

    for (final key in legacyGeneralKeys) {
      await _databaseService.migrateConversationKey(
        key,
        _defaultGeneralConversationKey,
      );
    }

    grouped = await _databaseService.loadAllChatMessagesGrouped();
    return grouped;
  }

  bool _isLegacyGeneralConversationKey(String conversationKey) {
    return conversationKey != _defaultGeneralConversationKey &&
        conversationKey.startsWith(_generalPrefix) &&
        !conversationKey.startsWith(_jobIdPrefix) &&
        !conversationKey.startsWith(_jobNumberPrefix);
  }

  void _ensureDefaultGeneralChat() {
    final receiver = _appState.loggedInEmail;
    if (receiver == null || receiver.isEmpty) {
      developer.log(
        '[DEBUG_LOG] _ensureDefaultGeneralChat: No receiver available, skipping',
        name: 'ChatService',
      );
      return;
    }

    const conversationKey = _defaultGeneralConversationKey;

    developer.log(
      '[DEBUG_LOG] _ensureDefaultGeneralChat: Creating/ensuring default general chat with key: $conversationKey (receiver=$receiver)',
      name: 'ChatService',
    );

    _chats.removeWhere(
      (chat) =>
          chat.id != conversationKey &&
          chat.type == ChatType.general &&
          chat.receiver != null &&
          chat.receiver!.toLowerCase() == receiver.toLowerCase() &&
          chat.messages.isEmpty,
    );
    final index = _chats.indexWhere((chat) => chat.id == conversationKey);

    if (index == -1) {
      developer.log(
        '[DEBUG_LOG] _ensureDefaultGeneralChat: Chat not found, creating new "Allgemeine Nachrichten" chat',
        name: 'ChatService',
      );
      _chats.add(
        Chat(
          id: conversationKey,
          title: 'Allgemeine Nachrichten',
          receiver: receiver,
          type: ChatType.general,
          jobId: null,
          jobNumber: null,
          messages: const [],
          lastMessageTime: DateTime.fromMillisecondsSinceEpoch(0),
          lastMessagePreview: 'Noch keine Nachrichten',
        ),
      );
    } else {
      developer.log(
        '[DEBUG_LOG] _ensureDefaultGeneralChat: Chat already exists at index $index, verifying/updating it',
        name: 'ChatService',
      );
      final existing = _chats[index];
      if (existing.type != ChatType.general ||
          existing.receiver == null ||
          existing.receiver!.toLowerCase() != receiver.toLowerCase() ||
          (existing.messages.isEmpty &&
              existing.title != 'Allgemeine Nachrichten')) {
        developer.log(
          '[DEBUG_LOG] _ensureDefaultGeneralChat: Updating existing chat to ensure correct settings',
          name: 'ChatService',
        );
        _chats[index] = Chat(
          id: existing.id,
          title:
              existing.messages.isEmpty
                  ? 'Allgemeine Nachrichten'
                  : existing.title,
          receiver: receiver,
          type: ChatType.general,
          jobId: existing.jobId,
          jobNumber: existing.jobNumber,
          messages: existing.messages,
          lastMessageTime: existing.lastMessageTime,
          lastMessagePreview: existing.lastMessagePreview,
        );
      } else {
        developer.log(
          '[DEBUG_LOG] _ensureDefaultGeneralChat: Existing chat is already correctly configured (${existing.messages.length} messages)',
          name: 'ChatService',
        );
      }
    }
  }
}
