import { layout } from './stores/layout.svelte.js';
import { isEpcType, isInductionType, isSpurType, isCurvedType, isRectConveyanceType, isExtendoType, isPhotoeyeType, getSymbolGroup, EPC_CONFIG, INDUCTION_CONFIG, PHOTOEYE_CONFIG, getCurveGeometry } from './symbols.js';
import { serializeSymbol, deserializeSymbol } from './serialization.js';
import type { PlacedSymbol } from './types.js';

/** Parse conveyance label into display lines — same logic as renderer */
function parseConveyanceLabel(label: string): { lines: string[]; stripped: string[] } {
  let core = label.replace(/_?VFD\d*$/i, '');
  if (core === label) core = label;
  const m = core.match(/^([A-Za-z]+)(.*)$/);
  if (m) {
    const prefix = m[1];
    const nums = m[2].replace(/_/g, '-').replace(/^-/, '');
    if (nums) return { lines: [prefix, nums], stripped: [nums] };
    return { lines: [prefix], stripped: [prefix] };
  }
  return { lines: [core], stripped: [core] };
}

/** Emit conveyance label text inside a <g> — inherits outer transform from group */
function emitConveyanceLabelInner(lines: string[], sym: PlacedSymbol) {
  if (!sym.label) return;
  const { lines: textLines } = parseConveyanceLabel(sym.label);

  let labelCx: number, labelCy: number, availH: number;
  let textRotDeg = 0; // additional rotation for the text

  if (isCurvedType(sym.symbolId)) {
    const angle = sym.curveAngle || 90;
    const { arcCx, arcCy, outerR, innerR, bandW } = getCurveGeometry(sym.symbolId, sym.x, sym.y, sym.w, sym.h);
    const midR = (outerR + innerR) / 2;
    const midAngleRad = ((angle / 2) * Math.PI) / 180;
    labelCx = arcCx + midR * Math.cos(midAngleRad);
    labelCy = arcCy - midR * Math.sin(midAngleRad);
    availH = bandW - 4;
    // Tangent rotation (same as canvas renderer)
    let textRotRad = -midAngleRad + Math.PI / 2;
    // Check readability with outer rotation
    const symRotRad = ((sym.rotation || 0) * Math.PI) / 180;
    let worldAngle = (textRotRad + symRotRad) % (2 * Math.PI);
    if (worldAngle < 0) worldAngle += 2 * Math.PI;
    if (worldAngle > Math.PI / 2 && worldAngle < Math.PI * 3 / 2) textRotRad += Math.PI;
    // Mirror: label follows the mirrored shape naturally
    textRotDeg = (textRotRad * 180) / Math.PI;
  } else if (isSpurType(sym.symbolId)) {
    const w2 = sym.w2 ?? sym.w;
    labelCx = sym.x + (w2 + sym.w) / 4;
    labelCy = sym.y + sym.h / 2;
    availH = sym.h - 4;
  } else if (isInductionType(sym.symbolId)) {
    const stripTopY = sym.y + sym.h * INDUCTION_CONFIG.stripTopFrac;
    const stripBottomY = sym.y + sym.h * INDUCTION_CONFIG.stripBottomFrac;
    labelCx = sym.x + (INDUCTION_CONFIG.headWidth + sym.w) / 2;
    labelCy = (stripTopY + stripBottomY) / 2;
    availH = stripBottomY - stripTopY - 4;
  } else {
    labelCx = sym.x + sym.w / 2;
    labelCy = sym.y + sym.h / 2;
    availH = sym.h - 4;
  }

  // For non-curved: check readability and flip if needed
  let needsMirrorFix = false;
  if (!isCurvedType(sym.symbolId)) {
    const rot = ((sym.rotation || 0) % 360 + 360) % 360;
    if (rot > 90 && rot < 270) textRotDeg = 180;
    needsMirrorFix = !!sym.mirrored;
  }

  const fontSize = Math.min(14, availH / textLines.length);
  if (fontSize < 4) return;
  const lineH = fontSize;
  // Build transform: counter-mirror then rotate for readability
  let transformParts: string[] = [];
  if (needsMirrorFix) transformParts.push(`translate(${labelCx},${labelCy}) scale(-1,1) translate(${-labelCx},${-labelCy})`);
  if (textRotDeg) transformParts.push(`rotate(${textRotDeg.toFixed(1)},${labelCx},${labelCy})`);
  const rotAttr = transformParts.length ? ` transform="${transformParts.join(' ')}"` : '';
  for (let i = 0; i < textLines.length; i++) {
    const dy = -(textLines.length - 1) * lineH / 2 + i * lineH;
    const y = labelCy + dy + fontSize * 0.35;
    lines.push(`    <text x="${labelCx}" y="${y}" text-anchor="middle" style="font-family:Arial;font-weight:bold;font-size:${fontSize}px;fill:#000000"${rotAttr}>${textLines[i]}</text>`);
  }
}

