import {
  ALL_OPERATORS,
  INVOICE_STATUS_LABELS,
  REQUEST_UPDATE_EVENT_LABELS,
  ROLE_LABELS,
  STATUS_KIND_LABELS,
  STATUS_LABELS,
} from "./constants.js";

export function resolveAdminRoute(search) {
  const params = new URLSearchParams(String(search || ""));
  const section = String(params.get("section") || "").trim();
  const view = String(params.get("view") || "").trim();
  const requestId = String(params.get("requestId") || "").trim();
  return { section, view, requestId };
}

export function humanizeKey(value) {
  const text = String(value || "")
    .replace(/[_-]+/g, " ")
    .replace(/\s+/g, " ")
    .trim();
  if (!text) return "-";
  return text.charAt(0).toUpperCase() + text.slice(1);
}

export function metaKindToFilterType(kind) {
  if (kind === "boolean") return "boolean";
  if (kind === "number") return "number";
  if (kind === "date" || kind === "datetime") return "date";
  return "text";
}

export function metaKindToRecordType(kind) {
  if (kind === "boolean") return "boolean";
  if (kind === "number") return "number";
  if (kind === "json") return "json";
  return "text";
}

export function decodeJwtPayload(token) {
  try {
    const payload = token.split(".")[1] || "";
    const base64 = payload.replace(/-/g, "+").replace(/_/g, "/");
    const json = decodeURIComponent(
      atob(base64)
        .split("")
        .map((c) => "%" + ("00" + c.charCodeAt(0).toString(16)).slice(-2))
        .join("")
    );
    return JSON.parse(json);
  } catch (_) {
    return null;
  }
}

export function sortByName(items) {
  return [...items].sort((a, b) => String(a.name || a.code || "").localeCompare(String(b.name || b.code || ""), "ru"));
}

export function roleLabel(role) {
  return ROLE_LABELS[role] || role || "-";
}

export function statusLabel(code) {
  return STATUS_LABELS[code] || code || "-";
}

export function invoiceStatusLabel(code) {
  return INVOICE_STATUS_LABELS[code] || code || "-";
}

export function statusKindLabel(code) {
  return STATUS_KIND_LABELS[code] || code || "-";
}

export function fallbackStatusGroup(statusCode) {
  const code = String(statusCode || "").toUpperCase();
  if (!code) return "NEW";
  if (code.startsWith("NEW")) return "NEW";
  if (code.includes("WAIT") || code.includes("PEND") || code.includes("HOLD")) return "WAITING";
  if (code.includes("CLOSE") || code.includes("RESOLV") || code.includes("REJECT") || code.includes("DONE") || code.includes("PAID")) return "DONE";
  return "IN_PROGRESS";
}

export function boolLabel(value) {
  return value ? "Да" : "Нет";
}

export function boolFilterLabel(value) {
  return value ? "True" : "False";
}

export function fmtDate(value) {
  if (!value) return "-";
  const date = new Date(value);
  if (Number.isNaN(date.getTime())) return String(value);
  const day = String(date.getDate()).padStart(2, "0");
  const month = String(date.getMonth() + 1).padStart(2, "0");
  const year = String(date.getFullYear()).slice(-2);
  const hours = String(date.getHours()).padStart(2, "0");
  const minutes = String(date.getMinutes()).padStart(2, "0");
  return `${day}.${month}.${year} ${hours}:${minutes}`;
}

export function fmtDateOnly(value) {
  if (!value) return "-";
  const date = new Date(value);
  if (Number.isNaN(date.getTime())) return String(value);
  const day = String(date.getDate()).padStart(2, "0");
  const month = String(date.getMonth() + 1).padStart(2, "0");
  const year = String(date.getFullYear()).slice(-2);
  return `${day}.${month}.${year}`;
}

export function fmtTimeOnly(value) {
  if (!value) return "-";
  const date = new Date(value);
  return Number.isNaN(date.getTime())
    ? String(value)
    : date.toLocaleTimeString("ru-RU", { hour: "2-digit", minute: "2-digit" });
}

export function fmtKanbanDate(value) {
  if (!value) return "-";
  const date = new Date(value);
  if (Number.isNaN(date.getTime())) return String(value);
  const day = String(date.getDate()).padStart(2, "0");
  const month = String(date.getMonth() + 1).padStart(2, "0");
  const year = String(date.getFullYear()).slice(-2);
  const hours = String(date.getHours()).padStart(2, "0");
  const minutes = String(date.getMinutes()).padStart(2, "0");
  return `${day}.${month}.${year} ${hours}:${minutes}`;
}

