import { test, expect } from "bun:test"
import { draw, type DrawCommand } from "./draw"
import { renderGridToPng } from "./render"
import { mkdirSync, existsSync } from "fs"

const OUTPUT_DIR = `${import.meta.dir}/test-output`

if (!existsSync(OUTPUT_DIR)) {
  mkdirSync(OUTPUT_DIR, { recursive: true })
}

const saveTestPng = async (name: string, commands: DrawCommand[]) => {
  const grid = draw(commands)
  const png = await renderGridToPng(grid)
  await Bun.write(`${OUTPUT_DIR}/${name}.png`, png)
  return grid
}

const ROWS = 6
const COLS = 22

// Helper to safely access grid cells (we know grid is always 6x22)
const at = (grid: number[][], r: number, c: number) => grid[r]![c]!
const rowAt = (grid: number[][], r: number) => grid[r]!

test("fill - solid color", async () => {
  const grid = await saveTestPng("fill-solid-blue", [{ cmd: "fill", color: "blue" }])
  expect(grid.length).toBe(ROWS)
  expect(rowAt(grid, 0).length).toBe(COLS)
  expect(grid.every((r) => r.every((c) => c === 67))).toBe(true)
})

test("rect - simple rectangle", async () => {
  const grid = await saveTestPng("rect-simple", [
    { cmd: "fill", color: "black" },
    { cmd: "rect", x: 2, y: 1, w: 5, h: 3, color: "red" },
  ])
  expect(at(grid, 1, 2)).toBe(63) // red
  expect(at(grid, 2, 4)).toBe(63) // red
  expect(at(grid, 0, 0)).toBe(70) // black
})

test("rect - full width row", async () => {
  const grid = await saveTestPng("rect-full-row", [
    { cmd: "fill", color: "white" },
    { cmd: "rect", x: 0, y: 0, w: 22, h: 1, color: "orange" },
    { cmd: "rect", x: 0, y: 5, w: 22, h: 1, color: "orange" },
  ])
  expect(rowAt(grid, 0).every((c) => c === 64)).toBe(true)
  expect(rowAt(grid, 5).every((c) => c === 64)).toBe(true)
  expect(rowAt(grid, 2).every((c) => c === 69)).toBe(true)
})

test("text - centered (default)", async () => {
  const grid = await saveTestPng("text-centered", [
    { cmd: "fill", color: "black" },
    { cmd: "text", content: "HELLO", row: 2 },
  ])
  // "HELLO" is 5 chars, centered in 22 = starts at position 8
  expect(at(grid, 2, 8)).toBe(8)   // H
  expect(at(grid, 2, 9)).toBe(5)   // E
  expect(at(grid, 2, 10)).toBe(12) // L
  expect(at(grid, 2, 11)).toBe(12) // L
  expect(at(grid, 2, 12)).toBe(15) // O
})

test("text - left aligned", async () => {
  const grid = await saveTestPng("text-left", [
    { cmd: "fill", color: "black" },
    { cmd: "text", content: "HI", row: 0, align: "left" },
  ])
  expect(at(grid, 0, 0)).toBe(8) // H
  expect(at(grid, 0, 1)).toBe(9) // I
})

test("text - right aligned", async () => {
  const grid = await saveTestPng("text-right", [
    { cmd: "fill", color: "black" },
    { cmd: "text", content: "HI", row: 0, align: "right" },
  ])
  expect(at(grid, 0, 20)).toBe(8) // H
  expect(at(grid, 0, 21)).toBe(9) // I
})

test("text - with startCol/endCol bounds", async () => {
  const grid = await saveTestPng("text-bounded", [
    { cmd: "fill", color: "black" },
    { cmd: "text", content: "HI", row: 2, startCol: 5, endCol: 16, align: "center" },
  ])
  // Width is 12 (5-16 inclusive), "HI" is 2 chars, centered = starts at col 10
  expect(at(grid, 2, 10)).toBe(8) // H
  expect(at(grid, 2, 11)).toBe(9) // I
})

test("text - left aligned with bounds", async () => {
  const grid = await saveTestPng("text-bounded-left", [
    { cmd: "fill", color: "black" },
    { cmd: "text", content: "HI", row: 2, startCol: 4, endCol: 17, align: "left" },
  ])
  expect(at(grid, 2, 4)).toBe(8) // H
  expect(at(grid, 2, 5)).toBe(9) // I
})

test("text - right aligned with bounds", async () => {
  const grid = await saveTestPng("text-bounded-right", [
    { cmd: "fill", color: "black" },
    { cmd: "text", content: "HI", row: 2, startCol: 4, endCol: 17, align: "right" },
  ])
  expect(at(grid, 2, 16)).toBe(8) // H
  expect(at(grid, 2, 17)).toBe(9) // I
})

test("text_block - word wrap", async () => {
  const grid = await saveTestPng("text-block-wrap", [
    { cmd: "fill", color: "black" },
    { cmd: "text_block", content: "HAPPY BIRTHDAY BEATRICE", startRow: 1 },
  ])
  expect(grid.length).toBe(ROWS)
})

test("text_block - with column bounds", async () => {
  const grid = await saveTestPng("text-block-bounded", [
    { cmd: "fill", color: "black" },
    { cmd: "text_block", content: "THE QUICK BROWN FOX JUMPS", startRow: 0, startCol: 2, endCol: 19 },
  ])
  // First and last 2 cols should remain black
  expect(at(grid, 0, 0)).toBe(70)
  expect(at(grid, 0, 1)).toBe(70)
  expect(at(grid, 0, 20)).toBe(70)
  expect(at(grid, 0, 21)).toBe(70)
})