function downloadBlob(blob: Blob, filename: string) {
  const url = URL.createObjectURL(blob);
  const a = document.createElement('a');
  a.href = url;
  a.download = filename;
  a.click();
  URL.revokeObjectURL(url);
}

export function exportJSON() {
  const data = {
    symbols: layout.symbols.map(s => serializeSymbol(s)),
    gridSize: layout.gridSize,
    minSpacing: layout.minSpacing,
    canvasW: layout.canvasW,
    canvasH: layout.canvasH,
  };
  const mcmName = layout.currentMcm || 'export';
  downloadBlob(new Blob([JSON.stringify(data, null, 2)], { type: 'application/json' }), `${mcmName}_layout.json`);
}

/** Serialize child elements of an SVG, stripping xmlns added by XMLSerializer */
function serializeChildren(parent: Element): string {
  return Array.from(parent.children)
    .map(el => new XMLSerializer().serializeToString(el)
      .replace(/ xmlns="http:\/\/www\.w3\.org\/2000\/svg"/g, ''))
    .join('\n    ');
}

export async function exportSVG() {
  const lines: string[] = [
    '<?xml version="1.0" encoding="UTF-8" standalone="no"?>',
    `<svg width="${layout.canvasW}" height="${layout.canvasH}" viewBox="0 0 ${layout.canvasW} ${layout.canvasH}" version="1.1"`,
    '  xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"',
    '  xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape">',
    `  <rect width="${layout.canvasW}" height="${layout.canvasH}" fill="#ffffff" />`,
  ];

  // Overlay types render on top of base conveyance
  const OVERLAY_IDS = new Set([
    'photoeye', 'photoeye_v',
    'fio_sio_fioh', 'fio_sio_fioh_v',
    'dpm', 'dpm_v',
    'pdp', 'pdp_v',
    'mcm', 'mcm_v',
  ]);

  const visible = layout.symbols.filter(s => !s.hidden && !layout.hiddenGroups.has(getSymbolGroup(s.symbolId)));
  const baseSymbols = visible.filter(s => !OVERLAY_IDS.has(s.symbolId));
  const overlaySymbols = visible.filter(s => OVERLAY_IDS.has(s.symbolId));

  for (const sym of [...baseSymbols, ...overlaySymbols]) {
    const rot = sym.rotation || 0;
    const mirrored = sym.mirrored || false;
    const cx = sym.x + sym.w / 2;
    const cy = sym.y + sym.h / 2;
    const label = sym.label || sym.name;
    const idAttr = `id="${label}" inkscape:label="${label}"`;

    // Build outer transform (rotation + mirror)
    const outerParts: string[] = [];
    if (rot) outerParts.push(`rotate(${rot},${cx},${cy})`);
    if (mirrored) outerParts.push(`translate(${cx},0) scale(-1,1) translate(${-cx},0)`);
    const outerTransform = outerParts.join(' ');

    if (isEpcType(sym.symbolId)) {
      await emitEpc(lines, sym as PlacedSymbol, label, outerTransform);
    } else if (isInductionType(sym.symbolId)) {
      const hw = INDUCTION_CONFIG.headWidth;
      const stripTopY = sym.y + sym.h * INDUCTION_CONFIG.stripTopFrac;
      const stripBottomY = sym.y + sym.h * INDUCTION_CONFIG.stripBottomFrac;
      const pts = INDUCTION_CONFIG.arrowPoints.map(([xf, yf]) => [sym.x + xf * hw, sym.y + yf * sym.h] as const);
      const d = `M ${sym.x + sym.w},${stripTopY} L ${pts[0][0]},${stripTopY} ${pts.map(([px, py]) => `L ${px},${py}`).join(' ')} L ${pts[5][0]},${stripBottomY} L ${sym.x + sym.w},${stripBottomY} Z`;
      lines.push(`  <g ${idAttr}${outerTransform ? ` transform="${outerTransform}"` : ''}>`);
      lines.push(`    <path d="${d}" fill="#ffffff" stroke="#000000" stroke-width="1" />`);
      emitConveyanceLabelInner(lines, sym as PlacedSymbol);
      lines.push(`  </g>`);
    } else if (isCurvedType(sym.symbolId)) {
      const angle = sym.curveAngle || 90;
      const { arcCx, arcCy, outerR, innerR } = getCurveGeometry(sym.symbolId, sym.x, sym.y, sym.w, sym.h);
      const sweepRad = (angle * Math.PI) / 180;
      const outerEndX = arcCx + outerR * Math.cos(sweepRad);
      const outerEndY = arcCy - outerR * Math.sin(sweepRad);
      const innerEndX = arcCx + innerR * Math.cos(sweepRad);
      const innerEndY = arcCy - innerR * Math.sin(sweepRad);
      const largeArc = angle > 180 ? 1 : 0;
      const d = [
        `M ${arcCx + outerR},${arcCy}`,
        `A ${outerR},${outerR} 0 ${largeArc},0 ${outerEndX},${outerEndY}`,
        `L ${innerEndX},${innerEndY}`,
        `A ${innerR},${innerR} 0 ${largeArc},1 ${arcCx + innerR},${arcCy}`,
        'Z',
      ].join(' ');
      lines.push(`  <g ${idAttr}${outerTransform ? ` transform="${outerTransform}"` : ''}>`);
      lines.push(`    <path d="${d}" fill="#ffffff" stroke="#000000" stroke-width="1" />`);
      emitConveyanceLabelInner(lines, sym as PlacedSymbol);
      lines.push(`  </g>`);
    } else if (isSpurType(sym.symbolId)) {
      const w2 = sym.w2 ?? sym.w;
      const d = `M ${sym.x},${sym.y} L ${sym.x + w2},${sym.y} L ${sym.x + sym.w},${sym.y + sym.h} L ${sym.x},${sym.y + sym.h} Z`;
      lines.push(`  <g ${idAttr}${outerTransform ? ` transform="${outerTransform}"` : ''}>`);
      lines.push(`    <path d="${d}" fill="#ffffff" stroke="#000000" stroke-width="1" />`);
      emitConveyanceLabelInner(lines, sym as PlacedSymbol);
      lines.push(`  </g>`);
    } else if (isRectConveyanceType(sym.symbolId)) {
      lines.push(`  <g ${idAttr}${outerTransform ? ` transform="${outerTransform}"` : ''}>`);
      lines.push(`    <rect x="${sym.x}" y="${sym.y}" width="${sym.w}" height="${sym.h}" fill="#ffffff" stroke="#000000" stroke-width="1" />`);
      emitConveyanceLabelInner(lines, sym as PlacedSymbol);
      lines.push(`  </g>`);
    } else if (isExtendoType(sym.symbolId)) {
      const bracketW = 10.6 / 31.07 * 73;
      const x = sym.x, y = sym.y, w = sym.w, h = sym.h;
      const pts = [
        [x + bracketW * 0.44, y + h * 0.085],
        [x + bracketW, y + h * 0.085],
        [x + bracketW, y + h * 0.222],
        [x + w, y + h * 0.222],
        [x + w, y + h * 0.780],
        [x + bracketW, y + h * 0.780],
        [x + bracketW, y + h * 0.917],
        [x + bracketW * 0.44, y + h * 0.916],
        [x + bracketW * 0.34, y + h * 0.985],
        [x, y + h * 0.980],
        [x, y + h * 0.017],
        [x + bracketW * 0.34, y + h * 0.016],
      ];
      const d = `M ${pts[0][0]},${pts[0][1]} ` + pts.slice(1).map(p => `L ${p[0]},${p[1]}`).join(' ') + ' Z';
      lines.push(`  <g ${idAttr}${outerTransform ? ` transform="${outerTransform}"` : ''}>`);
      lines.push(`    <path d="${d}" fill="#ffffff" stroke="#000000" stroke-width="1" />`);
      emitConveyanceLabelInner(lines, sym as PlacedSymbol);
      lines.push(`  </g>`);
    } else if (isPhotoeyeType(sym.symbolId)) {
      const { leftCap, rightCap } = PHOTOEYE_CONFIG;
      const x = sym.x, y = sym.y, w = sym.w, h = sym.h;
      const pts = [
        [x + leftCap, y + h * 0.42],
        [x + leftCap, y + h * 0.248],
        [x, y + h * 0.05],
        [x, y + h * 0.948],
        [x + leftCap, y + h * 0.744],
        [x + leftCap, y + h * 0.585],
        [x + w - rightCap, y + h * 0.585],
        [x + w - rightCap, y + h * 0.826],
        [x + w, y + h * 0.826],
        [x + w, y + h * 0.181],
        [x + w - rightCap, y + h * 0.181],
        [x + w - rightCap, y + h * 0.42],
      ];
      const d = `M ${pts[0][0]},${pts[0][1]} ` + pts.slice(1).map(p => `L ${p[0]},${p[1]}`).join(' ') + ' Z';
      lines.push(`  <path ${idAttr} d="${d}" fill="#ffffff" stroke="#000000" stroke-width="1"${outerTransform ? ` transform="${outerTransform}"` : ''} />`);
    } else {
      // Regular SVG symbol
      try {
        const svgText = await (await fetch(sym.file)).text();
        const doc = new DOMParser().parseFromString(svgText, 'image/svg+xml');
        const svgEl = doc.documentElement;
        if (svgEl.querySelector('parsererror')) {
          console.error('SVG parse error for:', sym.file);
          continue;
        }
        const vb = svgEl.getAttribute('viewBox');
        const [vbX, vbY, vbW, vbH] = vb
          ? vb.split(/[\s,]+/).map(Number)
          : [0, 0, sym.w, sym.h];
        const sx = sym.w / vbW;
        const sy = sym.h / vbH;

        // Base positioning transform
        let baseTransform = `translate(${sym.x},${sym.y}) scale(${sx.toFixed(6)},${sy.toFixed(6)}) translate(${-vbX},${-vbY})`;
        if (mirrored) baseTransform = `translate(${cx},0) scale(-1,1) translate(${-cx},0) ${baseTransform}`;
        if (rot) baseTransform = `rotate(${rot},${cx},${cy}) ${baseTransform}`;

        const children = Array.from(svgEl.children);

        if (children.length === 1 && children[0].tagName === 'g') {
          // SVG has a <g> wrapper — keep it, put id/label on it, compose transforms
          const g = children[0];
          const gTransform = g.getAttribute('transform') || '';
          const fullTransform = gTransform ? `${baseTransform} ${gTransform}` : baseTransform;
          const innerContent = serializeChildren(g);
          lines.push(`  <g ${idAttr} transform="${fullTransform}">`);
          lines.push(`    ${innerContent}`);
          lines.push('  </g>');
        } else if (children.length === 1) {
          // Single element, no group — put id/label/transform directly on it
          const el = children[0].cloneNode(true) as Element;
          const elTransform = el.getAttribute('transform');
          el.setAttribute('transform', elTransform ? `${baseTransform} ${elTransform}` : baseTransform);
          el.setAttribute('id', label);
          let s = new XMLSerializer().serializeToString(el)
            .replace(/ xmlns="http:\/\/www\.w3\.org\/2000\/svg"/g, '');
          // Inject inkscape:label after the tag name
          const firstSpace = s.indexOf(' ');
          s = s.slice(0, firstSpace) + ` inkscape:label="${label}"` + s.slice(firstSpace);
          lines.push(`  ${s}`);
        } else {
          // Multiple children without a group — wrap in <g> with id/label
          const innerContent = serializeChildren(svgEl);
          lines.push(`  <g ${idAttr} transform="${baseTransform}">`);
          lines.push(`    ${innerContent}`);
          lines.push('  </g>');
        }
      } catch (err) {
        console.error('Failed to embed symbol:', sym.name, err);
      }
    }
  }

  // Embed layout data as metadata for re-import
  const layoutData = {
    symbols: layout.symbols.filter(s => !s.hidden && !layout.hiddenGroups.has(getSymbolGroup(s.symbolId))).map(s => serializeSymbol(s)),
    gridSize: layout.gridSize,
    minSpacing: layout.minSpacing,
    canvasW: layout.canvasW,
    canvasH: layout.canvasH,
  };
  lines.push(`  <!-- LAYOUT_DATA:${JSON.stringify(layoutData)}:END_LAYOUT_DATA -->`);

  lines.push('</svg>');
  const mcmName = layout.currentMcm || 'export';
  downloadBlob(new Blob([lines.join('\n')], { type: 'image/svg+xml' }), `${mcmName}_Detailed_View.svg`);
}

