import crypto from 'crypto';

export const asText = (value: unknown): string => {
  if (value === null || value === undefined) return '';
  return String(value).normalize('NFC');
};

export const cleanText = (value: unknown): string =>
  asText(value).trim().replace(/\s+/g, ' ');

export const lookupKey = (value: unknown): string => cleanText(value).toLocaleLowerCase('en');

export const sourceKey = (namespace: string, value: unknown): string =>
  crypto.createHash('sha256').update(`${namespace}\u0000${asText(value)}`, 'utf8').digest('hex');

export const slugify = (value: unknown): string =>
  cleanText(value)
    .normalize('NFKD')
    .replace(/\p{Mark}+/gu, '')
    .toLocaleLowerCase('en')
    .replace(/[^\p{Letter}\p{Number}]+/gu, '-')
    .replace(/^-+|-+$/g, '')
    .replace(/-{2,}/g, '-');

const withSuffix = (base: string, suffix: string, maxLength: number): string => {
  const normalizedSuffix = slugify(suffix) || crypto.createHash('sha1').update(suffix).digest('hex').slice(0, 12);
  const suffixWithSeparator = `-${normalizedSuffix}`;
  const available = Math.max(1, maxLength - suffixWithSeparator.length);
  return `${base.slice(0, available).replace(/-+$/g, '')}${suffixWithSeparator}`;
};

export const operationalSlug = (
  sourceSlugValue: unknown,
  title: unknown,
  sourceProductId: string,
  baseSlugCount: number
): string => {
  const base = slugify(sourceSlugValue) || slugify(title) || 'book';
  if (baseSlugCount === 1 && base.length <= 191) return base;
  return withSuffix(base, sourceProductId, 191);
};

export const operationalSku = (
  sourceSkuValue: unknown,
  sourceProductId: string,
  sourceSkuCount: number
): string => {
  const raw = asText(sourceSkuValue).trim();
  if (raw && sourceSkuCount === 1 && raw.length <= 191) return raw;
  const base = raw || 'SKU';
  const suffix = `::${sourceProductId}`;
  const available = Math.max(1, 191 - suffix.length);
  return `${base.slice(0, available)}${suffix}`;
};

export const parseMoney = (
  value: unknown,
  fallback = 0
): { value: number; warning?: string } => {
  const raw = cleanText(value).replace(/[,₹$£€]/g, '');
  const numberValue = Number(raw);
  if (!raw || !Number.isFinite(numberValue) || numberValue < 0) {
    return { value: fallback, warning: `Invalid money value "${asText(value)}"; used ${fallback}.` };
  }
  return { value: Math.round(numberValue * 100) / 100 };
};

export const parseNonNegativeInteger = (
  value: unknown,
  fallback = 0
): { value: number; warning?: string } => {
  const raw = cleanText(value);
  const numberValue = Number(raw);
  if (!raw || !Number.isInteger(numberValue) || numberValue < 0) {
    return { value: fallback, warning: `Invalid non-negative integer "${asText(value)}"; used ${fallback}.` };
  }
  return { value: numberValue };
};

export const parseOptionalInteger = (value: unknown): number | null => {
  const raw = cleanText(value);
  if (!/^\d+$/.test(raw)) return null;
  const parsed = Number(raw);
  return Number.isSafeInteger(parsed) ? parsed : null;
};

export const parsePercent = (value: unknown): { value: number; warning?: string } => {
  const raw = cleanText(value).replace(/%$/, '');
  const parsed = Number(raw);
  if (!raw || !Number.isFinite(parsed) || parsed < 0 || parsed > 100) {
    return { value: 0, warning: `Invalid percentage "${asText(value)}"; used 0%.` };
  }
  return { value: parsed };
};

export const parseEnabled = (value: unknown): boolean => lookupKey(value) !== 'disabled';

export const parseAffirmative = (value: unknown): boolean =>
  ['yes', 'true', '1', 'enabled', 'in stock'].includes(lookupKey(value));

export const normalizeFormat = (binding: unknown): string => {
  const value = lookupKey(binding);
  if (/hard\s*(cover|back|bound|bond)/.test(value)) return 'HARDCOVER';
  if (/paper\s*back|soft\s*cover/.test(value)) return 'PAPERBACK';
  if (/ebook|e-book/.test(value)) return 'EBOOK';
  if (/audio/.test(value)) return 'AUDIOBOOK';
  return value ? 'OTHER' : 'PAPERBACK';
};

export const imageUrlsFromCell = (value: unknown): string[] => {
  const matches = asText(value).match(/https?:\/\/[^\s|]+/gu) ?? [];
  return [...new Set(matches.map((url) => url.replace(/[;,]+$/g, '')))];
};