test("text_block - with row bounds", async () => {
  const grid = await saveTestPng("text-block-row-bounded", [
    { cmd: "fill", color: "black" },
    { cmd: "text_block", content: "A B C D E F G H I J K L M", startRow: 2, endRow: 4 },
  ])
  // Should only use rows 2-4
  expect(at(grid, 0, 0)).toBe(70) // black, untouched
  expect(at(grid, 1, 0)).toBe(70) // black, untouched
  expect(at(grid, 5, 0)).toBe(70) // black, untouched
})

test("text_block - overflow ellipsis", async () => {
  const grid = await saveTestPng("text-block-overflow", [
    { cmd: "fill", color: "black" },
    { cmd: "text_block", content: "THIS IS A VERY LONG MESSAGE THAT WILL NOT FIT", startRow: 2, endRow: 3, overflow: "ellipsis" },
  ])
  expect(grid.length).toBe(ROWS)
})

test("border - all sides", async () => {
  const grid = await saveTestPng("border-all", [
    { cmd: "fill", color: "black" },
    { cmd: "border", color: "blue" },
  ])
  expect(rowAt(grid, 0).every((c) => c === 67)).toBe(true)
  expect(rowAt(grid, 5).every((c) => c === 67)).toBe(true)
  expect(at(grid, 2, 0)).toBe(67)
  expect(at(grid, 2, 21)).toBe(67)
  expect(at(grid, 2, 10)).toBe(70)
})

test("border - top and bottom only", async () => {
  const grid = await saveTestPng("border-top-bottom", [
    { cmd: "fill", color: "black" },
    { cmd: "border", color: "orange", sides: ["top", "bottom"] },
  ])
  expect(rowAt(grid, 0).every((c) => c === 64)).toBe(true)
  expect(rowAt(grid, 5).every((c) => c === 64)).toBe(true)
  expect(at(grid, 2, 0)).toBe(70)
  expect(at(grid, 2, 21)).toBe(70)
})

test("gradient - horizontal", async () => {
  const grid = await saveTestPng("gradient-horizontal", [
    { cmd: "gradient", direction: "horizontal", colors: ["purple", "blue", "green", "yellow", "orange", "red"] },
  ])
  expect(at(grid, 0, 0)).toBe(68)  // purple
  expect(at(grid, 0, 21)).toBe(63) // red
})

test("gradient - vertical", async () => {
  const grid = await saveTestPng("gradient-vertical", [
    { cmd: "gradient", direction: "vertical", colors: ["blue", "green", "yellow"] },
  ])
  expect(at(grid, 0, 10)).toBe(67) // blue
  expect(at(grid, 5, 10)).toBe(65) // yellow
})

test("circle - small", async () => {
  const grid = await saveTestPng("circle-small", [
    { cmd: "fill", color: "white" },
    { cmd: "circle", cx: 11, cy: 3, r: 2, color: "red" },
  ])
  expect(at(grid, 3, 11)).toBe(63) // red center
})

test("line - horizontal", async () => {
  const grid = await saveTestPng("line-horizontal", [
    { cmd: "fill", color: "black" },
    { cmd: "line", x1: 2, y1: 3, x2: 19, y2: 3, color: "yellow" },
  ])
  expect(at(grid, 3, 2)).toBe(65)
  expect(at(grid, 3, 10)).toBe(65)
  expect(at(grid, 3, 19)).toBe(65)
})

test("line - diagonal", async () => {
  const grid = await saveTestPng("line-diagonal", [
    { cmd: "fill", color: "black" },
    { cmd: "line", x1: 0, y1: 0, x2: 21, y2: 5, color: "green" },
  ])
  expect(at(grid, 0, 0)).toBe(66)
  expect(at(grid, 5, 21)).toBe(66)
})

test("combined - birthday message", async () => {
  await saveTestPng("combined-birthday", [
    { cmd: "fill", color: "black" },
    { cmd: "border", color: "orange", sides: ["top", "bottom"] },
    { cmd: "text_block", content: "HAPPY BIRTHDAY BEATRICE", startRow: 2, endRow: 3 },
  ])
})

test("combined - quote with accent", async () => {
  await saveTestPng("combined-quote", [
    { cmd: "fill", color: "black" },
    { cmd: "text_block", content: "KINDLE A LIGHT OF MEANING IN THE DARKNESS", startRow: 0, endRow: 3, startCol: 2, endCol: 19 },
    { cmd: "text", content: "-CARL JUNG", row: 5 },
  ])
})

test("combined - alert with border", async () => {
  await saveTestPng("combined-alert", [
    { cmd: "fill", color: "black" },
    { cmd: "border", color: "red" },
    { cmd: "text", content: "ALERT", row: 2 },
    { cmd: "text", content: "MEETING AT 6PM", row: 3 },
  ])
})

test("combined - asymmetric layout", async () => {
  await saveTestPng("combined-asymmetric", [
    { cmd: "fill", color: "black" },
    { cmd: "rect", x: 0, y: 0, w: 4, h: 6, color: "blue" },
    { cmd: "text_block", content: "QUOTE GOES HERE ON THE RIGHT SIDE", startCol: 5, endCol: 21, startRow: 1, endRow: 4, align: "left" },
  ])
})

test("layered - shapes overlap correctly", async () => {
  await saveTestPng("layered-shapes", [
    { cmd: "fill", color: "white" },
    { cmd: "rect", x: 0, y: 0, w: 22, h: 3, color: "blue" },
    { cmd: "rect", x: 5, y: 1, w: 12, h: 4, color: "yellow" },
    { cmd: "circle", cx: 11, cy: 3, r: 2, color: "red" },
  ])
})