export function fmtShortDateTime(value) {
  if (!value) return "-";
  const date = new Date(value);
  if (Number.isNaN(date.getTime())) return String(value);
  const day = String(date.getDate()).padStart(2, "0");
  const month = String(date.getMonth() + 1).padStart(2, "0");
  const year = String(date.getFullYear()).slice(-2);
  const hours = String(date.getHours()).padStart(2, "0");
  const minutes = String(date.getMinutes()).padStart(2, "0");
  return `${day}.${month}.${year} ${hours}:${minutes}`;
}

export function resolveDeadlineTone(value) {
  if (!value) return "ok";
  const time = new Date(value).getTime();
  if (!Number.isFinite(time)) return "ok";
  const delta = time - Date.now();
  const fourDaysMs = 4 * 24 * 60 * 60 * 1000;
  const oneDayMs = 24 * 60 * 60 * 1000;
  if (delta > fourDaysMs) return "ok";
  if (delta > oneDayMs) return "warn";
  return "danger";
}

export function fmtAmount(value) {
  if (value == null || value === "") return "-";
  const number = Number(value);
  if (Number.isNaN(number)) return String(value);
  return number.toLocaleString("ru-RU");
}

export function fmtBytes(value) {
  const size = Number(value || 0);
  if (!Number.isFinite(size) || size <= 0) return "0 Б";
  const units = ["Б", "КБ", "МБ", "ГБ"];
  let normalized = size;
  let index = 0;
  while (normalized >= 1024 && index < units.length - 1) {
    normalized /= 1024;
    index += 1;
  }
  return normalized.toLocaleString("ru-RU", { maximumFractionDigits: index === 0 ? 0 : 1 }) + " " + units[index];
}

export function normalizeStringList(value) {
  if (!Array.isArray(value)) return [];
  const out = [];
  const seen = new Set();
  value.forEach((item) => {
    const text = String(item || "").trim();
    if (!text) return;
    const key = text.toLowerCase();
    if (seen.has(key)) return;
    seen.add(key);
    out.push(text);
  });
  return out;
}

export function listPreview(value, emptyLabel) {
  const items = normalizeStringList(value);
  return items.length ? items.join(", ") : emptyLabel;
}

export function normalizeReferenceMeta(raw) {
  if (!raw || typeof raw !== "object") return null;
  const table = String(raw.table || "").trim();
  const valueField = String(raw.value_field || "id").trim() || "id";
  const labelField = String(raw.label_field || valueField).trim() || valueField;
  if (!table) return null;
  return { table, value_field: valueField, label_field: labelField };
}

export function userInitials(name, email) {
  const source = String(name || "").trim();
  if (source) {
    const parts = source.split(/\s+/).filter(Boolean);
    if (parts.length >= 2) return (parts[0][0] + parts[1][0]).toUpperCase();
    return source.slice(0, 2).toUpperCase();
  }
  const mail = String(email || "").trim();
  return (mail.slice(0, 2) || "U").toUpperCase();
}

export function avatarColor(seed) {
  const palette = ["#6f8fa9", "#568f7d", "#a07a5c", "#7d6ea9", "#8f6f8f", "#7f8c5a"];
  const text = String(seed || "");
  let hash = 0;
  for (let i = 0; i < text.length; i += 1) hash = (hash * 31 + text.charCodeAt(i)) >>> 0;
  return palette[hash % palette.length];
}

export function resolveAvatarSrc(avatarUrl, accessToken, size) {
  const raw = String(avatarUrl || "").trim();
  if (!raw) return "";
  if (raw.startsWith("s3://")) {
    const key = raw.slice("s3://".length);
    if (!key || !accessToken) return "";
    const useThumb = Number(size || 0) > 0 && Number(size || 0) <= 160;
    return (
      "/api/admin/uploads/object/" +
      encodeURIComponent(key) +
      "?token=" +
      encodeURIComponent(accessToken) +
      (useThumb ? "&variant=thumb" : "")
    );
  }
  return raw;
}

export function resolveAdminObjectSrc(s3Key, accessToken) {
  const key = String(s3Key || "").trim();
  if (!key || !accessToken) return "";
  return "/api/admin/uploads/object/" + encodeURIComponent(key) + "?token=" + encodeURIComponent(accessToken);
}

