import React from 'react';
import { createRoot } from 'react-dom/client';
import NFCBarcodePreview from './NFCBarcodePreview';
import I18nProvider from 'B/I18nProvider';
import { setupI18n } from 'B/i18nSetup';

import { msg } from '@lingui/macro';
import type { MessageDescriptor } from '@lingui/core';

type Options = {
  url?: string;
  internalUrl?: string;
  externalUrl?: string;
  text?: MessageDescriptor;
  fileName?: string;
  fileNameInternal?: string;
  fileNameExternal?: string;
  internalTabTitle?: MessageDescriptor;
  externalTabTitle?: MessageDescriptor;
  internalHeading?: MessageDescriptor;
  externalHeading?: MessageDescriptor;
  internalText?: MessageDescriptor;
  externalText?: MessageDescriptor;
  size?: number;
};

let isNFCPreviewI18nInitialized = false;

function sanitizeFileName(fileName?: string): string {
  const normalized = (fileName ?? '')
    .trim()
    .replace(/[\\/:*?"<>|]/g, '-')
    .replace(/\s+/g, '_')
    .replace(/_+/g, '_')
    .replace(/-+/g, '-')
    .replace(/^[-_.]+|[-_.]+$/g, '');

  return normalized || 'qr-code';
}

function ensureSvgFileName(fileName?: string): string {
  const safeName = sanitizeFileName(fileName);
  return safeName.endsWith('.svg') ? safeName : `${safeName}.svg`;
}

function wrapLines(text: string, maxCharsPerLine: number): string[] {
  const words = text.trim().split(/\s+/).filter(Boolean);
  const lines: string[] = [];

  for (const word of words) {
    if (word.length > maxCharsPerLine) {
      const chunks = word.match(new RegExp(`.{1,${maxCharsPerLine}}`, 'g')) ?? [word];
      chunks.forEach((chunk) => lines.push(chunk));
      continue;
    }

    const last = lines[lines.length - 1];
    if (!last) {
      lines.push(word);
    } else if (`${last} ${word}`.length <= maxCharsPerLine) {
      lines[lines.length - 1] = `${last} ${word}`;
    } else {
      lines.push(word);
    }
  }

  return lines;
}

const SVG_NS = 'http://www.w3.org/2000/svg';

type ExportMetrics = {
  sourceSize: number;
  lineHeight: number;
  labelPadding: number;
  extraHeight: number;
};

function getSourceSize(svg: SVGSVGElement): number {
  return (
    Number.parseFloat(svg.getAttribute('width') ?? '') ||
    Number.parseFloat(svg.getAttribute('viewBox')?.split(/\s+/)[2] ?? '') ||
    220
  );
}

function getLabelLines(label: string, sourceSize: number): string[] {
  const cleanLabel = label.replace(/\.svg$/i, '').trim();
  const maxCharsPerLine = Math.max(10, Math.floor(sourceSize / 9));
  return cleanLabel ? wrapLines(cleanLabel, maxCharsPerLine) : [];
}

function getExportMetrics(sourceSize: number, lines: string[]): ExportMetrics {
  const lineHeight = 18;
  const labelPadding = 10;
  const extraHeight = lines.length > 0 ? labelPadding * 2 + lines.length * lineHeight : 0;
  return { sourceSize, lineHeight, labelPadding, extraHeight };
}

function createExportSvgRoot(metrics: ExportMetrics): SVGSVGElement {
  const exportSvg = document.createElementNS(SVG_NS, 'svg');
  exportSvg.setAttribute('xmlns', SVG_NS);
  exportSvg.setAttribute('width', `${metrics.sourceSize}`);
  exportSvg.setAttribute('height', `${metrics.sourceSize + metrics.extraHeight}`);
  exportSvg.setAttribute('viewBox', `0 0 ${metrics.sourceSize} ${metrics.sourceSize + metrics.extraHeight}`);
  return exportSvg;
}

function appendQrGroup(exportSvg: SVGSVGElement, sourceSvg: SVGSVGElement, sourceSize: number): void {
  const qrGroup = document.createElementNS(SVG_NS, 'g');
  const viewBox = sourceSvg.getAttribute('viewBox')?.split(/\s+/).map(Number) ?? [];
  const viewBoxSize = Number.isFinite(viewBox[2]) ? viewBox[2] : sourceSize;
  qrGroup.setAttribute('transform', `scale(${sourceSize / viewBoxSize})`);
  Array.from(sourceSvg.childNodes).forEach((node) => qrGroup.appendChild(node.cloneNode(true)));
  exportSvg.appendChild(qrGroup);
}

function appendLabelArea(exportSvg: SVGSVGElement, lines: string[], metrics: ExportMetrics): void {
  if (lines.length === 0) return;

  const textBackground = document.createElementNS(SVG_NS, 'rect');
  textBackground.setAttribute('x', '0');
  textBackground.setAttribute('y', `${metrics.sourceSize}`);
  textBackground.setAttribute('width', `${metrics.sourceSize}`);
  textBackground.setAttribute('height', `${metrics.extraHeight}`);
  textBackground.setAttribute('fill', '#fff');
  exportSvg.appendChild(textBackground);

  const textNode = document.createElementNS(SVG_NS, 'text');
  textNode.setAttribute('x', `${metrics.sourceSize / 2}`);
  textNode.setAttribute('y', `${metrics.sourceSize + metrics.labelPadding + 14}`);
  textNode.setAttribute('text-anchor', 'middle');
  textNode.setAttribute('font-size', '12');
  textNode.setAttribute('font-family', 'Arial, sans-serif');
  textNode.setAttribute('fill', '#000');

  lines.forEach((line, index) => {
    const tspan = document.createElementNS(SVG_NS, 'tspan');
    tspan.setAttribute('x', `${metrics.sourceSize / 2}`);
    if (index > 0) tspan.setAttribute('dy', `${metrics.lineHeight}`);
    tspan.textContent = line;
    textNode.appendChild(tspan);
  });

  exportSvg.appendChild(textNode);
}

function serializeAndDownloadSvg(svg: SVGSVGElement, fileName: string): void {
  const serializer = new XMLSerializer();
  const svgText = serializer.serializeToString(svg);
  const blob = new Blob([svgText], { type: 'image/svg+xml;charset=utf-8' });
  const blobUrl = URL.createObjectURL(blob);

  const link = document.createElement('a');
  link.href = blobUrl;
  link.download = fileName;
  document.body.appendChild(link);
  link.click();
  document.body.removeChild(link);
  URL.revokeObjectURL(blobUrl);
}

function downloadSvg(sourceSvg: SVGSVGElement, fileName: string, label: string): void {
  const sourceSize = getSourceSize(sourceSvg);
  const lines = getLabelLines(label, sourceSize);
  const metrics = getExportMetrics(sourceSize, lines);
  const exportSvg = createExportSvgRoot(metrics);
  appendQrGroup(exportSvg, sourceSvg, sourceSize);
  appendLabelArea(exportSvg, lines, metrics);
  serializeAndDownloadSvg(exportSvg, fileName);
}

export function openNFCBarcodePreview(
  options: Options = {}
): Promise<'close'> {
  if (!isNFCPreviewI18nInitialized) {
    setupI18n();
    isNFCPreviewI18nInitialized = true;
  }

  return new Promise((resolve) => {
    const container = document.createElement('div');
    document.body.appendChild(container);

    const root = createRoot(container);

    const cleanup = () => {
      root.unmount();
      container.remove();
    };

    const handleClose = () => {
      cleanup();
      resolve('close');
    };

    const internalUrl = options.internalUrl ?? '';
    const externalUrl = options.externalUrl ?? options.url ?? '';

    const handleDownloadInternal = () => {
      const svg = document.getElementById('nfc-qr-internal') as SVGSVGElement | null;
      if (!svg) return;
      const label = options.fileNameInternal ?? `${options.fileName ?? 'qr-code'}-intern`;
      const fileName = ensureSvgFileName(label);
      downloadSvg(svg, fileName, label);
    };

    const handleDownloadExternal = () => {
      const svg = document.getElementById('nfc-qr-external') as SVGSVGElement | null;
      if (!svg) return;
      const label = options.fileNameExternal ?? `${options.fileName ?? 'qr-code'}-extern`;
      const fileName = ensureSvgFileName(label);
      downloadSvg(svg, fileName, label);
    };

    root.render(
      <I18nProvider>
        <NFCBarcodePreview
          open={true}
          text={options.text}
          internalUrl={internalUrl}
          externalUrl={externalUrl}
          internalTabTitle={options.internalTabTitle ?? msg`QR-Code für TASKO-App`}
          externalTabTitle={options.externalTabTitle ?? msg`QR-Code für externe Nutzung`}
          internalHeading={options.internalHeading ?? msg`Hinweis zur QR-Code-Nutzung`}
          externalHeading={options.externalHeading ?? msg`Hinweis zur QR-Code-Nutzung`}
          internalText={options.internalText}
          externalText={options.externalText}
          qrSize={options.size ?? 220}
          onClose={handleClose}
          onDownloadInternal={handleDownloadInternal}
          onDownloadExternal={handleDownloadExternal}
          downloadInternalDisabled={!internalUrl}
          downloadExternalDisabled={!externalUrl}
        />
      </I18nProvider>
    );
  });
}
