(function attachPyroStrokeStyle(root) {
  'use strict';

  const MAX_STROKES = 12;
  const patterns = Object.freeze(['solid', 'dashed', 'dotted', 'zigzag', 'custom']);
  const lineCaps = Object.freeze(['butt', 'round', 'square']);
  const lineJoins = Object.freeze(['miter', 'round', 'bevel']);
  const alignments = Object.freeze(['inside', 'center', 'outside']);
  const markers = Object.freeze(['none', 'open-arrow', 'triangle', 'circle', 'diamond', 'bar']);
  const strokeTypes = Object.freeze(['none', 'basic-hard', 'basic-hard-rounded', 'basic-soft', 'basic-soft-rounded']);
  const finite = (value, fallback = 0) => Number.isFinite(Number(value)) ? Number(value) : fallback;
  const clamp = (value, minimum, maximum) => Math.min(maximum, Math.max(minimum, value));
  const round = (value, digits = 4) => Number(finite(value).toFixed(digits));
  const oneOf = (value, allowed, fallback) => allowed.includes(value) ? value : fallback;
  const clone = (value) => JSON.parse(JSON.stringify(value));

  function color(value, fallback = '#172033') {
    const text = String(value || '').trim().toLowerCase();
    if (/^#[0-9a-f]{6}$/.test(text)) return text;
    if (/^#[0-9a-f]{3}$/.test(text)) return `#${[...text.slice(1)].map((entry) => entry.repeat(2)).join('')}`;
    return /^#[0-9a-f]{6}$/.test(String(fallback || '').toLowerCase()) ? String(fallback).toLowerCase() : '#172033';
  }

  function dashArray(value, fallback = [8, 4]) {
    const entries = (Array.isArray(value) ? value : String(value || '').split(/[\s,;]+/))
      .map((entry) => finite(entry, NaN)).filter((entry) => Number.isFinite(entry) && entry > 0).slice(0, 32)
      .map((entry) => round(clamp(entry, .01, 10000), 3));
    return entries.length ? entries : clone(fallback);
  }

  function safeId(value, fallback) {
    const text = String(value || '').trim().replace(/[^a-z0-9_-]/gi, '-').slice(0, 96);
    return text || fallback;
  }

  function normalizeStyle(value = {}, options = {}) {
    const fallback = options.fallback || {};
    const index = Math.max(0, Math.round(finite(options.index, 0)));
    const createId = typeof options.createId === 'function' ? options.createId : null;
    const rawId = value?.id || (index === 0 ? 'stroke-primary' : '');
    const id = safeId(rawId, createId ? createId('stroke') : `stroke-${index + 1}`);
    const width = clamp(finite(value?.width, finite(fallback.width, 3)), 0, 10000);
    const strokeType = oneOf(value?.strokeType, strokeTypes, oneOf(fallback.strokeType, strokeTypes, 'basic-soft-rounded'));
    const fallbackCap = oneOf(fallback.lineCap, lineCaps, strokeType.endsWith('rounded') ? 'round' : 'butt');
    const fallbackJoin = oneOf(fallback.lineJoin, lineJoins, strokeType.endsWith('rounded') ? 'round' : 'miter');
    return {
      id,
      enabled: value?.enabled !== false,
      color: color(value?.color, fallback.color),
      width: round(width, 3),
      strokeType,
      pattern: oneOf(value?.pattern, patterns, oneOf(fallback.pattern, patterns, 'solid')),
      dashArray: dashArray(value?.dashArray, dashArray(fallback.dashArray, [8, 4])),
      dashOffset: round(clamp(finite(value?.dashOffset, finite(fallback.dashOffset, 0)), -10000, 10000), 3),
      lineCap: oneOf(value?.lineCap, lineCaps, fallbackCap),
      lineJoin: oneOf(value?.lineJoin, lineJoins, fallbackJoin),
      miterLimit: round(clamp(finite(value?.miterLimit, finite(fallback.miterLimit, 10)), 1, 1000), 3),
      startMarker: oneOf(value?.startMarker, markers, oneOf(fallback.startMarker, markers, 'none')),
      endMarker: oneOf(value?.endMarker, markers, oneOf(fallback.endMarker, markers, 'none')),
      startMarkerSize: round(clamp(finite(value?.startMarkerSize, finite(fallback.startMarkerSize, 12)), 1, 1000), 3),
      endMarkerSize: round(clamp(finite(value?.endMarkerSize, finite(fallback.endMarkerSize, 12)), 1, 1000), 3),
      alignment: oneOf(value?.alignment, alignments, oneOf(fallback.alignment, alignments, 'center'))
    };
  }

  function legacyStyle(object = {}) {
    const text = object.type === 'text';
    const rawColor = text ? object.textStroke : object.stroke;
    const width = text ? object.textStrokeWidth : object.strokeWidth;
    const strokeType = oneOf(object.strokeType, strokeTypes, 'basic-soft-rounded');
    const visibleColor = !['none', 'transparent', ''].includes(String(rawColor || '').toLowerCase());
    return normalizeStyle({
      id: 'stroke-primary', enabled: visibleColor && finite(width, 0) > 0 && strokeType !== 'none',
      color: visibleColor ? rawColor : '#172033', width, strokeType,
      pattern: object.strokePattern, dashArray: object.strokeDashArray, dashOffset: object.strokeDashOffset,
      lineCap: object.strokeLineCap, lineJoin: object.strokeLineJoin, miterLimit: object.strokeMiterLimit,
      startMarker: object.strokeStartMarker, endMarker: object.strokeEndMarker,
      startMarkerSize: object.strokeStartMarkerSize, endMarkerSize: object.strokeEndMarkerSize,
      alignment: object.strokeAlignment
    });
  }

  function normalizeStack(value, options = {}) {
    const fallback = normalizeStyle(options.fallback || {});
    const source = Array.isArray(value) ? value.slice(0, MAX_STROKES) : [];
    const ids = new Set();
    return source.map((entry, index) => {
      const style = normalizeStyle(entry, { fallback, index, createId: options.createId });
      if (ids.has(style.id)) style.id = safeId(options.createId?.('stroke'), `stroke-${index + 1}-${ids.size}`);
      ids.add(style.id); return style;
    });
  }

  function stackFor(object, options = {}) {
    const fallback = legacyStyle(object);
    const normalized = normalizeStack(object?.strokeStack, { ...options, fallback });
    return normalized.length ? normalized : [fallback];
  }

  function ensureStack(object, options = {}) {
    if (!object) return [];
    object.strokeStack = stackFor(object, options);
    syncLegacy(object); return object.strokeStack;
  }

  function syncLegacy(object) {
    if (!object) return null;
    const primary = Array.isArray(object.strokeStack) && object.strokeStack.length
      ? normalizeStyle(object.strokeStack[0], { fallback: legacyStyle(object), index: 0 })
      : legacyStyle(object);
    if (Array.isArray(object.strokeStack) && object.strokeStack.length) object.strokeStack[0] = primary;
    const visible = primary.enabled && primary.strokeType !== 'none' && primary.width > 0;
    if (object.type === 'text') {
      object.textStroke = visible ? primary.color : 'none'; object.textStrokeWidth = primary.width;
    } else {
      object.stroke = visible ? primary.color : 'none'; object.strokeWidth = primary.width;
    }
    object.strokeType = visible ? primary.strokeType : 'none';
    object.strokePattern = primary.pattern; object.strokeDashArray = clone(primary.dashArray); object.strokeDashOffset = primary.dashOffset;
    object.strokeLineCap = primary.lineCap; object.strokeLineJoin = primary.lineJoin; object.strokeMiterLimit = primary.miterLimit;
    object.strokeStartMarker = primary.startMarker; object.strokeEndMarker = primary.endMarker;
    object.strokeStartMarkerSize = primary.startMarkerSize; object.strokeEndMarkerSize = primary.endMarkerSize; object.strokeAlignment = primary.alignment;
    return primary;
  }

  function syncPrimaryFromLegacy(object) {
    if (!Array.isArray(object?.strokeStack) || !object.strokeStack.length) return legacyStyle(object);
    const legacy = legacyStyle(object); const current = normalizeStyle(object.strokeStack[0], { fallback: legacy, index: 0 });
    current.color = legacy.color; current.width = legacy.width; current.strokeType = legacy.strokeType; current.enabled = legacy.enabled;
    object.strokeStack[0] = current; return current;
  }

  function isVisible(style) {
    return Boolean(style?.enabled !== false && style?.strokeType !== 'none' && finite(style?.width, 0) > 0 && /^#[0-9a-f]{6}$/i.test(String(style?.color || '')));
  }

  function renderedDashArray(style) {
    const normalized = normalizeStyle(style);
    if (normalized.pattern === 'solid' || normalized.pattern === 'zigzag') return [];
    if (normalized.pattern === 'dashed') return [round(Math.max(.01, normalized.width * 3), 3), round(Math.max(.01, normalized.width * 2), 3)];
    if (normalized.pattern === 'dotted') return [.01, round(Math.max(1, normalized.width * 2), 3)];
    return normalized.dashArray;
  }

  function zigzagMetrics(style) {
    const normalized = normalizeStyle(style);
    return {
      amplitude: round(Math.max(1, normalized.width * 1.25), 3),
      spacing: round(Math.max(3, normalized.width * 2.5), 3)
    };
  }

  function dashText(style) { return normalizeStyle(style).dashArray.join(' '); }

  const api = Object.freeze({
    MAX_STROKES, patterns, lineCaps, lineJoins, alignments, markers, strokeTypes,
    color, dashArray, normalizeStyle, legacyStyle, normalizeStack, stackFor, ensureStack,
    syncLegacy, syncPrimaryFromLegacy, isVisible, renderedDashArray, zigzagMetrics, dashText
  });
  root.PyroStrokeStyle = api;
  if (typeof module === 'object' && module?.exports) module.exports = api;
})(typeof globalThis !== 'undefined' ? globalThis : self);