/** Emit EPC symbol — polyline + icon + right box, wrapped in <g> with id/label */
async function emitEpc(lines: string[], sym: PlacedSymbol, label: string, outerTransform: string) {
  const waypoints = sym.epcWaypoints || EPC_CONFIG.defaultWaypoints;
  const ox = sym.x;
  const oy = sym.y;
  const parts: string[] = [];
  const tAttr = outerTransform ? ` transform="${outerTransform}"` : '';

  // Polyline
  if (waypoints.length >= 2) {
    const points = waypoints.map(wp => `${ox + wp.x},${oy + wp.y}`).join(' ');
    parts.push(`    <polyline points="${points}" fill="none" stroke="#000000" stroke-width="${EPC_CONFIG.lineWidth}" />`);
  }

  if (waypoints.length >= 2) {
    // Left icon
    const lb = EPC_CONFIG.leftBox;
    const p0x = ox + waypoints[0].x, p0y = oy + waypoints[0].y;
    const p1x = ox + waypoints[1].x, p1y = oy + waypoints[1].y;
    const lAngle = Math.atan2(p1y - p0y, p1x - p0x) * 180 / Math.PI;

    try {
      const svgText = await (await fetch(EPC_CONFIG.iconFile)).text();
      const doc = new DOMParser().parseFromString(svgText, 'image/svg+xml');
      const svgEl = doc.documentElement;
      const vb = svgEl.getAttribute('viewBox');
      const [vbX, vbY, vbW, vbH] = vb ? vb.split(/[\s,]+/).map(Number) : [0, 0, lb.w, lb.h];
      const sx = lb.w / vbW;
      const sy = lb.h / vbH;
      const iconTransform = `translate(${p0x},${p0y}) rotate(${lAngle.toFixed(2)}) translate(${-lb.w},${-lb.h / 2}) scale(${sx.toFixed(6)},${sy.toFixed(6)}) translate(${-vbX},${-vbY})`;

      const children = Array.from(svgEl.children);
      if (children.length === 1 && children[0].tagName === 'g') {
        const g = children[0];
        const gT = g.getAttribute('transform') || '';
        const fullT = gT ? `${iconTransform} ${gT}` : iconTransform;
        parts.push(`    <g transform="${fullT}">`);
        parts.push(`      ${serializeChildren(g)}`);
        parts.push(`    </g>`);
      } else {
        parts.push(`    <g transform="${iconTransform}">`);
        parts.push(`      ${serializeChildren(svgEl)}`);
        parts.push(`    </g>`);
      }
    } catch {
      parts.push(`    <rect x="${-lb.w}" y="${-lb.h / 2}" width="${lb.w}" height="${lb.h}" fill="#aaaaaa" stroke="#000000" stroke-width="${EPC_CONFIG.lineWidth}" transform="translate(${p0x},${p0y}) rotate(${lAngle.toFixed(2)})" />`);
    }

    // Right box
    const last = waypoints[waypoints.length - 1];
    const prev = waypoints[waypoints.length - 2];
    const plx = ox + last.x, ply = oy + last.y;
    const ppx = ox + prev.x, ppy = oy + prev.y;
    const rAngle = Math.atan2(ply - ppy, plx - ppx) * 180 / Math.PI;
    const rb = EPC_CONFIG.rightBox;
    parts.push(`    <rect x="${-rb.w}" y="${-rb.h / 2}" width="${rb.w}" height="${rb.h}" fill="#aaaaaa" stroke="#000000" stroke-width="${EPC_CONFIG.lineWidth}" transform="translate(${plx},${ply}) rotate(${rAngle.toFixed(2)})" />`);
  }

  lines.push(`  <g id="${label}" inkscape:label="${label}"${tAttr}>`);
  lines.push(parts.join('\n'));
  lines.push('  </g>');
}

