import React, { CSSProperties } from 'react';
import { useNode, UserComponent } from '@craftjs/core';
import { cssPropsToString } from '../../utils/style-helpers';
import { escapeHtml, escapeAttr, scopeId, cssValue } from '../../utils/escape';

interface Counter {
  number: number;
  suffix: string;
  label: string;
}

interface NumberCounterProps {
  counters?: Counter[];
  columns?: number;
  numberColor?: string;
  labelColor?: string;
  numberSize?: string;
  style?: CSSProperties;
  animation?: string;
  animationDelay?: string;
  hideOnDesktop?: boolean;
  hideOnTablet?: boolean;
  hideOnMobile?: boolean;
}

const defaultCounters: Counter[] = [
  { number: 150, suffix: '+', label: 'Projects' },
  { number: 50, suffix: '+', label: 'Clients' },
  { number: 10, suffix: '', label: 'Years' },
  { number: 99, suffix: '%', label: 'Satisfaction' },
];

export const NumberCounter: UserComponent<NumberCounterProps> = ({
  counters = defaultCounters,
  columns = 4,
  numberColor = '#3b82f6',
  labelColor = '#6b7280',
  numberSize = '48px',
  style = {},
}) => {
  const {
    connectors: { connect, drag },
    selected,
  } = useNode((node) => ({
    selected: node.events.selected,
  }));

  const items = counters.length > 0 ? counters : defaultCounters;

  return (
    <section
      ref={(ref: HTMLElement | null): void => { if (ref) connect(drag(ref)); }}
      style={{
        padding: '60px 20px',
        backgroundColor: '#ffffff',
        outline: selected ? '2px solid #3b82f6' : 'none',
        ...style,
      }}
    >
      <div
        style={{
          maxWidth: '1100px',
          margin: '0 auto',
          display: 'grid',
          gridTemplateColumns: `repeat(${columns}, 1fr)`,
          gap: '32px',
          textAlign: 'center',
        }}
      >
        {items.map((counter, i) => (
          <div key={i} style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: '8px' }}>
            <span style={{
              fontSize: numberSize,
              fontWeight: '700',
              color: numberColor,
              lineHeight: '1.1',
              fontFamily: 'Inter, sans-serif',
            }}>
              {counter.number}{counter.suffix}
            </span>
            <span style={{
              fontSize: '15px',
              color: labelColor,
              fontFamily: 'Inter, sans-serif',
              fontWeight: '500',
            }}>
              {counter.label}
            </span>
          </div>
        ))}
      </div>
    </section>
  );
};

/* ---------- Craft config ---------- */

NumberCounter.craft = {
  displayName: 'Number Counter',
  props: {
    counters: defaultCounters,
    columns: 4,
    numberColor: '#3b82f6',
    labelColor: '#6b7280',
    numberSize: '48px',
    style: { backgroundColor: '#ffffff' },
    animation: '',
    animationDelay: '',
    hideOnDesktop: false,
    hideOnTablet: false,
    hideOnMobile: false,
  },
  rules: {
    canDrag: () => true,
    canMoveIn: () => false,
    canMoveOut: () => true,
  },
};

/* ---------- HTML export ---------- */

(NumberCounter as any).toHtml = (props: NumberCounterProps, _childrenHtml: string, nodeId?: string) => {
  const {
    counters = defaultCounters,
    style = {},
  } = props;
  // Number() coercion: `columns` is a raw string-interpolation sink into the
  // grid style attribute below (repeat(${columns},1fr)); Number() of
  // anything non-numeric collapses safely to NaN instead of breaking out.
  const columns = Number(props.columns) || 4;
  // Sanitized -- raw string-interpolation sinks in the counter/label spans
  // below.
  const numberColor = cssValue(props.numberColor) || '#3b82f6';
  const labelColor = cssValue(props.labelColor) || '#6b7280';
  const numberSize = cssValue(props.numberSize) || '48px';

  const items = counters.length > 0 ? counters : defaultCounters;

  // Deterministic AND unique id for this counter instance's wrapper/span
  // ids and getElementById() calls inside its inline script -- scoped on
  // the Craft node id so two NumberCounter instances (e.g. both left at
  // default props) don't collide and end up animating each other's digits.
  const seed = items.map((c) => `${c.number}${c.suffix}::${c.label}`).join('|');
  const uid = scopeId(nodeId, seed, 'nc');

  const sectionStyle = cssPropsToString({
    padding: '60px 20px',
    backgroundColor: '#ffffff',
    ...style,
  });

  const countersHtml = items.map((counter, i) => {
    // Number() coercion + escapeAttr: `counter.number` is declared `number`
    // per-item inside an array prop, but is NOT type-checked at runtime --
    // it arrives raw via the AI `update_props` path or a deserialized
    // saved-state blob and was previously interpolated straight into this
    // data-target attribute, letting a string like `150"><script>...`
    // break out of the attribute and inject markup. Number() collapses any
    // non-numeric value safely to NaN (then 0), and escapeAttr is kept as
    // defense-in-depth in case Number()'s string coercion output ever
    // contains a stray character (it can't today, but the sink should never
    // rely solely on the coercion).
    const numberVal = Number(counter.number);
    const safeNumber = Number.isFinite(numberVal) ? numberVal : 0;
    return `<div style="display:flex;flex-direction:column;align-items:center;gap:8px">
      <span id="${uid}_n${i}" data-target="${escapeAttr(String(safeNumber))}" data-suffix="${escapeAttr(counter.suffix)}" style="font-size:${numberSize};font-weight:700;color:${numberColor};line-height:1.1;font-family:Inter,sans-serif">0${escapeHtml(counter.suffix)}</span>
      <span style="font-size:15px;color:${labelColor};font-family:Inter,sans-serif;font-weight:500">${escapeHtml(counter.label)}</span>
    </div>`;
  }).join('\n    ');

  return {
    html: `<section${sectionStyle ? ` style="${sectionStyle}"` : ''}>
  <div id="${uid}" style="max-width:1100px;margin:0 auto;display:grid;grid-template-columns:repeat(${columns},1fr);gap:32px;text-align:center">
    ${countersHtml}
  </div>
  <script>
  (function(){
    var uid="${uid}",started=false;
    function animate(){
      if(started)return;started=true;
      for(var i=0;i<${items.length};i++){
        (function(el){
          var target=parseInt(el.getAttribute("data-target")),
              suffix=el.getAttribute("data-suffix")||"",
              current=0,
              step=Math.max(1,Math.floor(target/60)),
              timer=setInterval(function(){
                current+=step;
                if(current>=target){current=target;clearInterval(timer);}
                el.textContent=current+suffix;
              },16);
        })(document.getElementById(uid+"_n"+i));
      }
    }
    if("IntersectionObserver"in window){
      var obs=new IntersectionObserver(function(entries){
        entries.forEach(function(e){if(e.isIntersecting){animate();obs.disconnect();}});
      },{threshold:0.2});
      obs.observe(document.getElementById(uid));
    }else{animate();}
  })();
  </script>
</section>`,
  };
};
