import prisma from '../lib/prisma.js';
import { stripHtml, isValidEmail, sanitizePhoneField, validateProviderAddress } from '../utils/sanitize.js';
import { validateHttpUrl } from '../utils/url.js';

// Pentest 46.1 (HIGH, 2026-06-01): Stored XSS via provider.portalUrl.
// PUT akzeptierte `javascript:alert(...)` als URL, das Portal rendert
// sie als <a href={portalUrl}> → ein Klick im Kunden-Browser löst die
// XSS aus. Fix: vor Schreiben durch validateHttpUrl, das auch andere
// gefährliche Schemata (data:, vbscript:, file:) sperrt und private
// IPs verbietet (die URL wird Kunden gezeigt, denen interne Hosts
// nichts bringen).
function assertValidPortalUrl(portalUrl: string | undefined | null): string | undefined {
  if (portalUrl == null || portalUrl === '') return undefined;
  const check = validateHttpUrl(portalUrl, { fieldLabel: 'Portal-URL' });
  if (!check.ok) throw new Error(check.error);
  return check.value === '' ? undefined : check.value;
}

export async function getAllProviders(includeInactive = false) {
  const where = includeInactive ? {} : { isActive: true };
  return prisma.provider.findMany({
    where,
    orderBy: { name: 'asc' },
    include: {
      tariffs: {
        where: includeInactive ? {} : { isActive: true },
        orderBy: { name: 'asc' },
      },
      _count: {
        select: { contracts: true, tariffs: true },
      },
    },
  });
}

export async function getProviderById(id: number) {
  return prisma.provider.findUnique({
    where: { id },
    include: {
      tariffs: {
        orderBy: { name: 'asc' },
      },
      _count: {
        select: { contracts: true },
      },
    },
  });
}

// Pentest 47.2 (INFO, 2026-06-01): provider.name landete roh in der DB.
// Aktuell escapt React das Textnode, also kein direkter XSS – aber neue
// Renderpfade (PDF, Mail-Templates, Embedded-Strings in URLs) wären
// sofort betroffen. Defense-in-depth: schon beim Schreiben strippen.
//
// 2026-06-21: contactEmail/cancellationEmail laufen zusätzlich durch
// isValidEmail (Header-Injection-Schutz für künftige Mail-Templates),
// contactPhone/contactFax/cancellationFax durch sanitizePhoneField
// (kein CRLF/Control-Char), Postadressen durch sanitizeNotes mit
// 500-Cap (mehrzeilig, normalisierte Newlines).
function stripProviderStrings<T extends object>(data: T): T {
  const out: any = { ...data };
  for (const k of ['name', 'usernameFieldName', 'passwordFieldName'] as const) {
    if (typeof out[k] === 'string') out[k] = stripHtml(out[k]);
  }
  for (const k of ['contactEmail', 'cancellationEmail'] as const) {
    if (out[k] === '' || out[k] === null) { out[k] = null; continue; }
    if (out[k] === undefined) continue;
    const stripped = typeof out[k] === 'string' ? stripHtml(out[k]) : out[k];
    const value = typeof stripped === 'string' ? stripped.trim() : stripped;
    if (value === '' ) { out[k] = null; continue; }
    if (!isValidEmail(value)) {
      throw new Error(`${k === 'contactEmail' ? 'Kontakt-Emailadresse' : 'Kündigungs-Emailadresse'} ist ungültig.`);
    }
    out[k] = value;
  }
  const phoneLabels: Record<string, string> = {
    contactPhone: 'Kontakt-Telefonnummer',
    contactFax: 'Kontakt-Faxnummer',
    cancellationFax: 'Kündigungs-Faxnummer',
  };
  for (const k of ['contactPhone', 'contactFax', 'cancellationFax'] as const) {
    if (out[k] === undefined) continue;
    if (out[k] === '' || out[k] === null) { out[k] = null; continue; }
    const v = sanitizePhoneField(out[k], phoneLabels[k]);
    out[k] = v === undefined ? null : v;
  }
  const addressLabels: Record<string, string> = {
    contactAddress: 'Kontakt-Postadresse',
    cancellationAddress: 'Kündigungs-Postadresse',
  };
  for (const k of ['contactAddress', 'cancellationAddress'] as const) {
    if (out[k] === undefined) continue;
    // R89.1/R89.2: validateProviderAddress wirft 400 bei Längen-
    // Verstoß, HTML, Tabs oder Steuerzeichen. Kein silent truncate,
    // kein silent null-overwrite mehr.
    out[k] = validateProviderAddress(out[k], addressLabels[k]);
  }
  return out;
}

interface ProviderWritable {
  name?: string;
  portalUrl?: string;
  usernameFieldName?: string;
  passwordFieldName?: string;
  contactEmail?: string | null;
  contactPhone?: string | null;
  contactFax?: string | null;
  contactAddress?: string | null;
  cancellationEmail?: string | null;
  cancellationFax?: string | null;
  cancellationAddress?: string | null;
  isActive?: boolean;
}

export async function createProvider(data: ProviderWritable & { name: string }) {
  const clean = stripProviderStrings(data);
  const portalUrl = assertValidPortalUrl(clean.portalUrl);
  return prisma.provider.create({
    data: {
      ...clean,
      name: clean.name,
      portalUrl,
      isActive: true,
    },
  });
}

export async function updateProvider(id: number, data: ProviderWritable) {
  // portalUrl nur validieren wenn explizit mitgesendet (undefined = unverändert).
  // Leerstring = "auf null setzen" - hier setzen wir explizit auf null,
  // damit Prisma nicht den alten Wert hält.
  const updateData: any = stripProviderStrings(data);
  if (data.portalUrl !== undefined) {
    const validated = assertValidPortalUrl(data.portalUrl);
    updateData.portalUrl = validated ?? null;
  }
  return prisma.provider.update({
    where: { id },
    data: updateData,
  });
}

export async function deleteProvider(id: number) {
  // Check if provider is used by any contracts
  const count = await prisma.contract.count({
    where: { providerId: id },
  });

  if (count > 0) {
    throw new Error(
      `Anbieter kann nicht gelöscht werden, da er von ${count} Verträgen verwendet wird`
    );
  }

  return prisma.provider.delete({ where: { id } });
}
