/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */

/**
 * Unit tests for the symbolic-annotation segment helpers.
 *
 * The full React hook is exercised at integration time (covered by the
 * #653 manual smoke test); here we lock in the geometry conversion that
 * turns WASM-returned polylines/arcs into DrawingLine2D pairs.
 */

import { describe, it } from 'node:test';
import assert from 'node:assert/strict';
import type { DrawingLine2D } from '@ifc-lite/renderer';
import { polylineToSegments, circleToSegments, liftTo3DLineList } from './useSymbolicAnnotations.js';

describe('polylineToSegments', () => {
  it('emits N-1 segments for an open polyline', () => {
    // 3 points -> 2 segments
    const points = new Float32Array([0, 0, 1, 0, 1, 1]);
    const out: DrawingLine2D[] = [];
    polylineToSegments(points, 3, false, out);
    assert.equal(out.length, 2);
    assert.deepEqual(out[0].line.start, { x: 0, y: 0 });
    assert.deepEqual(out[0].line.end, { x: 1, y: 0 });
    assert.deepEqual(out[1].line.start, { x: 1, y: 0 });
    assert.deepEqual(out[1].line.end, { x: 1, y: 1 });
    assert.equal(out[0].category, 'annotation');
  });

  it('adds a closing segment when isClosed is true', () => {
    const points = new Float32Array([0, 0, 2, 0, 2, 2]);
    const out: DrawingLine2D[] = [];
    polylineToSegments(points, 3, true, out);
    // 2 open + 1 closing = 3
    assert.equal(out.length, 3);
    assert.deepEqual(out[2].line.start, { x: 2, y: 2 });
    assert.deepEqual(out[2].line.end, { x: 0, y: 0 });
  });

  it('skips the closing segment for two-point polylines even when isClosed', () => {
    const points = new Float32Array([0, 0, 1, 0]);
    const out: DrawingLine2D[] = [];
    polylineToSegments(points, 2, true, out);
    assert.equal(out.length, 1);
  });

  it('appends to an existing output array without clearing it', () => {
    const points = new Float32Array([0, 0, 1, 0]);
    const out: DrawingLine2D[] = [
      { line: { start: { x: 9, y: 9 }, end: { x: 8, y: 8 } }, category: 'preset' },
    ];
    polylineToSegments(points, 2, false, out);
    assert.equal(out.length, 2);
    assert.equal(out[0].category, 'preset');
    assert.equal(out[1].category, 'annotation');
  });
});

describe('circleToSegments', () => {
  it('tessellates a full circle into 32 segments', () => {
    const out: DrawingLine2D[] = [];
    circleToSegments(0, 0, 1, 0, Math.PI * 2, true, out);
    assert.equal(out.length, 32);
  });

  it('tessellates an arc into 16 segments', () => {
    const out: DrawingLine2D[] = [];
    circleToSegments(0, 0, 1, 0, Math.PI, false, out);
    assert.equal(out.length, 16);
  });

  it('places points on the circle within numerical tolerance', () => {
    const out: DrawingLine2D[] = [];
    circleToSegments(5, 7, 3, 0, Math.PI * 2, true, out);
    for (const seg of out) {
      const dx = seg.line.start.x - 5;
      const dy = seg.line.start.y - 7;
      const r = Math.sqrt(dx * dx + dy * dy);
      assert.ok(Math.abs(r - 3) < 1e-5, `expected radius 3, got ${r}`);
    }
  });

  it('marks output with category "annotation"', () => {
    const out: DrawingLine2D[] = [];
    circleToSegments(0, 0, 1, 0, Math.PI / 2, false, out);
    assert.ok(out.every((seg) => seg.category === 'annotation'));
  });
});

describe('liftTo3DLineList', () => {
  it('emits six floats per segment with the storey Y in the middle slot', () => {
    const lines: DrawingLine2D[] = [
      { line: { start: { x: 1, y: 2 }, end: { x: 3, y: 4 } }, category: 'annotation' },
      { line: { start: { x: 5, y: 6 }, end: { x: 7, y: 8 } }, category: 'annotation' },
    ];
    const out: number[] = [];
    liftTo3DLineList(lines, 5.45, out);
    assert.deepEqual(out, [
      1, 5.45, 2,   3, 5.45, 4,
      5, 5.45, 6,   7, 5.45, 8,
    ]);
  });

  it('appends to an existing output array', () => {
    const out: number[] = [99, 99, 99];
    liftTo3DLineList(
      [{ line: { start: { x: 0, y: 0 }, end: { x: 1, y: 1 } }, category: 'annotation' }],
      2,
      out,
    );
    assert.equal(out.length, 9);
    assert.equal(out[0], 99);
    assert.equal(out[3], 0);
    assert.equal(out[4], 2);
  });

  it('does nothing for an empty input', () => {
    const out: number[] = [];
    liftTo3DLineList([], 1, out);
    assert.equal(out.length, 0);
  });
});