export function loadLayoutSVG(file: File): Promise<void> {
  return new Promise((resolve, reject) => {
    const reader = new FileReader();
    reader.onload = (ev) => {
      try {
        const svgText = ev.target!.result as string;
        const match = svgText.match(/<!-- LAYOUT_DATA:(.*?):END_LAYOUT_DATA -->/);
        if (!match) throw new Error('No layout data found in SVG. Only SVGs exported from this tool can be imported.');
        const data = JSON.parse(match[1]);
        layout.pushUndo();
        if (data.gridSize) layout.gridSize = data.gridSize;
        if (data.minSpacing) layout.minSpacing = data.minSpacing;
        if (data.canvasW) layout.canvasW = data.canvasW;
        if (data.canvasH) layout.canvasH = data.canvasH;
        layout.symbols = [];
        layout.nextId = 1;
        for (const s of data.symbols) {
          layout.symbols.push(deserializeSymbol(s, layout.nextId++));
        }
        layout.markDirty();
        layout.saveMcmState();
        resolve();
      } catch (err) {
        reject(err);
      }
    };
    reader.readAsText(file);
  });
}

export function loadLayoutJSON(file: File): Promise<void> {
  return new Promise((resolve, reject) => {
    const reader = new FileReader();
    reader.onload = (ev) => {
      try {
        const data = JSON.parse(ev.target!.result as string);
        layout.pushUndo();
        if (data.gridSize) layout.gridSize = data.gridSize;
        if (data.minSpacing) layout.minSpacing = data.minSpacing;
        layout.symbols = [];
        layout.nextId = 1;
        for (const s of data.symbols) {
          layout.symbols.push(deserializeSymbol(s, layout.nextId++));
        }
        layout.markDirty();
        layout.saveMcmState();
        resolve();
      } catch (err) {
        reject(err);
      }
    };
    reader.readAsText(file);
  });
}
