import { describe, it, expect, beforeAll } from 'vitest';
import { readFileSync } from 'node:fs';
import { resolve, dirname } from 'node:path';
import { fileURLToPath } from 'node:url';
import { createRequire } from 'node:module';

const __dirname = dirname(fileURLToPath(import.meta.url));
const require = createRequire(import.meta.url);

let core: any;
const cubeOBJ = readFileSync(resolve(__dirname, '../fixtures/cube.obj'), 'utf-8');
function loadCube(): number { return core.loadMeshFromOBJ(cubeOBJ); }

beforeAll(async () => {
  const MeshLabCore = require(resolve(__dirname, '../../public/meshlab-core.cjs'));
  core = await MeshLabCore();
}, 15000);

// ========== Sprint P: Boundary/NonManifold edges ==========
describe('Boundary Edge Detection', () => {
  it('returns boundary edges for open mesh', () => {
    const obj = 'v 0 0 0\nv 1 0 0\nv 0 1 0\nf 1 2 3';
    const h = core.loadMeshFromOBJ(obj);
    const edges = core.getBoundaryEdges(h);
    expect(edges.length).toBeGreaterThan(0); // 3 boundary edges * 6 floats
    expect(edges.length).toBe(18); // 3 edges * 2 vertices * 3 coords
    core.deleteMesh(h);
  });

  it('returns no boundary edges for closed mesh', () => {
    const h = loadCube();
    const edges = core.getBoundaryEdges(h);
    expect(edges.length).toBe(0); // Closed mesh has no boundary
    core.deleteMesh(h);
  });
});

describe('Non-Manifold Edge Detection', () => {
  it('returns empty for well-formed mesh', () => {
    const h = loadCube();
    const edges = core.getNonManifoldEdges(h);
    expect(edges.length).toBe(0);
    core.deleteMesh(h);
  });
});

// ========== Sprint S: LSCM Parametrization ==========
describe('LSCM Parametrization', () => {
  it('assigns UV coordinates to mesh', () => {
    const h = loadCube();
    core.filterLSCM(h);
    expect(core.meshHasUVs(h)).toBe(true);
    const uvs = core.getUVBuffer(h);
    expect(uvs.length).toBeGreaterThan(0);
    // UVs should be in [0,1] range
    for (let i = 0; i < uvs.length; i++) {
      expect(uvs[i]).toBeGreaterThanOrEqual(-0.01);
      expect(uvs[i]).toBeLessThanOrEqual(1.01);
    }
    core.deleteMesh(h);
  });

  it('assigns UVs to sphere', () => {
    const h = core.generateTestSphere(2);
    core.filterLSCM(h);
    expect(core.meshHasUVs(h)).toBe(true);
    core.deleteMesh(h);
  });
});

// ========== Sprint R: X3D format ==========
describe('X3D Format', () => {
  it('exportX3D produces valid XML', () => {
    // Test the export logic inline
    const vertices = new Float32Array([0,0,0, 1,0,0, 0,1,0]);
    const indices = new Uint32Array([0,1,2]);
    let vStr = '';
    for (let i = 0; i < vertices.length; i += 3) {
      if (i > 0) vStr += ' ';
      vStr += `${vertices[i]} ${vertices[i+1]} ${vertices[i+2]}`;
    }
    let iStr = '';
    for (let i = 0; i < indices.length; i += 3) {
      if (i > 0) iStr += ' ';
      iStr += `${indices[i]} ${indices[i+1]} ${indices[i+2]} -1`;
    }
    const x3d = `<?xml version="1.0"?><X3D><Scene><Shape>` +
      `<IndexedFaceSet coordIndex="${iStr}"><Coordinate point="${vStr}"/>` +
      `</IndexedFaceSet></Shape></Scene></X3D>`;
    expect(x3d).toContain('IndexedFaceSet');
    expect(x3d).toContain('Coordinate');
  });
});

// ========== DXF/WRL Export ==========
describe('DXF Export Format', () => {
  it('generates 3DFACE entities', () => {
    let dxf = '0\nSECTION\n2\nENTITIES\n';
    dxf += '0\n3DFACE\n8\n0\n';
    dxf += '0\nENDSEC\n0\nEOF\n';
    expect(dxf).toContain('3DFACE');
    expect(dxf).toContain('EOF');
  });
});

describe('WRL Export Format', () => {
  it('generates VRML header', () => {
    const wrl = '#VRML V2.0 utf8\n\nShape { geometry IndexedFaceSet {} }';
    expect(wrl).toContain('#VRML V2.0');
  });
});
