/**
 * Общие утилиты-помощники
 */

/**
 * Проверка типов
 */
export const is = {
  string: (value) => typeof value === 'string',
  number: (value) => typeof value === 'number' && !isNaN(value),
  boolean: (value) => typeof value === 'boolean',
  function: (value) => typeof value === 'function',
  object: (value) => value !== null && typeof value === 'object',
  array: (value) => Array.isArray(value),
  null: (value) => value === null,
  undefined: (value) => typeof value === 'undefined',
  empty: (value) => {
    if (value == null) return true;
    if (is.string(value) || is.array(value)) return value.length === 0;
    if (is.object(value)) return Object.keys(value).length === 0;
    return false;
  },
  element: (value) => value instanceof Element,
  nodeList: (value) => value instanceof NodeList,
  date: (value) => value instanceof Date && !isNaN(value),
  promise: (value) => value && typeof value.then === 'function'
};

/**
 * Глубокое клонирование объекта
 */
export function deepClone(obj) {
  if (obj === null || typeof obj !== 'object') return obj;
  if (obj instanceof Date) return new Date(obj.getTime());
  if (obj instanceof Array) return obj.map(item => deepClone(item));
  if (typeof obj === 'object') {
    const clonedObj = {};
    Object.keys(obj).forEach(key => {
      clonedObj[key] = deepClone(obj[key]);
    });
    return clonedObj;
  }
}

/**
 * Глубокое слияние объектов
 */
export function deepMerge(target, ...sources) {
  if (!sources.length) return target;
  const source = sources.shift();

  if (is.object(target) && is.object(source)) {
    Object.keys(source).forEach(key => {
      if (is.object(source[key])) {
        if (!target[key]) Object.assign(target, { [key]: {} });
        deepMerge(target[key], source[key]);
      } else {
        Object.assign(target, { [key]: source[key] });
      }
    });
  }

  return deepMerge(target, ...sources);
}

/**
 * Получение значения по пути в объекте
 */
export function get(obj, path, defaultValue = undefined) {
  const keys = is.string(path) ? path.split('.') : path;
  let result = obj;

  for (const key of keys) {
    result = result?.[key];
    if (result === undefined) return defaultValue;
  }

  return result;
}

/**
 * Установка значения по пути в объекте
 */
export function set(obj, path, value) {
  const keys = is.string(path) ? path.split('.') : path;
  const lastKey = keys.pop();
  let current = obj;

  for (const key of keys) {
    if (!(key in current) || !is.object(current[key])) {
      current[key] = {};
    }
    current = current[key];
  }

  current[lastKey] = value;
  return obj;
}

/**
 * Форматирование строк (простой template engine)
 */
export function format(template, data) {
  return template.replace(/\{(\w+)\}/g, (match, key) => {
    return data[key] !== undefined ? data[key] : match;
  });
}

/**
 * Генерация случайной строки
 */
export function randomString(length = 8, chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789') {
  let result = '';
  for (let i = 0; i < length; i++) {
    result += chars.charAt(Math.floor(Math.random() * chars.length));
  }
  return result;
}

/**
 * Генерация UUID v4
 */
export function uuid() {
  return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
    const r = Math.random() * 16 | 0;
    const v = c === 'x' ? r : (r & 0x3 | 0x8);
    return v.toString(16);
  });
}

/**
 * Slug из строки
 */
export function slugify(text) {
  return text
    .toString()
    .toLowerCase()
    .replace(/\s+/g, '-')           // Заменяем пробелы на дефисы
    .replace(/[^\w\-]+/g, '')       // Удаляем не-алфавитные символы
    .replace(/\-\-+/g, '-')         // Заменяем множественные дефисы на одинарные
    .replace(/^-+/, '')             // Убираем дефисы в начале
    .replace(/-+$/, '');            // Убираем дефисы в конце
}

/**
 * Capitalize первую букву
 */
export function capitalize(str) {
  return str.charAt(0).toUpperCase() + str.slice(1);
}

/**
 * camelCase строки
 */
export function camelCase(str) {
  return str.replace(/[-_\s]+(.)?/g, (_, c) => c ? c.toUpperCase() : '');
}

/**
 * kebab-case строки
 */
export function kebabCase(str) {
  return str
    .replace(/([a-z])([A-Z])/g, '$1-$2')
    .replace(/[\s_]+/g, '-')
    .toLowerCase();
}

/**
 * Обрезка строки с многоточием
 */
export function truncate(str, length = 100, suffix = '...') {
  if (str.length <= length) return str;
  return str.substring(0, length - suffix.length) + suffix;
}

/**
 * Escape HTML
 */
export function escapeHtml(text) {
  const div = document.createElement('div');
  div.textContent = text;
  return div.innerHTML;
}

/**
 * Unescape HTML
 */
export function unescapeHtml(html) {
  const div = document.createElement('div');
  div.innerHTML = html;
  return div.textContent || div.innerText || '';
}

/**
 * Форматирование чисел
 */
