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';
import { parseUSDA } from '../../src/loaders/usdLoader';

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

let core: any;

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

describe('USDA Parser', () => {
  it('parses a simple triangle mesh', () => {
    const usda = `#usda 1.0
def Xform "Root" {
  def Mesh "Triangle" {
    int[] faceVertexCounts = [3]
    int[] faceVertexIndices = [0, 1, 2]
    point3f[] points = [(0,0,0), (1,0,0), (0,1,0)]
  }
}`;
    const meshes = parseUSDA(usda);
    expect(meshes.length).toBe(1);
    expect(meshes[0].name).toBe('Triangle');
    expect(meshes[0].points.length).toBe(9);
    expect(meshes[0].faceVertexIndices.length).toBe(3);
    expect(meshes[0].faceVertexIndices).toEqual([0, 1, 2]);
  });

  it('parses multiple meshes', () => {
    const usda = `#usda 1.0
def Xform "Root" {
  def Mesh "Mesh1" {
    int[] faceVertexCounts = [3]
    int[] faceVertexIndices = [0, 1, 2]
    point3f[] points = [(0,0,0), (1,0,0), (0,1,0)]
  }
  def Mesh "Mesh2" {
    int[] faceVertexCounts = [3]
    int[] faceVertexIndices = [0, 1, 2]
    point3f[] points = [(2,0,0), (3,0,0), (2,1,0)]
  }
}`;
    const meshes = parseUSDA(usda);
    expect(meshes.length).toBe(2);
    expect(meshes[0].name).toBe('Mesh1');
    expect(meshes[1].name).toBe('Mesh2');
  });

  it('triangulates quad faces', () => {
    const usda = `#usda 1.0
def Mesh "Quad" {
  int[] faceVertexCounts = [4]
  int[] faceVertexIndices = [0, 1, 2, 3]
  point3f[] points = [(0,0,0), (1,0,0), (1,1,0), (0,1,0)]
}`;
    const meshes = parseUSDA(usda);
    expect(meshes.length).toBe(1);
    // 4-sided face -> 2 triangles -> 6 indices
    expect(meshes[0].faceVertexIndices.length).toBe(6);
    expect(meshes[0].faceVertexIndices).toEqual([0, 1, 2, 0, 2, 3]);
  });

  it('parses normals and UVs', () => {
    const usda = `#usda 1.0
def Mesh "MeshWithAttrs" {
  int[] faceVertexCounts = [3]
  int[] faceVertexIndices = [0, 1, 2]
  point3f[] points = [(0,0,0), (1,0,0), (0,1,0)]
  normal3f[] normals = [(0,0,1), (0,0,1), (0,0,1)]
  texCoord2f[] primvars:st = [(0,0), (1,0), (0,1)]
}`;
    const meshes = parseUSDA(usda);
    expect(meshes[0].normals).toBeDefined();
    expect(meshes[0].normals!.length).toBe(9);
    expect(meshes[0].uvs).toBeDefined();
    expect(meshes[0].uvs!.length).toBe(6);
  });

  it('parses the test fixture cube.usda', () => {
    const text = readFileSync(resolve(__dirname, '../fixtures/cube.usda'), 'utf-8');
    const meshes = parseUSDA(text);
    expect(meshes.length).toBe(1);
    expect(meshes[0].name).toBe('Cube');
    expect(meshes[0].points.length).toBe(24); // 8 vertices * 3
    expect(meshes[0].faceVertexIndices.length).toBe(36); // 12 triangles * 3
  });

  it('handles mixed face counts', () => {
    const usda = `#usda 1.0
def Mesh "Mixed" {
  int[] faceVertexCounts = [3, 4, 3]
  int[] faceVertexIndices = [0,1,2, 3,4,5,6, 7,8,9]
  point3f[] points = [(0,0,0),(1,0,0),(0,1,0),(2,0,0),(3,0,0),(3,1,0),(2,1,0),(4,0,0),(5,0,0),(4,1,0)]
}`;
    const meshes = parseUSDA(usda);
    // tri(3) + quad(4->6) + tri(3) = 12 indices
    expect(meshes[0].faceVertexIndices.length).toBe(12);
  });

  it('handles negative coordinates', () => {
    const usda = `#usda 1.0
def Mesh "Neg" {
  int[] faceVertexCounts = [3]
  int[] faceVertexIndices = [0, 1, 2]
  point3f[] points = [(-1,-2,-3), (1.5,0,0), (0,1.5,0)]
}`;
    const meshes = parseUSDA(usda);
    expect(meshes[0].points[0]).toBe(-1);
    expect(meshes[0].points[1]).toBe(-2);
    expect(meshes[0].points[2]).toBe(-3);
    expect(meshes[0].points[3]).toBe(1.5);
  });
});

describe('USDA Export', () => {
  it('round-trips through parser', () => {
    // Create a cube, export to USDA, re-parse
    const h = core.loadMeshFromOBJ('v 0 0 0\nv 1 0 0\nv 0 1 0\nf 1 2 3\n');
    const vCount = core.getVertexCount(h);
    const fCount = core.getFaceCount(h);

    // Manual USDA export for testing (since we can't use async import in sync test)
    const verts = new Float32Array(core.getVertexBuffer(h));
    const indices = new Uint32Array(core.getIndexBuffer(h));

    let usda = '#usda 1.0\n(\n    defaultPrim = "Root"\n)\n\n';
    usda += 'def Xform "Root"\n{\n    def Mesh "Mesh"\n    {\n';
    usda += '        int[] faceVertexCounts = [' + Array(indices.length / 3).fill(3).join(', ') + ']\n';
    usda += '        int[] faceVertexIndices = [' + Array.from(indices).join(', ') + ']\n';
    usda += '        point3f[] points = [';
    const pts: string[] = [];
    for (let i = 0; i < verts.length; i += 3) pts.push(`(${verts[i]}, ${verts[i+1]}, ${verts[i+2]})`);
    usda += pts.join(', ') + ']\n';
    usda += '    }\n}\n';

    // Re-parse
    const meshes = parseUSDA(usda);
    expect(meshes.length).toBe(1);
    expect(meshes[0].points.length / 3).toBe(vCount);
    expect(meshes[0].faceVertexIndices.length / 3).toBe(fCount);

    core.deleteMesh(h);
  });
});
