const BLOCKED_CHAT_MEDIA_TYPES = new Set([
  'application/javascript',
  'application/x-javascript',
  'text/javascript',
  'text/html',
  'application/xhtml+xml',
  'application/x-httpd-php',
  'application/x-sh',
  'application/x-msdownload'
]);

const EXTENSION_TO_MIME: Record<string, string> = {
  jpg: 'image/jpeg',
  jpeg: 'image/jpeg',
  png: 'image/png',
  webp: 'image/webp',
  gif: 'image/gif',
  heic: 'image/heic',
  heif: 'image/heif',
  mp3: 'audio/mpeg',
  m4a: 'audio/mp4',
  ogg: 'audio/ogg',
  opus: 'audio/ogg',
  webm: 'audio/webm',
  wav: 'audio/wav',
  aac: 'audio/aac',
  flac: 'audio/flac',
  mp4: 'video/mp4',
  mov: 'video/quicktime',
  pdf: 'application/pdf',
  doc: 'application/msword',
  docx: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
  xls: 'application/vnd.ms-excel',
  xlsx: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
  ppt: 'application/vnd.ms-powerpoint',
  pptx: 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
  txt: 'text/plain',
  csv: 'text/csv',
  zip: 'application/zip',
  rar: 'application/vnd.rar',
  '7z': 'application/x-7z-compressed',
  json: 'application/json',
  xml: 'application/xml'
};

const MIME_ALIASES: Record<string, string> = {
  'audio/x-m4a': 'audio/mp4',
  'audio/x-aac': 'audio/aac',
  'audio/x-wav': 'audio/wav',
  'audio/x-flac': 'audio/flac',
  'audio/x-mpeg': 'audio/mpeg',
  'audio/mp3': 'audio/mpeg',
  'image/jpg': 'image/jpeg',
  'image/pjpeg': 'image/jpeg',
  'application/x-pdf': 'application/pdf',
  'application/x-zip-compressed': 'application/zip'
};

export type ChatAttachmentKind = 'IMAGE' | 'AUDIO' | 'VOICE' | 'FILE';

export function normalizeChatContentType(contentType?: string | null, fileName?: string | null) {
  const raw = (contentType ?? '').trim().toLowerCase();
  const base = raw.split(';')[0]?.trim() ?? '';
  const aliased = MIME_ALIASES[base] ?? base;

  if (aliased && aliased !== 'application/octet-stream') {
    return aliased;
  }

  const extension = extractFileExtension(fileName);
  if (extension && EXTENSION_TO_MIME[extension]) {
    return EXTENSION_TO_MIME[extension];
  }

  return aliased || 'application/octet-stream';
}

export function assertChatMediaContentType(contentType?: string | null, fileName?: string | null) {
  const normalized = normalizeChatContentType(contentType, fileName);
  if (BLOCKED_CHAT_MEDIA_TYPES.has(normalized)) {
    throw new Error('Этот тип файла нельзя отправить в чат');
  }

  const [category] = normalized.split('/');
  if (category === 'image' || category === 'audio' || category === 'video') {
    return normalized;
  }

  const allowedApplicationTypes = new Set([
    'application/pdf',
    'application/msword',
    'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
    'application/vnd.ms-excel',
    'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
    'application/vnd.ms-powerpoint',
    'application/vnd.openxmlformats-officedocument.presentationml.presentation',
    'application/zip',
    'application/vnd.rar',
    'application/x-7z-compressed',
    'application/json',
    'application/xml',
    'application/octet-stream'
  ]);

  if (category === 'text' || allowedApplicationTypes.has(normalized)) {
    return normalized;
  }

  throw new Error('Неподдерживаемый тип медиафайла для чата');
}

export function extensionForChatContentType(contentType: string, fileName?: string | null) {
  const normalized = normalizeChatContentType(contentType, fileName);
  switch (normalized) {
    case 'image/jpeg':
      return 'jpg';
    case 'image/png':
      return 'png';
    case 'image/webp':
      return 'webp';
    case 'image/gif':
      return 'gif';
    case 'image/heic':
      return 'heic';
    case 'image/heif':
      return 'heif';
    case 'audio/mpeg':
      return 'mp3';
    case 'audio/mp4':
      return 'm4a';
    case 'audio/ogg':
      return 'ogg';
    case 'audio/webm':
      return 'webm';
    case 'audio/wav':
      return 'wav';
    case 'audio/aac':
      return 'aac';
    case 'audio/flac':
      return 'flac';
    case 'video/mp4':
      return 'mp4';
    case 'video/quicktime':
      return 'mov';
    case 'application/pdf':
      return 'pdf';
    case 'application/msword':
      return 'doc';
    case 'application/vnd.openxmlformats-officedocument.wordprocessingml.document':
      return 'docx';
    case 'application/vnd.ms-excel':
      return 'xls';
    case 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet':
      return 'xlsx';
    case 'application/vnd.ms-powerpoint':
      return 'ppt';
    case 'application/vnd.openxmlformats-officedocument.presentationml.presentation':
      return 'pptx';
    case 'text/plain':
      return 'txt';
    case 'text/csv':
      return 'csv';
    case 'application/zip':
      return 'zip';
    case 'application/vnd.rar':
      return 'rar';
    case 'application/x-7z-compressed':
      return '7z';
    case 'application/json':
      return 'json';
    case 'application/xml':
      return 'xml';
    default: {
      const fromName = extractFileExtension(fileName);
      if (fromName) return fromName;
      return 'bin';
    }
  }
}

export function contentTypeForStorageKey(storageKey: string) {
  const extension = extractFileExtension(storageKey);
  if (extension && EXTENSION_TO_MIME[extension]) {
    return EXTENSION_TO_MIME[extension];
  }
  if (storageKey.endsWith('.bin')) return 'application/octet-stream';
  return 'application/octet-stream';
}

export function inferChatMessageType(contentType: string, options?: { voice?: boolean }): ChatAttachmentKind {
  const normalized = normalizeChatContentType(contentType);
  if (normalized.startsWith('image/')) return 'IMAGE';
  if (normalized.startsWith('video/')) return 'FILE';
  if (normalized.startsWith('audio/')) {
    return options?.voice ? 'VOICE' : 'AUDIO';
  }
  return 'FILE';
}

function extractFileExtension(value?: string | null) {
  if (!value) return '';
  const clean = value.split('?')[0]?.split('#')[0] ?? value;
  const parts = clean.split('.');
  if (parts.length < 2) return '';
  return parts.pop()?.trim().toLowerCase() ?? '';
}
