/**
 * SVG hotspot regions over the house illustration.
 *
 * Coordinates are FRACTIONAL (0..1) of the SVG viewBox so they scale with
 * any viewport. The viewBox aspect ratio matches the source image
 * (1536 x 1024, ratio 3:2). To remap polygons after replacing the image,
 * regenerate from this file.
 *
 * The wall bricks are generated programmatically by `gridBricks()`.
 * Windows, doors, columns, and roof tiles are hand-placed.
 *
 * Each Region has:
 *   - id: globally unique (used as Donation.regionId)
 *   - tier: which TierId the region belongs to (price + label come from TIERS)
 *   - polygon: array of [x, y] points in 0..1 fractional coordinates
 */

import type { TierId } from "./tiers";

export interface Region {
  id: string;
  tier: TierId;
  polygon: [number, number][];
}

/* ---------- helpers ---------- */

function rect(
  x: number,
  y: number,
  w: number,
  h: number,
): [number, number][] {
  return [
    [x, y],
    [x + w, y],
    [x + w, y + h],
    [x, y + h],
  ];
}

/**
 * Build a row of brick polygons within [x0..x1] horizontally and
 * [y0..y1] vertically, evenly subdivided into `count` columns.
 * Optional offset shifts every other row to mimic running bond.
 */
function brickRow(opts: {
  rowId: string;
  tier: TierId;
  x0: number;
  x1: number;
  y0: number;
  y1: number;
  count: number;
  offset?: number; // fractional offset of the row (0..1 of brick width)
}): Region[] {
  const { rowId, tier, x0, x1, y0, y1, count, offset = 0 } = opts;
  const out: Region[] = [];
  const totalW = x1 - x0;
  const bw = totalW / count;
  const shift = offset * bw;
  for (let i = 0; i < count; i++) {
    const bx0 = Math.max(x0, x0 + i * bw - shift);
    const bx1 = Math.min(x1, x0 + (i + 1) * bw - shift);
    if (bx1 - bx0 < bw * 0.25) continue; // skip ultra-thin slivers at edges
    out.push({
      id: `${rowId}-b${i}`,
      tier,
      polygon: rect(bx0, y0, bx1 - bx0, y1 - y0),
    });
  }
  return out;
}

/* ---------- house geometry (fractional coords from house.png) ---------- */

// Foundation stone band (light grey stone strip below brick walls).
// In the source image, the visible stone runs from y ~0.78 to y ~0.86,
// horizontally x ~0.13 to ~0.87 (with the porch steps interrupting in front).
const FOUNDATION_Y0 = 0.785;
const FOUNDATION_Y1 = 0.860;
// Steps occupy roughly x 0.45 to 0.55 in front; we still want stones
// behind, so we go full width.

// Brick wall band (left-of-porch + right-of-porch on ground floor;
// full width above the porch roof on upper floor).
// Upper floor wall: y 0.20 to 0.39
// Ground floor wall (visible left/right of porch): y 0.46 to 0.71
// Center under porch roof is hidden by porch shadow / door area.

// Upper-floor wall stretches roughly x 0.185 to 0.855 (between roof eaves)
// y from just below the main roof eave (~0.215) down to the porch roof (~0.385).
const UPPER_WALL_X0 = 0.190;
const UPPER_WALL_X1 = 0.855;
const UPPER_WALL_Y0 = 0.215;
const UPPER_WALL_Y1 = 0.380;

// Upper-floor windows (3). Hand-measured fractions from the image.
// Each window is a tall sash with a cream frame; treated as one polygon.
const UPPER_WINDOWS = [
  { x0: 0.255, y0: 0.230, x1: 0.330, y1: 0.345 },
  { x0: 0.475, y0: 0.230, x1: 0.555, y1: 0.345 },
  { x0: 0.690, y0: 0.230, x1: 0.770, y1: 0.345 },
];

// Ground-floor walls show left & right of porch only.
// Left strip: x 0.185 - 0.225 (column frame edge) y 0.43 - 0.71
// Wide left wall: x 0.185 - 0.43 (with window cut)
// Wide right wall: x 0.59 - 0.84 (with window cut)
// Ground floor windows: left x 0.245-0.430, y 0.490-0.665
//                       right x 0.595-0.775, y 0.490-0.665
// Ground-floor windows: paired sashes inside a deep cream frame.
// Hand-measured: y ~0.51 to 0.69; left x 0.235-0.420, right x 0.605-0.785.
const GROUND_WINDOWS = [
  { x0: 0.235, y0: 0.515, x1: 0.420, y1: 0.685 },
  { x0: 0.605, y0: 0.515, x1: 0.785, y1: 0.685 },
];

