import assert from "node:assert/strict";
import {
  BLUR_AMOUNT_MAX,
  INVALID_LAYOUT_TEMPLATE_MESSAGE,
  blurToBoxblur,
  boxblurFilterSegment,
  buildArtTrackFilterComplex,
  buildClassicBlurFillFilterComplex,
  clampBlurAmount,
  clampTextPadding,
  clampTitleArtistGap,
  computeLayoutGeometry,
  normalizeLayoutSettings,
  requiresArtTrackLayoutEntitlement,
} from "../lib/layout";
import { sanitizeDrawtext } from "../lib/watermark";

function testEnumsAndClamp() {
  assert.equal(clampBlurAmount(150), BLUR_AMOUNT_MAX);
  assert.equal(clampBlurAmount(-1), 0);
  assert.equal(clampTextPadding(999), 120);
  assert.equal(clampTextPadding(1), 16);
  assert.equal(clampTitleArtistGap(100), 64);
  assert.equal(clampTitleArtistGap(-5), 0);

  assert.equal(blurToBoxblur(0), null);
  const mid = blurToBoxblur(50);
  assert.ok(mid && mid.radius >= 1 && mid.power >= 1);
  assert.ok(boxblurFilterSegment(60).includes("gblur="));
  assert.equal(boxblurFilterSegment(0), "");
}

function testNormalize() {
  const classic = normalizeLayoutSettings(null);
  assert.equal(classic.template, null);
  assert.equal(classic.blurFill, false);
  assert.equal(classic.titleArtistGap, 10);
  assert.equal(classic.textOffsetX, 0);
  assert.equal(classic.titleBold, true);

  assert.equal(
    normalizeLayoutSettings({ blur_fill: true }).blurFill,
    true,
  );
  assert.equal(
    normalizeLayoutSettings({
      template: "CENTERED_COMPACT",
      blurFill: true,
    }).blurFill,
    false,
  );

  assert.equal(
    normalizeLayoutSettings({
      layout_template: "CENTERED_COMPACT",
      blur_amount: 80,
      title_artist_gap: 24,
      text_offset_y: -20,
    }).titleArtistGap,
    24,
  );

  assert.throws(
    () => normalizeLayoutSettings({ template: "NOT_A_TEMPLATE" }),
    (err: Error) => err.message === INVALID_LAYOUT_TEMPLATE_MESSAGE,
  );
  assert.throws(
    () => normalizeLayoutSettings({ template: "COVER_LEFT_TEXT_RIGHT", x: 10 }),
    (err: Error) => err.message === INVALID_LAYOUT_TEMPLATE_MESSAGE,
  );

  assert.equal(
    requiresArtTrackLayoutEntitlement(normalizeLayoutSettings({ template: null })),
    false,
  );
  assert.equal(
    requiresArtTrackLayoutEntitlement(
      normalizeLayoutSettings({ template: "COVER_TOP_TEXT_BOTTOM" }),
    ),
    true,
  );
}

