import { PlatformError } from '../../kernel/errors.mjs';
import { sanitizeRichHtml } from '../../../lib/html-templates.mjs';

const ALLOWED_FIELDS = new Set([
  'platform','ai_provider','title','description','description_html','tags','photos','seller_notes',
  'brand','category','size','color','condition','price','status','language','sku','storage_location',
]);
const ARRAY_LIMITS = { tags: 50, photos: 100 };

function fail(message, field, status = 400, code = 'LISTING_INPUT_INVALID') {
  throw new PlatformError(message, { code, status, expose: true, details: { field } });
}
function htmlToPlainText(html) {
  return String(html || '').replace(/<\s*br\s*\/?>/gi, '\n').replace(/<\/(p|div|h[1-6]|li|blockquote|pre)>/gi, '\n')
    .replace(/<[^>]+>/g, '').replace(/&nbsp;/gi, ' ').replace(/&amp;/gi, '&').replace(/&lt;/gi, '<')
    .replace(/&gt;/gi, '>').replace(/&quot;/gi, '"').replace(/&#39;/gi, "'").replace(/\n{3,}/g, '\n\n').trim();
}
function cleanText(value, max, field, { required = false } = {}) {
  const text = String(value ?? '').trim();
  if (required && !text) fail(`${field} ist erforderlich.`, field);
  if (text.length > max) fail(`${field} ist zu lang.`, field);
  if (/\u0000/.test(text)) fail(`${field} enthält ungültige Steuerzeichen.`, field);
  return text;
}
function normalizePayload(input = {}, { partial = false } = {}) {
  const source = input && typeof input === 'object' && !Array.isArray(input) ? input : {};
  for (const key of Object.keys(source)) if (!ALLOWED_FIELDS.has(key)) fail(`Unbekanntes Artikelfeld: ${key}`, key);
  const out = {};
  const textRules = {
    platform:64, ai_provider:64, title:240, description:12000, seller_notes:6000, brand:160,
    category:240, size:120, color:120, condition:120, status:64, language:16, sku:80, storage_location:160,
  };
  for (const [field,max] of Object.entries(textRules)) if (source[field] !== undefined) out[field] = cleanText(source[field], max, field, { required: field === 'title' && !partial });
  if (!partial && source.title === undefined) fail('title ist erforderlich.', 'title');
  if (source.description_html !== undefined) {
    const html = String(source.description_html || '');
    if (html.length > 50000) fail('description_html ist zu lang.', 'description_html');
    out.description_html = sanitizeRichHtml(html);
    if (source.description === undefined) out.description = htmlToPlainText(out.description_html).slice(0, 12000);
  }
  for (const field of ['tags','photos']) if (source[field] !== undefined) {
    if (!Array.isArray(source[field])) fail(`${field} muss eine Liste sein.`, field);
    out[field] = source[field].map(v => cleanText(v, field === 'photos' ? 500 : 120, field)).filter(Boolean).slice(0, ARRAY_LIMITS[field]);
  }
  if (source.price !== undefined && source.price !== null && source.price !== '') {
    const price = Number(source.price);
    if (!Number.isFinite(price) || price < 0 || price > 100000000) fail('price ist ungültig.', 'price');
    out.price = price;
  } else if (source.price !== undefined) out.price = null;
  return Object.freeze(out);
}

export class ListingsService {
  constructor(repository) { this.repository = repository; }
  validateCreate(input) { return normalizePayload(input, { partial: false }); }
  validateUpdate(input) { return normalizePayload(input, { partial: true }); }
  create(input, actorId) { return this.repository.create({ ...this.validateCreate(input), created_by: actorId || null, updated_by: actorId || null }); }
  list(filters = {}) { return this.repository.list(cleanText(filters.q || '', 240, 'q'), cleanText(filters.platform || '', 64, 'platform'), cleanText(filters.status || '', 64, 'status')); }
  get(id) { const item = this.repository.get(Number(id)); if (!item) throw new PlatformError('Artikel nicht gefunden.', { code:'LISTING_NOT_FOUND', status:404, expose:true }); return item; }
  update(id, input, actorId) { this.get(id); return this.repository.update(Number(id), { ...this.validateUpdate(input), updated_by: actorId || null }); }
  softDelete(id) { this.get(id); this.repository.softDelete(Number(id)); return { ok:true }; }
  duplicate(id) { const copy = this.repository.duplicate(Number(id)); if (!copy) throw new PlatformError('Artikel nicht gefunden.', { code:'LISTING_NOT_FOUND', status:404, expose:true }); return copy; }
  trash() { return this.repository.listDeleted(); }
  trashCount() { return { count: this.repository.trashCount() }; }
  restore(id) { const item = this.repository.listDeleted().find(entry => Number(entry.id) === Number(id)); if (!item) throw new PlatformError('Artikel befindet sich nicht im Papierkorb.', { code:'LISTING_TRASH_NOT_FOUND', status:404, expose:true }); this.repository.restore(Number(id)); return { ok:true }; }
  permanentDelete(id) { const item = this.repository.listDeleted().find(entry => Number(entry.id) === Number(id)); if (!item) throw new PlatformError('Artikel befindet sich nicht im Papierkorb.', { code:'LISTING_TRASH_NOT_FOUND', status:404, expose:true }); this.repository.permanentDelete(Number(id)); return { ok:true }; }
  emptyTrash() { const items = this.repository.listDeleted(); for (const item of items) this.repository.permanentDelete(item.id); return { ok:true, deleted:items.length }; }
}
