import 'dart:convert';

import '../../models/attachment.dart';
import '../../models/chat_preview_media.dart';

const int _maxPreviewThumbs = 3;
const int _maxThumbLength = 20000;

String? attachPreviewLabel(dynamic attaches) {
  final parts = _attachPreviewParts(attaches);
  if (parts == null) return null;
  final detail = parts.detail;
  return detail == null ? parts.label : '${parts.label}: $detail';
}

({String label, String? detail})? _attachPreviewParts(dynamic attaches) {
  final first = _firstPreviewAttach(attaches);
  if (first == null) return null;
  final type = (first['_type'] as String? ?? '').toUpperCase();
  switch (type) {
    case 'PHOTO':
      return (
        label: _mediaAttachCount(attaches) > 1 ? 'Изображения' : 'Изображение',
        detail: null,
      );
    case 'VIDEO':
      if (_isVideoNote(first)) return (label: 'Видео-сообщение', detail: null);
      return (label: 'Видео', detail: null);
    case 'AUDIO':
      return (label: 'Голосовое сообщение', detail: null);
    case 'FILE':
      return (label: 'Файл', detail: _nonEmpty(first['name']));
    case 'STICKER':
      return (label: 'Стикер', detail: null);
    case 'SHARE':
      return (label: 'Ссылка', detail: _nonEmpty(first['title']));
    case 'POLL':
      return (label: 'Опрос', detail: _nonEmpty(first['title']));
    case 'LOCATION':
      return (label: 'Геопозиция', detail: null);
    case 'CONTACT':
      return (label: 'Контакт', detail: null);
    case 'CONTROL':
      final label = _controlPreviewLabel(first);
      return label == null ? null : (label: label, detail: null);
    case 'INLINE_KEYBOARD':
      return null;
    case 'CALL':
      return (label: _callPreviewLabel(first), detail: null);
    default:
      return (label: 'Вложение', detail: null);
  }
}

ChatPreviewKind? _attachPreviewKind(Map attach) {
  switch ((attach['_type'] as String? ?? '').toUpperCase()) {
    case 'PHOTO':
      return ChatPreviewKind.photo;
    case 'VIDEO':
      return _isVideoNote(attach)
          ? ChatPreviewKind.videoNote
          : ChatPreviewKind.video;
    case 'AUDIO':
      return ChatPreviewKind.audio;
    case 'FILE':
      return ChatPreviewKind.file;
    case 'STICKER':
      return ChatPreviewKind.sticker;
    case 'SHARE':
      return ChatPreviewKind.share;
    case 'POLL':
      return ChatPreviewKind.poll;
    case 'LOCATION':
      return ChatPreviewKind.location;
    case 'CONTACT':
      return ChatPreviewKind.contact;
    case 'CONTROL':
      return ChatPreviewKind.control;
    case 'INLINE_KEYBOARD':
      return null;
    case 'CALL':
      final video = attach['callType']?.toString().toUpperCase() == 'VIDEO';
      if (_isFailedCall(attach)) {
        return video
            ? ChatPreviewKind.missedVideoCall
            : ChatPreviewKind.missedCall;
      }
      return video ? ChatPreviewKind.videoCall : ChatPreviewKind.call;
    default:
      return ChatPreviewKind.other;
  }
}

String _callPreviewLabel(Map attach) {
  final video = attach['callType']?.toString().toUpperCase() == 'VIDEO';
  if (attach['joinLink'] != null) {
    return video ? 'Групповой видеозвонок' : 'Групповой звонок';
  }
  if (_isFailedCall(attach)) {
    return video ? 'Пропущенный видеозвонок' : 'Пропущенный звонок';
  }
  return video ? 'Видеозвонок' : 'Звонок';
}

bool _isFailedCall(Map attach) {
  final duration = (attach['duration'] as num?)?.toInt() ?? 0;
  final hangup = attach['hangupType']?.toString();
  return duration == 0 ||
      hangup == 'CANCELED' ||
      hangup == 'REJECTED' ||
      hangup == 'MISSED';
}

String? _nonEmpty(dynamic raw) {
  final value = raw?.toString();
  return value != null && value.isNotEmpty ? value : null;
}

Map? _firstPreviewAttach(dynamic attaches) {
  if (attaches is! List || attaches.isEmpty) return null;
  for (final attach in attaches) {
    if (attach is! Map) continue;
    final type = (attach['_type'] as String? ?? '').toUpperCase();
    if (type == 'INLINE_KEYBOARD') continue;
    return attach;
  }
  return null;
}

int _mediaAttachCount(dynamic attaches) {
  if (attaches is! List) return 0;
  var count = 0;
  for (final attach in attaches.whereType<Map>()) {
    final type = (attach['_type'] as String? ?? '').toUpperCase();
    if (type == 'PHOTO' || (type == 'VIDEO' && !_isVideoNote(attach))) count++;
  }
  return count;
}

bool _isVideoNote(Map attach) {
  final raw = attach['videoType'];
  if (raw is int) return raw == 1;
  return raw?.toString() == '1';
}