// Door + sidelights. The green front door spans x 0.475-0.535 roughly,
// the cream sidelights flank it. y from porch-deck top to porch threshold.
const DOOR = { x0: 0.475, y0: 0.500, x1: 0.535, y1: 0.745 };
const SIDELIGHT_LEFT = { x0: 0.448, y0: 0.520, x1: 0.475, y1: 0.745 };
const SIDELIGHT_RIGHT = { x0: 0.535, y0: 0.520, x1: 0.562, y1: 0.745 };

// Porch columns (2): the cream structural columns flanking the porch.
// Outer column at x ~0.165-0.200, inner column at x ~0.435-0.460 ... but
// per the spec we expose only TWO porch columns; the front-most/visible.
// Left (outer): x 0.155-0.205, y 0.395-0.770
// Right (outer): x 0.790-0.840, y 0.395-0.770
const COLUMNS = [
  { x0: 0.155, y0: 0.395, x1: 0.205, y1: 0.770 },
  { x0: 0.790, y0: 0.395, x1: 0.840, y1: 0.770 },
];

// Porch threshold / decking band: the cream-colored strip between the
// brick courses and the steps, just below the door. Narrow band.
// y 0.745 to 0.785 across x 0.205 to 0.790 split into boards.
const PORCH_DECK = { x0: 0.205, y0: 0.745, x1: 0.790, y1: 0.785 };
const PORCH_BOARD_COUNT = 5;

// Main roof: the large dark shingled hip/gable. y ~ 0.075 to 0.215,
// x ~ 0.135 to 0.870. 6 horizontal strips x 5 segments = 30 shingle regions.
// (Cells overlapping the central cream gable peak triangle are skipped.)
const MAIN_ROOF = { x0: 0.135, y0: 0.075, x1: 0.870, y1: 0.215 };
const MAIN_ROOF_ROWS = 6;
const MAIN_ROOF_COLS = 5;

// Porch roof: the smaller shingled roof over the porch.
// y ~0.385 to 0.460; spans x ~0.135 to 0.870 (slightly wider than the wall).
const PORCH_ROOF = { x0: 0.150, y0: 0.385, x1: 0.860, y1: 0.460 };
const PORCH_ROOF_ROWS = 3;
const PORCH_ROOF_COLS = 5;

// Gable peak triangle (cream-shingled triangular pediment at top center).
// Apex at (0.500, 0.075); base across (0.395, 0.205) to (0.605, 0.205).
const GABLE_PEAK = {
  apex: [0.500, 0.075] as [number, number],
  baseLeft: [0.395, 0.205] as [number, number],
  baseRight: [0.605, 0.205] as [number, number],
};
const GABLE_PEAK_ROWS = 5;

/* ---------- region builder ---------- */

