import { describe, expect, it } from "vitest";
import { assembleCharacter } from "../creator/assemble.js";
import { CHARACTER_PRESETS } from "../creator/presets.js";
import { vertexCount } from "../mesh/MeshBuilder.js";

/**
 * Weight defects are invisible in bind pose — they only show once a clip runs.
 * These assert the two that produced visible tearing: the far leg claiming a
 * share of this one (dragging heels across during a stride) and the shin
 * claiming half of every heel vertex (shearing the boot as the ankle pitches).
 */

const survivor = () => assembleCharacter(CHARACTER_PRESETS.find((p) => p.id === "survivor")!);

describe("skin weights", () => {
  it("keeps every vertex fully weighted", () => {
    const { mesh } = survivor();
    for (let v = 0; v < vertexCount(mesh); v++) {
      let sum = 0;
      for (let i = 0; i < 4; i++) sum += mesh.weights[v * 4 + i]!;
      expect(sum).toBeCloseTo(1, 5);
    }
  });

  it("never lets one leg influence geometry on the other side", () => {
    const { mesh, skeleton } = survivor();
    const name = (i: number) => skeleton.joints[i]!.name;
    let offenders = 0;

    for (let v = 0; v < vertexCount(mesh); v++) {
      const x = mesh.positions[v * 3]!;
      // Only judge geometry clearly lateralised — the pelvis straddles both.
      if (Math.abs(x) < skeleton.height * 0.03) continue;
      const wantSide = x > 0 ? "Left" : "Right";
      for (let i = 0; i < 4; i++) {
        const weight = mesh.weights[v * 4 + i]!;
        if (weight < 0.02) continue;
        const bone = name(mesh.joints[v * 4 + i]!);
        if (!bone.startsWith("Left") && !bone.startsWith("Right")) continue;
        if (!bone.startsWith(wantSide)) offenders++;
      }
    }
    expect(offenders).toBe(0);
  });

  it("gives the heel to the foot, not the shin", () => {
    const { mesh, skeleton } = survivor();
    const h = skeleton.height;
    const name = (i: number) => skeleton.joints[i]!.name;
    let heels = 0;

    for (let v = 0; v < vertexCount(mesh); v++) {
      const y = mesh.positions[v * 3 + 1]!;
      const z = mesh.positions[v * 3 + 2]!;
      // Behind the ankle and below it: heel and the back of the sole.
      if (y > h * 0.05 || z > -h * 0.02) continue;
      heels++;

      let foot = 0;
      let shin = 0;
      for (let i = 0; i < 4; i++) {
        const weight = mesh.weights[v * 4 + i]!;
        const bone = name(mesh.joints[v * 4 + i]!);
        if (bone.endsWith("Foot") || bone.endsWith("ToeBase")) foot += weight;
        else if (bone.endsWith("Leg") && !bone.endsWith("UpLeg")) shin += weight;
      }
      expect(foot).toBeGreaterThan(shin);
    }
    expect(heels).toBeGreaterThan(10);
  });
});