function testGeometryAndFilter() {
  const geo = computeLayoutGeometry("COVER_TOP_TEXT_BOTTOM", 1920, 1080, 48, 16, 10, -5);
  assert.ok(geo.coverMaxW > 1600, "top layout cover should be near full width");
  assert.ok(geo.artistY !== geo.titleY);

  const withGap = computeLayoutGeometry("COVER_LEFT_TEXT_RIGHT", 1280, 720, 40, 40, 0, 0);
  const tight = computeLayoutGeometry("COVER_LEFT_TEXT_RIGHT", 1280, 720, 40, 0, 0, 0);
  assert.ok(Number(withGap.artistY) - Number(withGap.titleY) > Number(tight.artistY) - Number(tight.titleY));

  const lowerLeft = computeLayoutGeometry("LOWER_LEFT_COVER_TEXT", 1280, 720, 48, 10, 0, 0);
  assert.equal(lowerLeft.coverX, "48");
  assert.equal(lowerLeft.coverY, "H-h-48");
  assert.ok(Number(lowerLeft.titleX) > Number(lowerLeft.coverX));
  assert.ok(Number(lowerLeft.titleY) > 300, "title should sit in the lower half with the cover");

  const lowerRight = computeLayoutGeometry("LOWER_RIGHT_COVER_TEXT", 1280, 720, 48, 10, 0, 0);
  assert.equal(lowerRight.coverX, "W-w-48");
  assert.equal(lowerRight.coverY, "H-h-48");
  assert.ok(String(lowerRight.titleX).startsWith("W-text_w-"));

  const fc = buildArtTrackFilterComplex({
    width: 1280,
    height: 720,
    layout: {
      template: "CENTERED_COMPACT",
      blurFill: false,
      blurAmount: 40,
      blurOpacity: 70,
      textPadding: 40,
      titleArtistGap: 18,
      textOffsetX: 5,
      textOffsetY: -8,
      titleBold: true,
    },
    titleEscaped: sanitizeDrawtext("Hello:World"),
    artistEscaped: sanitizeDrawtext("Artist"),
    fontfileEscaped: null,
    titleFontfileEscaped: "C\\:/fonts/Arimo-Bold.ttf",
  });
  assert.ok(fc.includes("split=2"));
  assert.ok(fc.includes("blend=") || fc.includes("[blurred]"));
  assert.ok(fc.includes("overlay="));
  assert.ok(fc.includes("drawtext="));
  assert.ok(fc.includes("fontfile='C\\:/fonts/Arimo-Bold.ttf'"));
  assert.ok(fc.endsWith("[laid]"));

  const regular = buildArtTrackFilterComplex({
    width: 1280,
    height: 720,
    layout: {
      template: "CENTERED_COMPACT",
      blurFill: false,
      blurAmount: 40,
      blurOpacity: 70,
      textPadding: 40,
      titleArtistGap: 18,
      textOffsetX: 0,
      textOffsetY: 0,
      titleBold: false,
    },
    titleEscaped: "Hi",
    artistEscaped: null,
    fontfileEscaped: "C\\:/fonts/Arimo-Regular.ttf",
  });
  assert.ok(regular.includes("fontfile='C\\:/fonts/Arimo-Regular.ttf'"));
  assert.equal(regular.includes("borderw="), false);

  const withSeparateBg = buildArtTrackFilterComplex({
    width: 1280,
    height: 720,
    layout: {
      template: "LOWER_LEFT_COVER_TEXT",
      blurFill: false,
      blurAmount: 40,
      blurOpacity: 100,
      textPadding: 48,
      titleArtistGap: 10,
      textOffsetX: 0,
      textOffsetY: 0,
      titleBold: true,
    },
    titleEscaped: "Song",
    artistEscaped: "Artist",
    separateBackgroundInputIndex: 2,
  });
  assert.equal(withSeparateBg.includes("[0:v]split=2"), false);
  assert.ok(withSeparateBg.includes("[2:v]"));
  assert.ok(withSeparateBg.includes("[0:v]scale="));
  assert.ok(withSeparateBg.includes("overlay="));

  const withoutSeparateBg = buildArtTrackFilterComplex({
    width: 1280,
    height: 720,
    layout: {
      template: "LOWER_LEFT_COVER_TEXT",
      blurFill: false,
      blurAmount: 40,
      blurOpacity: 100,
      textPadding: 48,
      titleArtistGap: 10,
      textOffsetX: 0,
      textOffsetY: 0,
      titleBold: true,
    },
    titleEscaped: "Song",
    artistEscaped: null,
  });
  assert.ok(withoutSeparateBg.includes("[0:v]split=2"));

  const classicBlur = buildClassicBlurFillFilterComplex({
    width: 1280,
    height: 720,
    blurAmount: 55,
    blurOpacity: 100,
  });
  assert.ok(classicBlur.includes("[0:v]split=2"));
  assert.ok(classicBlur.includes("gblur=") || classicBlur.includes("[blurred]"));
  assert.ok(classicBlur.includes("overlay=(W-w)/2:(H-h)/2"));
  assert.equal(classicBlur.includes("pad="), false);
  assert.equal(classicBlur.includes("drawtext="), false);
  assert.ok(classicBlur.endsWith("[laid]"));

  const classicOpacity = buildClassicBlurFillFilterComplex({
    width: 1280,
    height: 720,
    blurAmount: 40,
    blurOpacity: 50,
  });
  assert.ok(classicOpacity.includes("d=86400"));
  assert.equal(classicOpacity.includes("shortest=1"), false);
  assert.ok(classicOpacity.includes("blend="));
}

testEnumsAndClamp();
testNormalize();
testGeometryAndFilter();
console.log("layout.test.ts: ok");
