export const buildUrl = (appBase, path) => new URL(path, appBase).toString();

export const getAppBase = () => {
  const baseEl = document.querySelector('base');
  if (baseEl && baseEl.href) {
    return baseEl.href;
  }
  return `${window.location.origin}/`;
};

const resolveAppBaseUrl = () => {
  try {
    return new URL(getAppBase(), `${window.location.origin}/`);
  } catch {
    return new URL(`${window.location.origin}/`);
  }
};

const normalizeBasePathname = (pathname) => {
  const trimmed = String(pathname ?? '').trim();
  if (trimmed === '' || trimmed === '/') {
    return '/';
  }
  return `/${trimmed.replace(/^\/+|\/+$/g, '')}/`;
};

const toAppRelativePath = (pathname, appBasePathname) => {
  const normalizedPath = `/${String(pathname ?? '').replace(/^\/+/, '')}`;
  const normalizedBasePath = normalizeBasePathname(appBasePathname);
  let relativePath = normalizedPath;

  if (normalizedBasePath !== '/' && relativePath.startsWith(normalizedBasePath)) {
    relativePath = relativePath.slice(normalizedBasePath.length - 1);
  } else if (normalizedBasePath !== '/' && relativePath === normalizedBasePath.slice(0, -1)) {
    relativePath = '/';
  }

  return relativePath.replace(/^\/+/, '');
};

const parseAppUrl = (value, baseUrl = resolveAppBaseUrl()) => {
  const raw = String(value ?? '').trim();
  if (raw === '') {
    return null;
  }
  try {
    return new URL(raw, baseUrl);
  } catch {
    return null;
  }
};

export const normalizeListReturnTarget = (sourceUrl = window.location.href) => {
  const appBaseUrl = resolveAppBaseUrl();
  const parsed = parseAppUrl(sourceUrl, appBaseUrl);
  if (!parsed) {
    return '';
  }
  const relativePath = toAppRelativePath(parsed.pathname, appBaseUrl.pathname);
  if (relativePath === '') {
    return '';
  }
  const query = new URLSearchParams(parsed.search);
  query.delete('return');
  const queryString = query.toString();
  return `${relativePath}${queryString ? `?${queryString}` : ''}`;
};

export const withReturnTarget = (targetUrl, returnTarget = '') => {
  const appBaseUrl = resolveAppBaseUrl();
  const parsedTarget = parseAppUrl(targetUrl, appBaseUrl);
  if (!parsedTarget) {
    return String(targetUrl ?? '');
  }
  if (parsedTarget.origin !== window.location.origin) {
    return String(targetUrl ?? '');
  }
  const normalizedReturn = normalizeListReturnTarget(returnTarget);
  parsedTarget.searchParams.delete('return');
  if (normalizedReturn !== '') {
    parsedTarget.searchParams.set('return', normalizedReturn);
  }
  return parsedTarget.toString();
};

export const withCurrentListReturn = (targetUrl) => withReturnTarget(targetUrl, window.location.href);

export const escapeHtml = (value) => String(value ?? '').replace(/[&<>"']/g, (char) => ({
  '&': '&amp;',
  '<': '&lt;',
  '>': '&gt;',
  '"': '&quot;',
  "'": '&#39;'
}[char]));

export const buildBadgeList = (items, options = {}) => {
  const {
    variant = 'neutral',
    primaryVariant = 'info',
    primaryTooltip = ''
  } = options;

  let list = items;
  let primary = '';
  if (items && typeof items === 'object' && !Array.isArray(items)) {
    list = Array.isArray(items.items) ? items.items : [];
    primary = String(items.primary ?? '');
  }
  if (!Array.isArray(list) || list.length === 0) {return '';}

  const tooltipAttr = primaryTooltip
    ? ` data-tooltip="${escapeHtml(primaryTooltip)}" data-tooltip-pos="top"`
    : '';

  const badges = list.map((label) => {
    const labelText = String(label ?? '');
    const isPrimary = primary !== '' && labelText === primary;
    const badgeVariant = isPrimary ? primaryVariant : variant;
    const badgeTooltip = isPrimary && primaryTooltip ? tooltipAttr : '';
    return `<span class="badge" data-variant="${badgeVariant}"${badgeTooltip}>${escapeHtml(labelText)}</span>`;
  }).join(' ');

  return `<div class="badge-list">${badges}</div>`;
};