export function detectAttachmentPreviewKind(fileName, mimeType) {
  const name = String(fileName || "").toLowerCase();
  const mime = String(mimeType || "").toLowerCase();
  if (/\.(txt|md|csv|json|log|xml|ya?ml|ini|cfg)$/i.test(name)) return "text";
  if (
    mime.startsWith("text/") ||
    mime === "application/json" ||
    mime === "application/xml" ||
    mime === "text/xml"
  ) {
    return "text";
  }
  if (mime.startsWith("image/") || /\.(png|jpe?g|gif|webp|bmp|svg)$/.test(name)) return "image";
  if (mime.startsWith("video/") || /\.(mp4|webm|ogg|mov|m4v)$/.test(name)) return "video";
  if (mime === "application/pdf" || /\.pdf$/.test(name)) return "pdf";
  return "none";
}

export function buildUniversalQuery(filters, sort, limit, offset) {
  return {
    filters: filters || [],
    sort: sort || [],
    page: { limit: limit ?? 50, offset: offset ?? 0 },
  };
}

export function canAccessSection(role, section) {
  const roleCode = String(role || "").toUpperCase();
  const allowed = new Set([
    "dashboard",
    "kanban",
    "requests",
    "serviceRequests",
    "requestWorkspace",
    "invoices",
    "meta",
    "quotes",
    "config",
    "availableTables",
  ]);
  if (!allowed.has(section)) return false;
  if (section === "requests") return roleCode === "ADMIN" || roleCode === "LAWYER";
  if (section === "serviceRequests") return roleCode === "ADMIN" || roleCode === "CURATOR";
  if (section === "quotes" || section === "config" || section === "availableTables") return roleCode === "ADMIN";
  return true;
}

export function translateApiError(message) {
  const direct = {
    "Missing auth token": "Отсутствует токен авторизации",
    "Missing bearer token": "Отсутствует токен авторизации",
    "Invalid token": "Некорректный токен",
    Forbidden: "Недостаточно прав",
    "Invalid credentials": "Неверный логин или пароль",
    "Request not found": "Заявка не найдена",
    "Quote not found": "Цитата не найдена",
    not_found: "Запись не найдена",
  };
  if (direct[message]) return direct[message];
  if (String(message).startsWith("HTTP ")) return "Ошибка сервера (" + message + ")";
  return message;
}

export function getOperatorsForType(type) {
  if (type === "number" || type === "date" || type === "datetime") return ["=", "!=", ">", "<", ">=", "<="];
  if (type === "boolean" || type === "reference" || type === "enum") return ["=", "!="];
  return [...ALL_OPERATORS];
}

export function localizeRequestDetails(row) {
  return {
    ID: row.id || null,
    "Номер заявки": row.track_number || null,
    Клиент: row.client_name || null,
    Телефон: row.client_phone || null,
    "Тема (код)": row.topic_code || null,
    Статус: statusLabel(row.status_code),
    Описание: row.description || null,
    "Дополнительные поля": row.extra_fields || {},
    "Назначенный юрист (ID)": row.assigned_lawyer_id || null,
    "Ставка (фикс.)": row.effective_rate ?? null,
    "Сумма счета": row.invoice_amount ?? null,
    "Оплачено": row.paid_at ? fmtDate(row.paid_at) : null,
    "Оплату подтвердил (ID)": row.paid_by_admin_id || null,
    "Непрочитано клиентом": boolLabel(Boolean(row.client_has_unread_updates)),
    "Тип обновления для клиента": row.client_unread_event_type
      ? (REQUEST_UPDATE_EVENT_LABELS[row.client_unread_event_type] || row.client_unread_event_type)
      : null,
    "Непрочитано юристом": boolLabel(Boolean(row.lawyer_has_unread_updates)),
    "Тип обновления для юриста": row.lawyer_unread_event_type
      ? (REQUEST_UPDATE_EVENT_LABELS[row.lawyer_unread_event_type] || row.lawyer_unread_event_type)
      : null,
    "Общий размер вложений (байт)": row.total_attachments_bytes ?? 0,
    Создано: fmtDate(row.created_at),
    Обновлено: fmtDate(row.updated_at),
  };
}

export function localizeMeta(data) {
  const fieldTypeMap = {
    string: "строка",
    text: "текст",
    boolean: "булево",
    number: "число",
    date: "дата",
  };
  return {
    Сущность: data.entity,
    Поля: (data.fields || []).map((field) => ({
      "Код поля": field.field_name,
      Название: field.label,
      Тип: fieldTypeMap[field.type] || field.type,
      Обязательное: boolLabel(field.required),
      "Только чтение": boolLabel(field.read_only),
      "Редактируемые роли": (field.editable_roles || []).map(roleLabel),
    })),
  };
}