function buildRegions(): Region[] {
  const regions: Region[] = [];

  /* Foundation stones (~30 across) */
  const FOUNDATION_COUNT = 28;
  regions.push(
    ...brickRow({
      rowId: "foundation",
      tier: "foundation",
      x0: 0.130,
      x1: 0.870,
      y0: FOUNDATION_Y0,
      y1: FOUNDATION_Y1,
      count: FOUNDATION_COUNT,
    }),
  );

  /* Upper-floor wall: 6 brick rows.
     Row 1 = lowest (just above porch roof): tier wall-row5
     Row 2: wall-row5
     Row 3: wall-row6
     Row 4: wall-row6
     Row 5 (upper): wall-row7 ... we want progression but most upper bricks
     should still be expensive. Mapping: lowest visible upper wall row
     starts at row 5 of the price ladder per spec. */
  const upperRows = 6;
  const rowHeight = (UPPER_WALL_Y1 - UPPER_WALL_Y0) / upperRows;
  const upperTiers: TierId[] = [
    "wall-row5",
    "wall-row5",
    "wall-row6",
    "wall-row6",
    "wall-row7",
    "wall-row7",
  ];
  for (let r = 0; r < upperRows; r++) {
    const y0 = UPPER_WALL_Y1 - (r + 1) * rowHeight;
    const y1 = UPPER_WALL_Y1 - r * rowHeight;
    // Build full row, then we'll filter out bricks intersecting upper windows.
    const row = brickRow({
      rowId: `upper-r${r + 1}`,
      tier: upperTiers[r],
      x0: UPPER_WALL_X0,
      x1: UPPER_WALL_X1,
      y0,
      y1,
      count: 18,
      offset: r % 2 === 0 ? 0 : 0.5,
    });
    for (const b of row) {
      const [bx0, by0] = b.polygon[0];
      const [bx1, by1] = b.polygon[2];
      const cx = (bx0 + bx1) / 2;
      const cy = (by0 + by1) / 2;
      const blockedByWindow = UPPER_WINDOWS.some(
        (w) => cx >= w.x0 && cx <= w.x1 && cy >= w.y0 && cy <= w.y1,
      );
      if (!blockedByWindow) regions.push(b);
    }
  }

  /* Ground-floor wall (left of porch + right of porch).
     Visible vertical band: y 0.460 to 0.780 (above foundation, below porch eave).
     Split into 4 rows. Tier ladder rows 1..4 (low to high). */
  const groundRows = 4;
  const groundY0 = 0.460;
  const groundY1 = 0.780;
  const groundRowH = (groundY1 - groundY0) / groundRows;
  const groundTiers: TierId[] = [
    "wall-row1",
    "wall-row2",
    "wall-row3",
    "wall-row4",
  ];
  // Left segment: x 0.210 - 0.448 (between column and door sidelight)
  const leftSeg = { x0: 0.210, x1: 0.448 };
  // Right segment: x 0.562 - 0.790
  const rightSeg = { x0: 0.562, x1: 0.790 };
  for (let r = 0; r < groundRows; r++) {
    const y0 = groundY1 - (r + 1) * groundRowH;
    const y1 = groundY1 - r * groundRowH;

    for (const seg of [
      { id: "L", ...leftSeg },
      { id: "R", ...rightSeg },
    ]) {
      const row = brickRow({
        rowId: `ground-${seg.id}-r${r + 1}`,
        tier: groundTiers[r],
        x0: seg.x0,
        x1: seg.x1,
        y0,
        y1,
        count: 6,
        offset: r % 2 === 0 ? 0 : 0.5,
      });
      // Filter out bricks whose CENTER falls inside a window opening.
      for (const b of row) {
        const [bx0, by0] = b.polygon[0];
        const [bx1, by1] = b.polygon[2];
        const cx = (bx0 + bx1) / 2;
        const cy = (by0 + by1) / 2;
        const blocked = GROUND_WINDOWS.some(
          (w) => cx >= w.x0 && cx <= w.x1 && cy >= w.y0 && cy <= w.y1,
        );
        if (!blocked) regions.push(b);
      }
    }
  }

  /* Upper-floor windows */
  UPPER_WINDOWS.forEach((w, i) => {
    regions.push({
      id: `window-upper-${i + 1}`,
      tier: "window-upper",
      polygon: rect(w.x0, w.y0, w.x1 - w.x0, w.y1 - w.y0),
    });
  });

  /* Ground-floor windows */
  GROUND_WINDOWS.forEach((w, i) => {
    regions.push({
      id: `window-ground-${i + 1}`,
      tier: "window-ground",
      polygon: rect(w.x0, w.y0, w.x1 - w.x0, w.y1 - w.y0),
    });
  });

  /* Door */
  regions.push({
    id: "door-front",
    tier: "door",
    polygon: rect(DOOR.x0, DOOR.y0, DOOR.x1 - DOOR.x0, DOOR.y1 - DOOR.y0),
  });

  /* Sidelights */
  regions.push({
    id: "sidelight-left",
    tier: "door-sidelight",
    polygon: rect(
      SIDELIGHT_LEFT.x0,
      SIDELIGHT_LEFT.y0,
      SIDELIGHT_LEFT.x1 - SIDELIGHT_LEFT.x0,
      SIDELIGHT_LEFT.y1 - SIDELIGHT_LEFT.y0,
    ),
  });
  regions.push({
    id: "sidelight-right",
    tier: "door-sidelight",
    polygon: rect(
      SIDELIGHT_RIGHT.x0,
      SIDELIGHT_RIGHT.y0,
      SIDELIGHT_RIGHT.x1 - SIDELIGHT_RIGHT.x0,
      SIDELIGHT_RIGHT.y1 - SIDELIGHT_RIGHT.y0,
    ),
  });

  /* Porch columns */
  COLUMNS.forEach((c, i) => {
    regions.push({
      id: `porch-column-${i + 1}`,
      tier: "porch-column",
      polygon: rect(c.x0, c.y0, c.x1 - c.x0, c.y1 - c.y0),
    });
  });

  /* Porch deck boards */
  const deckW = (PORCH_DECK.x1 - PORCH_DECK.x0) / PORCH_BOARD_COUNT;
  for (let i = 0; i < PORCH_BOARD_COUNT; i++) {
    regions.push({
      id: `porch-board-${i + 1}`,
      tier: "porch-board",
      polygon: rect(
        PORCH_DECK.x0 + i * deckW,
        PORCH_DECK.y0,
        deckW,
        PORCH_DECK.y1 - PORCH_DECK.y0,
      ),
    });
  }

  /* Main roof shingle grid */
  const mrW = MAIN_ROOF.x1 - MAIN_ROOF.x0;
  const mrH = MAIN_ROOF.y1 - MAIN_ROOF.y0;
  const cellH = mrH / MAIN_ROOF_ROWS;
  const cellW = mrW / MAIN_ROOF_COLS;
  for (let r = 0; r < MAIN_ROOF_ROWS; r++) {
    for (let c = 0; c < MAIN_ROOF_COLS; c++) {
      // Skip cells that would overlap the gable peak triangle (center top).
      const cx = MAIN_ROOF.x0 + (c + 0.5) * cellW;
      const cy = MAIN_ROOF.y0 + (r + 0.5) * cellH;
      // Triangle test: inside the cream gable triangle?
      const inside = pointInTriangle(
        [cx, cy],
        GABLE_PEAK.apex,
        GABLE_PEAK.baseLeft,
        GABLE_PEAK.baseRight,
      );
      if (inside) continue;
      regions.push({
        id: `roof-main-r${r + 1}-c${c + 1}`,
        tier: "roof-shingle",
        polygon: rect(
          MAIN_ROOF.x0 + c * cellW,
          MAIN_ROOF.y0 + r * cellH,
          cellW,
          cellH,
        ),
      });
    }
  }

  /* Porch roof shingles */
  const prW = (PORCH_ROOF.x1 - PORCH_ROOF.x0) / PORCH_ROOF_COLS;
  const prH = (PORCH_ROOF.y1 - PORCH_ROOF.y0) / PORCH_ROOF_ROWS;
  for (let r = 0; r < PORCH_ROOF_ROWS; r++) {
    for (let c = 0; c < PORCH_ROOF_COLS; c++) {
      regions.push({
        id: `roof-porch-r${r + 1}-c${c + 1}`,
        tier: "roof-shingle",
        polygon: rect(
          PORCH_ROOF.x0 + c * prW,
          PORCH_ROOF.y0 + r * prH,
          prW,
          prH,
        ),
      });
    }
  }

  /* Gable peak triangle: 5 horizontal trapezoidal strips */
  for (let r = 0; r < GABLE_PEAK_ROWS; r++) {
    const t0 = r / GABLE_PEAK_ROWS;
    const t1 = (r + 1) / GABLE_PEAK_ROWS;
    const yTop = GABLE_PEAK.apex[1] + t0 * (GABLE_PEAK.baseLeft[1] - GABLE_PEAK.apex[1]);
    const yBot = GABLE_PEAK.apex[1] + t1 * (GABLE_PEAK.baseLeft[1] - GABLE_PEAK.apex[1]);
    const halfTop = t0 * (GABLE_PEAK.baseRight[0] - GABLE_PEAK.apex[0]);
    const halfBot = t1 * (GABLE_PEAK.baseRight[0] - GABLE_PEAK.apex[0]);
    const cx = GABLE_PEAK.apex[0];
    regions.push({
      id: `gable-peak-r${r + 1}`,
      tier: "roof-gable",
      polygon: [
        [cx - halfTop, yTop],
        [cx + halfTop, yTop],
        [cx + halfBot, yBot],
        [cx - halfBot, yBot],
      ],
    });
  }

  return regions;
}

function pointInTriangle(
  p: [number, number],
  a: [number, number],
  b: [number, number],
  c: [number, number],
): boolean {
  const sign = (p1: [number, number], p2: [number, number], p3: [number, number]) =>
    (p1[0] - p3[0]) * (p2[1] - p3[1]) - (p2[0] - p3[0]) * (p1[1] - p3[1]);
  const d1 = sign(p, a, b);
  const d2 = sign(p, b, c);
  const d3 = sign(p, c, a);
  const hasNeg = d1 < 0 || d2 < 0 || d3 < 0;
  const hasPos = d1 > 0 || d2 > 0 || d3 > 0;
  return !(hasNeg && hasPos);
}

export const REGIONS: Region[] = buildRegions();

/** Map for quick lookup by id */
export const REGION_BY_ID: Record<string, Region> = Object.fromEntries(
  REGIONS.map((r) => [r.id, r]),
);

/** Total count exported for the README + summary tools */
export const REGION_COUNT = REGIONS.length;
