/* eslint-disable no-undef */
// =====================================================
// Poincaré classification diagram in (tr A, det A) plane
// =====================================================

function classifyTrDet(tr, det) {
  if (det < 0) return { type: 'saddle', label: 'Седло', stable: false, color: '#a8392c' };
  if (Math.abs(det) < 1e-9) return { type: 'degenerateZero', label: 'Вырожденный (det = 0)', stable: false, color: '#8b8579' };
  const D = tr * tr - 4 * det;
  if (Math.abs(tr) < 1e-6 && det > 0) return { type: 'center', label: 'Центр', stable: 'lyapunov', color: '#6b4290' };
  if (D > 1e-6) {
    if (tr < 0) return { type: 'stableNode', label: 'Устойчивый узел', stable: true, color: '#2a5e9e' };
    return { type: 'unstableNode', label: 'Неустойчивый узел', stable: false, color: '#2a5e9e' };
  }
  if (D < -1e-6) {
    if (tr < 0) return { type: 'stableFocus', label: 'Устойчивый фокус', stable: true, color: '#1f8a76' };
    return { type: 'unstableFocus', label: 'Неустойчивый фокус', stable: false, color: '#1f8a76' };
  }
  if (tr < 0) return { type: 'degenerateNode', label: 'Вырожденный узел (D = 0)', stable: true, color: '#b8581f' };
  return { type: 'degenerateNode', label: 'Вырожденный узел (D = 0)', stable: false, color: '#b8581f' };
}

