import { PlatformError } from './errors.mjs';

function fail(message, field) {
  throw new PlatformError(message, { code: 'INPUT_VALIDATION_FAILED', status: 400, expose: true, details: { field } });
}

export function objectSchema(fields, { allowUnknown = false } = {}) {
  const contract = Object.freeze({ ...fields });
  return input => {
    const source = input && typeof input === 'object' && !Array.isArray(input) ? input : {};
    const output = {};
    if (!allowUnknown) for (const key of Object.keys(source)) if (!contract[key]) fail(`Unbekanntes Eingabefeld: ${key}`, key);
    for (const [name, rule] of Object.entries(contract)) {
      let value = source[name];
      if (value === undefined || value === null) {
        if (rule.required) fail(`${name} ist erforderlich.`, name);
        continue;
      }
      if (rule.type === 'string') {
        value = String(value);
        if (rule.trim !== false) value = value.trim();
        if (rule.minLength && value.length < rule.minLength) fail(`${name} ist zu kurz.`, name);
        if (rule.maxLength && value.length > rule.maxLength) fail(`${name} ist zu lang.`, name);
        if (rule.pattern && !rule.pattern.test(value)) fail(`${name} besitzt ein ungültiges Format.`, name);
      } else if (rule.type === 'boolean') {
        if (typeof value !== 'boolean') fail(`${name} muss true oder false sein.`, name);
      } else if (rule.type === 'number' || rule.type === 'integer') {
        value = Number(value);
        if (!Number.isFinite(value)) fail(`${name} muss eine Zahl sein.`, name);
        if (rule.type === 'integer' && !Number.isInteger(value)) fail(`${name} muss eine ganze Zahl sein.`, name);
        if (rule.min !== undefined && value < rule.min) fail(`${name} ist zu klein.`, name);
        if (rule.max !== undefined && value > rule.max) fail(`${name} ist zu groß.`, name);
      }
      if (rule.enum && !rule.enum.includes(value)) fail(`${name} enthält einen nicht erlaubten Wert.`, name);
      if (typeof rule.validate === 'function') {
        const result = rule.validate(value);
        if (result !== true) fail(typeof result === 'string' ? result : `${name} ist ungültig.`, name);
      }
      output[name] = value;
    }
    return Object.freeze(output);
  };
}
