import { clsx, type ClassValue } from 'clsx';

export function cn(...inputs: ClassValue[]) {
  return clsx(inputs);
}

export function formatTime(date: string | Date, lang: string = 'ru'): string {
  const d = new Date(date);
  return d.toLocaleTimeString(lang === 'ru' ? 'ru-RU' : 'en-US', { hour: '2-digit', minute: '2-digit' });
}

export function formatDate(date: string | Date, lang: string = 'ru'): string {
  const d = new Date(date);
  const now = new Date();
  const diff = now.getTime() - d.getTime();
  const days = Math.floor(diff / (1000 * 60 * 60 * 24));

  if (days === 0) return lang === 'ru' ? 'Сегодня' : 'Today';
  if (days === 1) return lang === 'ru' ? 'Вчера' : 'Yesterday';
  if (days < 7) {
    const weekDaysRu = ['Воскресенье', 'Понедельник', 'Вторник', 'Среда', 'Четверг', 'Пятница', 'Суббота'];
    const weekDaysEn = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
    return (lang === 'ru' ? weekDaysRu : weekDaysEn)[d.getDay()];
  }

  return d.toLocaleDateString(lang === 'ru' ? 'ru-RU' : 'en-US', {
    day: 'numeric',
    month: 'long',
    year: days > 365 ? 'numeric' : undefined,
  });
}

export function formatLastSeen(date: string | Date, lang: string = 'ru'): string {
  const d = new Date(date);
  const now = new Date();
  const diff = now.getTime() - d.getTime();
  const minutes = Math.floor(diff / (1000 * 60));

  if (minutes < 1) return lang === 'ru' ? 'только что' : 'just now';
  if (minutes < 60) return lang === 'ru' ? `${minutes} мин. назад` : `${minutes}m ago`;

  const hours = Math.floor(minutes / 60);
  if (hours < 24) return lang === 'ru' ? `${hours} ч. назад` : `${hours}h ago`;

  const at = lang === 'ru' ? ' в ' : ' at ';
  return formatDate(date, lang) + at + formatTime(date, lang);
}

/**
 * Strips markdown syntax (**bold**, *italic*, _italic_, ~strikethrough~, `code`)
 * and returns plain text for use in previews.
 */
export function stripMarkdown(text: string): string {
  if (!text) return text;
  return text
    .replace(/\*\*([\s\S]*?)\*\*/g, '$1')
    .replace(/\*([\s\S]*?)\*/g, '$1')
    .replace(/_([\s\S]*?)_/g, '$1')
    .replace(/~([\s\S]*?)~/g, '$1')
    .replace(/`([\s\S]*?)`/g, '$1');
}

export function getInitials(name: string): string {
  if (!name) return '?';
  const parts = name.trim().split(/\s+/);
  if (parts.length >= 2) {
    return (parts[0][0] + parts[1][0]).toUpperCase();
  }
  return parts[0].slice(0, 2).toUpperCase();
}

export function generateAvatarColor(_name: string): string {
  return 'bg-gradient-to-br from-primary to-primary-container';
}

// Waveform cache so we don't decode the same audio twice
const waveformCache = new Map<string, number[]>();

/**
 * Decodes an audio file from a URL and extracts normalized waveform peak values.
 * Returns an array of `bars` values in [0, 1].
 */
export async function extractWaveform(url: string, bars: number = 28): Promise<number[]> {
  const cached = waveformCache.get(url);
  if (cached) return cached;

  let audioCtx: any;
  try {
    const response = await fetch(url);
    const arrayBuffer = await response.arrayBuffer();
    const OfflineCtx = window.OfflineAudioContext || (window as any).webkitOfflineAudioContext;
    if (OfflineCtx) {
      audioCtx = new OfflineCtx(1, 1, 44100);
    } else {
      audioCtx = new (window.AudioContext || (window as any).webkitAudioContext)();
    }
    
    // We only need the buffer, so decode it
    const audioBuffer = await audioCtx!.decodeAudioData(arrayBuffer);
    
    // If it was a regular AudioContext, close it.
    if (audioCtx.close) {
      await audioCtx.close();
    }
    audioCtx = undefined;

    const channelData = audioBuffer.getChannelData(0);
    const samplesPerBar = Math.floor(channelData.length / bars);
    const peaks: number[] = [];

    for (let i = 0; i < bars; i++) {
      let peak = 0;
      const start = i * samplesPerBar;
      // Sample a subset for performance
      const step = Math.max(1, Math.floor(samplesPerBar / 200));
      for (let j = 0; j < samplesPerBar; j += step) {
        const abs = Math.abs(channelData[start + j] || 0);
        if (abs > peak) peak = abs;
      }
      peaks.push(peak);
    }

    // Normalize to [0, 1]
    const max = Math.max(...peaks, 0.01);
    const normalized = peaks.map(p => p / max);
    waveformCache.set(url, normalized);
    return normalized;
  } catch {
    // Close leaked AudioContext if any
    if (audioCtx && audioCtx.close) audioCtx.close().catch(() => {});
    // On error, return uniform bars
    return Array(bars).fill(0.5);
  }
}

export function getMediaUrl(url: string | null | undefined): string {
  if (!url) return '';
  if (url.startsWith('http') || url.startsWith('blob:') || url.startsWith('data:')) return url;
  
  // Use VITE_API_URL if defined, but avoid duplicating '/api'
  const baseUrl = (import.meta.env.VITE_API_URL || '').replace(/\/api$/, '');
  return `${baseUrl}${url.startsWith('/') ? '' : '/'}${url}`;
}