export const postAction = async (url, csrf, data = {}) => {
  const body = new URLSearchParams({ ...data, [csrf.key]: csrf.token });
  return fetch(url, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/x-www-form-urlencoded',
      'Accept': 'application/json',
      'X-Requested-With': 'fetch'
    },
    body
  });
};

export const badgeHtml = (gridjs, value = '') => {
  const safe = value ?? '';
  return gridjs.html(`<span class="badge" data-variant="neutral">${safe}</span>`);
};

export const normalizeMultiSelectValue = (value) => {
  if (Array.isArray(value)) {
    return value
      .map((item) => String(item ?? '').trim())
      .filter(Boolean);
  }
  if (typeof value === 'string') {
    return value
      .split(',')
      .map((item) => item.trim())
      .filter(Boolean);
  }
  return [];
};

export const gridFilterSelect = (input, param, options = {}) => ({
  input,
  param,
  ...options
});

export const gridFilterDate = (input, param, options = {}) => ({
  input,
  param,
  normalize: (value) => {
    const date = String(value ?? '').trim();
    return /^\d{4}-\d{2}-\d{2}$/.test(date) ? date : '';
  },
  ...options
});

export const gridFilterText = (input, param, options = {}) => ({
  input,
  param,
  normalize: (value) => String(value ?? '').trim(),
  ...options
});

export const gridFilterMultiCsv = (input, param, options = {}) => ({
  input,
  param,
  normalize: normalizeMultiSelectValue,
  ...options
});

export const gridFilterNumber = (input, param, options = {}) => ({
  input,
  param,
  normalize: (value) => {
    const raw = String(value ?? '').trim();
    if (raw === '') {
      return '';
    }
    const parsed = Number(raw);
    return Number.isFinite(parsed) ? String(parsed) : '';
  },
  ...options
});

export const gridFilterHidden = (input, param, options = {}) => ({
  input,
  param,
  normalize: (value) => String(value ?? '').trim(),
  ...options
});

const NORMALIZERS = {
  trim: (value) => String(value ?? '').trim(),
  trim_lower: (value) => String(value ?? '').trim().toLowerCase(),
  all_to_empty: (value) => {
    const normalized = String(value ?? '').trim();
    return normalized === '' || normalized === 'all' ? '' : normalized;
  },
  csv: normalizeMultiSelectValue
};

export const gridFiltersFromSchema = (schema) => {
  if (!Array.isArray(schema)) {
    return [];
  }

  return schema.map((entry) => {
    if (!entry || typeof entry !== 'object') {
      return null;
    }

    const type = String(entry.type ?? '').trim().toLowerCase();
    const input = entry.input ?? '';
    const param = String(entry.param ?? '').trim();
    if (!input || !param) {
      return null;
    }

    const options = {};
    if ('default' in entry) {
      options.default = entry.default;
    }
    if (typeof entry.event === 'string' && entry.event.trim() !== '') {
      options.event = entry.event.trim();
    }
    if (typeof entry.normalize === 'string' && NORMALIZERS[entry.normalize]) {
      options.normalize = NORMALIZERS[entry.normalize];
    }

    if (type === 'date') {
      return gridFilterDate(input, param, options);
    }
    if (type === 'select') {
      return gridFilterSelect(input, param, options);
    }
    if (type === 'multi_csv') {
      return gridFilterMultiCsv(input, param, options);
    }
    if (type === 'number') {
      return gridFilterNumber(input, param, options);
    }
    if (type === 'hidden') {
      return gridFilterHidden(input, param, options);
    }
    if (type === 'text') {
      return gridFilterText(input, param, options);
    }

    return null;
  }).filter(Boolean);
};