export function formatNumber(num, options = {}) {
  const defaults = {
    locale: 'ru-RU',
    minimumFractionDigits: 0,
    maximumFractionDigits: 2
  };
  
  const config = { ...defaults, ...options };
  return new Intl.NumberFormat(config.locale, config).format(num);
}

/**
 * Форматирование валюты
 */
export function formatCurrency(amount, currency = 'RUB', locale = 'ru-RU') {
  return new Intl.NumberFormat(locale, {
    style: 'currency',
    currency: currency
  }).format(amount);
}

/**
 * Форматирование даты
 */
export function formatDate(date, options = {}) {
  const defaults = {
    locale: 'ru-RU',
    year: 'numeric',
    month: 'long',
    day: 'numeric'
  };
  
  const config = { ...defaults, ...options };
  return new Intl.DateTimeFormat(config.locale, config).format(new Date(date));
}

/**
 * Относительное время
 */
export function timeAgo(date, locale = 'ru-RU') {
  const now = new Date();
  const diffMs = now - new Date(date);
  const diffSec = Math.floor(diffMs / 1000);
  const diffMin = Math.floor(diffSec / 60);
  const diffHour = Math.floor(diffMin / 60);
  const diffDay = Math.floor(diffHour / 24);

  if (diffSec < 60) return 'только что';
  if (diffMin < 60) return `${diffMin} мин. назад`;
  if (diffHour < 24) return `${diffHour} ч. назад`;
  if (diffDay < 7) return `${diffDay} дн. назад`;
  
  return formatDate(date, { locale });
}

/**
 * Размер файла
 */
export function formatFileSize(bytes, decimals = 2) {
  if (bytes === 0) return '0 Bytes';

  const k = 1024;
  const dm = decimals < 0 ? 0 : decimals;
  const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];

  const i = Math.floor(Math.log(bytes) / Math.log(k));

  return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i];
}

/**
 * Проверка email
 */
export function isValidEmail(email) {
  const regex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
  return regex.test(email);
}

/**
 * Проверка URL
 */
export function isValidUrl(url) {
  try {
    new URL(url);
    return true;
  } catch {
    return false;
  }
}

/**
 * Получение параметров URL
 */
export function getUrlParams(url = window.location.href) {
  const urlObj = new URL(url);
  return Object.fromEntries(urlObj.searchParams);
}

/**
 * Обновление URL параметров
 */
export function updateUrlParams(params, replace = true) {
  const url = new URL(window.location);
  
  Object.entries(params).forEach(([key, value]) => {
    if (value === null || value === undefined) {
      url.searchParams.delete(key);
    } else {
      url.searchParams.set(key, value);
    }
  });
  
  const method = replace ? 'replaceState' : 'pushState';
  window.history[method]({}, '', url);
}

/**
 * Копирование в буфер обмена
 */
export async function copyToClipboard(text) {
  try {
    if (navigator.clipboard) {
      await navigator.clipboard.writeText(text);
      return true;
    } else {
      // Fallback для старых браузеров
      const textArea = document.createElement('textarea');
      textArea.value = text;
      textArea.style.position = 'fixed';
      textArea.style.opacity = '0';
      document.body.appendChild(textArea);
      textArea.focus();
      textArea.select();
      document.execCommand('copy');
      document.body.removeChild(textArea);
      return true;
    }
  } catch (err) {
    console.error('Ошибка копирования в буфер обмена:', err);
    return false;
  }
}

/**
 * Загрузка файла
 */
export function downloadFile(data, filename, type = 'application/octet-stream') {
  const file = new Blob([data], { type });
  const url = URL.createObjectURL(file);
  const link = document.createElement('a');
  
  link.href = url;
  link.download = filename;
  link.style.display = 'none';
  
  document.body.appendChild(link);
  link.click();
  document.body.removeChild(link);
  
  URL.revokeObjectURL(url);
}

/**
 * Преобразование FormData в объект
 */
export function formDataToObject(formData) {
  const obj = {};
  for (const [key, value] of formData.entries()) {
    if (obj[key]) {
      if (!Array.isArray(obj[key])) {
        obj[key] = [obj[key]];
      }
      obj[key].push(value);
    } else {
      obj[key] = value;
    }
  }
  return obj;
}

/**
 * Преобразование объекта в FormData
 */
export function objectToFormData(obj) {
  const formData = new FormData();
  
  Object.entries(obj).forEach(([key, value]) => {
    if (Array.isArray(value)) {
      value.forEach(item => formData.append(key, item));
    } else {
      formData.append(key, value);
    }
  });
  
  return formData;
}

/**
 * Ретрай функции с экспоненциальной задержкой
 */
export async function retry(fn, maxAttempts = 3, delay = 1000) {
  let lastError;
  
  for (let attempt = 1; attempt <= maxAttempts; attempt++) {
    try {
      return await fn();
    } catch (error) {
      lastError = error;
      
      if (attempt === maxAttempts) {
        throw lastError;
      }
      
      const waitTime = delay * Math.pow(2, attempt - 1);
      await new Promise(resolve => setTimeout(resolve, waitTime));
    }
  }
}