function PoincareDiagramViz() {
  const [tr, setTr] = useState(-2);
  const [det, setDet] = useState(1.5);
  const [drag, setDrag] = useState(false);

  const cls = classifyTrDet(tr, det);

  const canvasRef = useCanvas((ctx, W, H) => {
    const T = makeTransform({ xMin: -5, xMax: 5, yMin: -3, yMax: 7, W, H, pad: 42 });

    // background
    ctx.fillStyle = '#fbf6e9';
    ctx.fillRect(0, 0, W, H);

    // det < 0 -> saddle (red region)
    ctx.save();
    ctx.fillStyle = 'rgba(168, 57, 44, 0.10)';
    ctx.fillRect(T.X(T.xMin), T.Y(0), T.X(T.xMax) - T.X(T.xMin), T.Y(T.yMin) - T.Y(0));
    ctx.restore();

    // Parabola points: det = tr^2 / 4
    const samples = 200;
    const parabolaPts = [];
    for (let i = 0; i <= samples; i++) {
      const x = T.xMin + (i / samples) * (T.xMax - T.xMin);
      parabolaPts.push([x, x * x / 4]);
    }

    // Fill focus region (above parabola, det > 0)
    ctx.save();
    ctx.fillStyle = 'rgba(31, 138, 118, 0.10)';
    ctx.beginPath();
    ctx.moveTo(T.X(T.xMin), T.Y(T.yMax));
    ctx.lineTo(T.X(T.xMax), T.Y(T.yMax));
    for (let i = samples; i >= 0; i--) {
      const [x, y] = parabolaPts[i];
      ctx.lineTo(T.X(x), T.Y(Math.max(y, 0)));
    }
    ctx.closePath();
    ctx.fill();
    ctx.restore();

    // Fill node region (between det=0 and parabola, det>0)
    ctx.save();
    ctx.fillStyle = 'rgba(42, 94, 158, 0.10)';
    ctx.beginPath();
    ctx.moveTo(T.X(T.xMin), T.Y(0));
    ctx.lineTo(T.X(T.xMax), T.Y(0));
    for (let i = samples; i >= 0; i--) {
      const [x, y] = parabolaPts[i];
      if (y >= 0) ctx.lineTo(T.X(x), T.Y(y));
    }
    ctx.closePath();
    ctx.fill();
    ctx.restore();

    // axes
    drawAxes(ctx, T, {
      tickStep: 1,
      xLabel: 'tr A',
      yLabel: 'det A',
      gridColor: 'rgba(28,24,16,0.05)',
      axisColor: 'rgba(28,24,16,0.4)',
    });

    // parabola D = 0
    drawPath(ctx, T, parabolaPts.filter(p => p[1] >= 0), { color: '#b8581f', lineWidth: 2.6 });
    // centers — vertical line tr=0, det>0
    ctx.save();
    ctx.strokeStyle = '#6b4290';
    ctx.lineWidth = 2.4;
    ctx.beginPath();
    ctx.moveTo(T.X(0), T.Y(0));
    ctx.lineTo(T.X(0), T.Y(T.yMax));
    ctx.stroke();
    ctx.restore();

    // region labels
    ctx.save();
    ctx.font = '600 13px Inter, sans-serif';
    ctx.textAlign = 'center';
    ctx.fillStyle = '#2a5e9e';
    ctx.fillText('устойчивый узел', T.X(-2.8), T.Y(1.2));
    ctx.fillText('неустойчивый узел', T.X(2.8), T.Y(1.2));
    ctx.fillStyle = '#1f8a76';
    ctx.fillText('устойчивый фокус', T.X(-2.4), T.Y(5));
    ctx.fillText('неустойчивый фокус', T.X(2.4), T.Y(5));
    ctx.fillStyle = '#a8392c';
    ctx.font = '600 14px Inter, sans-serif';
    ctx.fillText('СЕДЛО', T.X(0), T.Y(-1.5));
    ctx.font = '500 11px Inter, sans-serif';
    ctx.fillText('(всегда неустойчиво)', T.X(0), T.Y(-1.5) + 16);
    ctx.fillStyle = '#6b4290';
    ctx.font = '600 13px Inter, sans-serif';
    ctx.fillText('ЦЕНТР', T.X(0), T.Y(6.3));
    ctx.fillStyle = '#b8581f';
    ctx.font = 'italic 11.5px Source Serif Pro, serif';
    ctx.textAlign = 'left';
    ctx.fillText('D = (tr A)² − 4 det A = 0', T.X(-4.6), T.Y(3.4));
    ctx.restore();

    // current point
    drawDot(ctx, T, tr, det, { color: cls.color, stroke: '#fbf6e9', r: 7.5, lineWidth: 2.5 });
    // crosshair
    ctx.save();
    ctx.strokeStyle = 'rgba(28,24,16,0.18)';
    ctx.lineWidth = 1;
    ctx.setLineDash([3, 4]);
    ctx.beginPath();
    ctx.moveTo(T.X(tr), T.Y(T.yMin)); ctx.lineTo(T.X(tr), T.Y(T.yMax));
    ctx.moveTo(T.X(T.xMin), T.Y(det)); ctx.lineTo(T.X(T.xMax), T.Y(det));
    ctx.stroke();
    ctx.restore();

  }, [tr, det]);

  // dragging point
  const onMouse = (e) => {
    const cvs = canvasRef.current;
    const rect = cvs.getBoundingClientRect();
    const W = rect.width, H = rect.height;
    const T = makeTransform({ xMin: -5, xMax: 5, yMin: -3, yMax: 7, W, H, pad: 42 });
    const px = e.clientX - rect.left;
    const py = e.clientY - rect.top;
    const newTr = T.xMin + (px - T.pad) / (W - 2 * T.pad) * (T.xMax - T.xMin);
    const newDet = T.yMin + ((H - py - T.pad) / (H - 2 * T.pad)) * (T.yMax - T.yMin);
    setTr(Math.max(T.xMin, Math.min(T.xMax, newTr)));
    setDet(Math.max(T.yMin, Math.min(T.yMax, newDet)));
  };

  // Mini phase portrait — A = [[0, -1], [det, tr]] gives matching tr/det.
  const miniA = 0, miniB = -1, miniC = det, miniD = tr;
  const F = useCallback((y) => [miniA * y[0] + miniB * y[1], miniC * y[0] + miniD * y[1]], [miniA, miniB, miniC, miniD]);
  const miniRef = useCanvas((ctx, W, H) => {
    const T = makeTransform({ xMin: -2, xMax: 2, yMin: -2, yMax: 2, W, H, pad: 8 });
    ctx.fillStyle = '#fbf6e9'; ctx.fillRect(0, 0, W, H);
    drawStreamlines(ctx, T, F, { seeds: 80, color: 'rgba(70,60,40,0.32)', lineWidth: 1, steps: 40, h: 0.05, arrowSize: 2.5 });

    // a few colored trajectories
    const N = 6;
    const eigs = eigsOf2x2(miniA, miniB, miniC, miniD);
    const stable = (eigs.type === 'real' && eigs.l1 < 0 && eigs.l2 < 0) ||
                   (eigs.type === 'complex' && eigs.re < 0);
    const unstable = (eigs.type === 'real' && eigs.l1 > 0 && eigs.l2 > 0) ||
                     (eigs.type === 'complex' && eigs.re > 0);
    const isSaddle = eigs.type === 'real' && eigs.l1 * eigs.l2 < 0;

    for (let i = 0; i < N; i++) {
      const ang = (i / N) * Math.PI * 2 + 0.2;
      const r = 1.6;
      const p = [r * Math.cos(ang), r * Math.sin(ang)];
      let allPts;
      try {
        const tEnd = 5;
        const fwd = integrateVector(F, 0, p, tEnd, 0.04, { yBound: 50, maxSteps: 500 });
        const fpts = fwd.map(s => s.y);
        if (stable || isSaddle) {
          const bwd = integrateVector(F, 0, p, -tEnd, -0.04, { yBound: 50, maxSteps: 500 });
          const bpts = bwd.map(s => s.y).reverse();
          allPts = bpts.concat(fpts.slice(1));
        } else {
          allPts = fpts;
        }
      } catch (e) { continue; }
      if (!allPts || allPts.length < 2) continue;
      drawPath(ctx, T, allPts, { color: TRAJ_COLORS[i % TRAJ_COLORS.length], lineWidth: 1.5 });
    }
    drawDot(ctx, T, 0, 0, { color: '#1c1810', stroke: '#fbf6e9', r: 3.5, lineWidth: 1.5 });
  }, [tr, det, F]);

  return (
    <VizWrap
      title="Диаграмма Пуанкаре: классификация особых точек 2D-системы"
      captionUnder="Перемещайте точку в плоскости (tr A, det A). Парабола D = (tr A)² − 4 det A = 0 разделяет узлы и фокусы. Вертикальная ось tr A = 0 при det A > 0 — центры. Знак tr A отвечает за устойчивость."
      controls={
        <React.Fragment>
          <h4>Текущая точка</h4>
          <Slider label="tr A" value={tr} setValue={setTr} min={-5} max={5} step={0.05} />
          <Slider label="det A" value={det} setValue={setDet} min={-3} max={7} step={0.05} />

          <h4>Классификация</h4>
          <div style={{
            fontFamily: 'Inter, sans-serif',
            fontSize: 14, fontWeight: 600,
            color: cls.color, padding: '8px 0',
          }}>
            {cls.label}
          </div>
          <div className="ctl-note">
            D = (tr)² − 4·det = <span style={{ fontFamily: 'JetBrains Mono, monospace', color: 'var(--orange)' }}>
              {(tr * tr - 4 * det).toFixed(2)}
            </span>
          </div>

          <h4>Фазовый портрет</h4>
          <div style={{ position: 'relative', width: '100%', paddingBottom: '100%', background: '#fbf6e9', borderRadius: 6, overflow: 'hidden', border: '1px solid var(--line)' }}>
            <canvas ref={miniRef} style={{ position: 'absolute', inset: 0, display: 'block', width: '100%', height: '100%' }} />
          </div>
          <div className="ctl-note">
            Превью системы ẋ = (0, −1; det, tr)·x
          </div>
        </React.Fragment>
      }
    >
      <canvas
        ref={canvasRef}
        onMouseDown={(e) => { setDrag(true); onMouse(e); }}
        onMouseMove={(e) => { if (drag) onMouse(e); }}
        onMouseUp={() => setDrag(false)}
        onMouseLeave={() => setDrag(false)}
        style={{ cursor: drag ? 'grabbing' : 'crosshair' }}
      />
      <div className="viz-legend chip bottom-right" style={{ color: cls.color, fontFamily: 'Inter, sans-serif', fontWeight: 600, fontSize: 13 }}>
        {cls.label}
      </div>
    </VizWrap>
  );
}

window.PoincareDiagramViz = PoincareDiagramViz;
window.classifyTrDet = classifyTrDet;