String? _controlPreviewLabel(Map c) {
  final title = c['title']?.toString();
  if (title != null && title.isNotEmpty) return title;
  final short = c['shortMessage']?.toString();
  if (short != null && short.isNotEmpty) return short;
  switch (c['event']?.toString()) {
    case 'new':
      return 'Чат создан';
    case 'add':
    case 'joinByLink':
      return 'Новый участник';
    case 'leave':
      return 'Участник вышел';
    case 'remove':
      return 'Участник удалён';
    case 'pin':
      return 'Закреплённое сообщение';
    case 'changeTitle':
      return 'Название чата изменено';
    case 'changeIcon':
      return 'Фото чата обновлено';
    default:
      return 'Системное сообщение';
  }
}

String? messagePreviewText(Map msg) {
  final original = _forwardOrigin(msg);
  if (original != null) {
    final inner = original is Map ? _bodyPreviewText(original) : null;
    return inner != null && inner.isNotEmpty
        ? '↪ $inner'
        : '↪ Пересланное сообщение';
  }
  return _bodyPreviewText(msg);
}

String? messagePreviewMedia(Map msg) {
  final origin = _forwardOrigin(msg);
  final body = origin ?? msg;
  if (body is! Map) return null;
  final first = _firstPreviewAttach(body['attaches']);
  if (first == null) return null;
  final kind = _attachPreviewKind(first);
  if (kind == null) return null;

  final text = body['text']?.toString();
  final captioned = text != null && text.isNotEmpty;
  final parts = captioned ? null : _attachPreviewParts(body['attaches']);
  final label = parts == null
      ? null
      : (origin == null ? parts.label : '↪ ${parts.label}');

  return ChatPreviewMedia(
    kind: kind,
    thumbs: _previewThumbs(body['attaches']),
    label: label,
    detail: parts?.detail,
  ).encode();
}

dynamic _forwardOrigin(Map msg) {
  final link = msg['link'];
  if (link is! Map) return null;
  if (link['type']?.toString().toUpperCase() != 'FORWARD') return null;
  return link['message'];
}

List<ChatPreviewThumb> _previewThumbs(dynamic attaches) {
  if (attaches is! List) return const [];
  final thumbs = <ChatPreviewThumb>[];
  for (final attach in attaches.whereType<Map>()) {
    if (thumbs.length >= _maxPreviewThumbs) break;
    final type = (attach['_type'] as String? ?? '').toUpperCase();
    final isVideo = type == 'VIDEO';
    if (type != 'PHOTO' && !isVideo) continue;
    final source = _thumbSource(attach, isVideo);
    if (source == null) continue;
    thumbs.add(ChatPreviewThumb(source: source, video: isVideo));
  }
  return thumbs;
}

String? _thumbSource(Map attach, bool isVideo) {
  final data = decodeAttachPreview(attach['previewData']);
  if (data != null && data.length <= _maxThumbLength) return data;
  final url = isVideo
      ? _nonEmpty(attach['thumbnail'])
      : _nonEmpty(attach['baseUrl']);
  if (url != null && url.startsWith('http')) return url;
  return null;
}

({String? text, bool isPreview}) pinnedMessagePreview(Map msg) {
  final link = msg['link'];
  if (link is Map && link['type']?.toString().toUpperCase() == 'FORWARD') {
    final original = link['message'];
    if (original is Map) {
      final inner = pinnedMessagePreview(original);
      return inner.text != null && inner.text!.isNotEmpty
          ? (text: '↪ ${inner.text}', isPreview: inner.isPreview)
          : (text: '↪ пересланное сообщение', isPreview: true);
    }
    return (text: '↪ пересланное сообщение', isPreview: true);
  }
  return _pinnedBodyPreview(msg);
}

({String? text, bool isPreview}) _pinnedBodyPreview(Map msg) {
  final text = msg['text']?.toString();
  if (text != null && text.isNotEmpty) return (text: text, isPreview: false);
  final label = _pinnedAttachPreviewLabel(msg['attaches']);
  return (text: label, isPreview: label != null);
}

String? _pinnedAttachPreviewLabel(dynamic attaches) {
  final first = _firstPreviewAttach(attaches);
  if (first == null) return null;
  final type = (first['_type'] as String? ?? '').toUpperCase();
  switch (type) {
    case 'PHOTO':
      return 'фото';
    case 'VIDEO':
      return _isVideoNote(first) ? 'кружок' : 'видео';
    case 'AUDIO':
      return 'голосовое сообщение';
    case 'FILE':
      return 'файл';
    case 'STICKER':
      return 'стикер';
    case 'SHARE':
      return 'ссылка';
    case 'POLL':
      return 'голосование';
    case 'LOCATION':
      return 'геопозиция';
    case 'CONTACT':
      return 'контакт';
    case 'CALL':
      return 'звонок';
    case 'CONTROL':
      return _controlPreviewLabel(first)?.toLowerCase();
    default:
      return 'вложение';
  }
}

String? _bodyPreviewText(Map msg) {
  final text = msg['text']?.toString();
  if (text != null && text.isNotEmpty) return text;
  return attachPreviewLabel(msg['attaches']);
}

String? messagePreviewElements(Map msg) {
  final text = msg['text'];
  if (text is! String || text.isEmpty) return null;
  final elements = msg['elements'];
  if (elements is List && elements.isNotEmpty) return jsonEncode(elements);
  return null;
}
