/**
 * Generate 10 Tiled-compatible JSON maps per PRD §10.
 * Also writes .tmx for optional `tiled --export-map` round-trip.
 */
import fs from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';

const __dirname = path.dirname(fileURLToPath(import.meta.url));
const outDir = path.join(__dirname, '../public/assets/maps');
const tmxDir = path.join(__dirname, '../mapsrc');
fs.mkdirSync(outDir, { recursive: true });
fs.mkdirSync(tmxDir, { recursive: true });

const TILE = 16;

const TILESET = {
  firstgid: 1,
  columns: 16,
  image: '../tilesets/village.png',
  imagewidth: 256,
  imageheight: 128,
  tilecount: 128,
  tilewidth: 16,
  tileheight: 16,
  name: 'village',
  margin: 0,
  spacing: 0,
  tiles: [
    // collidable tiles
    ...[9, 10, 11, 12, 13, 14, 15, 19, 20, 21, 22, 24, 26, 29, 30, 35, 41, 46].map((id) => ({
      id,
      properties: [{ name: 'collides', type: 'bool', value: true }],
    })),
  ],
};

function emptyGrid(w, h, fill = 0) {
  return Array.from({ length: w * h }, () => fill);
}

function set(data, w, x, y, v) {
  if (x < 0 || y < 0 || x >= w) return;
  data[y * w + x] = v;
}

function fillRect(data, w, x0, y0, x1, y1, v) {
  for (let y = y0; y <= y1; y++) {
    for (let x = x0; x <= x1; x++) set(data, w, x, y, v);
  }
}

function border(data, w, h, v) {
  for (let x = 0; x < w; x++) {
    set(data, w, x, 0, v);
    set(data, w, x, h - 1, v);
  }
  for (let y = 0; y < h; y++) {
    set(data, w, 0, y, v);
    set(data, w, w - 1, y, v);
  }
}

function makeMap(name, w, h, build) {
  const ground = emptyGrid(w, h, 1); // grass gid 1 (tile index 0 is empty, firstgid=1 → tile 0 empty, tile1 grass)
  // Our tileset: tile index in image 0 = empty transparent; index 1 = grass
  // firstgid 1 means GID 1 = tile index 0. Let's use firstgid 1 where GID N = image tile (N-1)
  // Simpler: firstgid=1, and we store image-tile-index+1 as GID.
  // grass is image tile 1 → GID 2
  for (let i = 0; i < ground.length; i++) ground[i] = 2; // grass

  const obstacles = emptyGrid(w, h, 0);
  const objects = [];

  const api = {
    w,
    h,
    ground,
    obstacles,
    objects,
    // GID 0 = empty; image tile index N → GID N+1. Passing 0 clears (no collision).
    setG: (x, y, imgTile) => set(ground, w, x, y, imgTile <= 0 ? 0 : imgTile + 1),
    setO: (x, y, imgTile) => set(obstacles, w, x, y, imgTile <= 0 ? 0 : imgTile + 1),
    clearO: (x, y) => set(obstacles, w, x, y, 0),
    fillG: (x0, y0, x1, y1, imgTile) =>
      fillRect(ground, w, x0, y0, x1, y1, imgTile <= 0 ? 0 : imgTile + 1),
    fillO: (x0, y0, x1, y1, imgTile) =>
      fillRect(obstacles, w, x0, y0, x1, y1, imgTile <= 0 ? 0 : imgTile + 1),
    borderO: (imgTile) => {
      const gid = imgTile <= 0 ? 0 : imgTile + 1;
      for (let x = 0; x < w; x++) {
        set(obstacles, w, x, 0, gid);
        set(obstacles, w, x, h - 1, gid);
      }
      for (let y = 0; y < h; y++) {
        set(obstacles, w, 0, y, gid);
        set(obstacles, w, w - 1, y, gid);
      }
    },
    spawn: (x, y) => {
      objects.push({
        id: objects.length + 1,
        name: 'spawn',
        type: 'spawn',
        x: x * TILE,
        y: y * TILE,
        width: TILE,
        height: TILE,
      });
    },
    npc: (name, x, y) => {
      objects.push({
        id: objects.length + 1,
        name,
        type: 'npc',
        x: x * TILE,
        y: y * TILE,
        width: TILE,
        height: TILE,
        properties: [{ name: 'npcId', type: 'string', value: name }],
      });
    },
    door: (name, x, y, target, targetSpawn) => {
      // Door tiles must be walkable (clear obstacle collision)
      set(obstacles, w, x, y, 0);
      objects.push({
        id: objects.length + 1,
        name,
        type: 'door',
        x: x * TILE,
        y: y * TILE,
        width: TILE,
        height: TILE,
        properties: [
          { name: 'target', type: 'string', value: target },
          { name: 'spawn', type: 'string', value: targetSpawn || 'spawn' },
        ],
      });
    },
    interact: (name, x, y, interactId) => {
      objects.push({
        id: objects.length + 1,
        name,
        type: 'interact',
        x: x * TILE,
        y: y * TILE,
        width: TILE,
        height: TILE,
        properties: [{ name: 'interactId', type: 'string', value: interactId }],
      });
    },
  };

  build(api);

  const map = {
    compressionlevel: -1,
    height: h,
    width: w,
    infinite: false,
    layers: [
      {
        id: 1,
        name: 'ground',
        type: 'tilelayer',
        width: w,
        height: h,
        data: ground,
        opacity: 1,
        visible: true,
        x: 0,
        y: 0,
      },
      {
        id: 2,
        name: 'obstacles',
        type: 'tilelayer',
        width: w,
        height: h,
        data: obstacles,
        opacity: 1,
        visible: true,
        x: 0,
        y: 0,
        properties: [{ name: 'collides', type: 'bool', value: true }],
      },
      {
        id: 3,
        name: 'objects',
        type: 'objectgroup',
        objects,
        opacity: 1,
        visible: true,
        x: 0,
        y: 0,
      },
    ],
    nextlayerid: 4,
    nextobjectid: objects.length + 1,
    orientation: 'orthogonal',
    renderorder: 'right-down',
    tiledversion: '1.12.2',
    tileheight: TILE,
    tilewidth: TILE,
    type: 'map',
    version: '1.10',
    tilesets: [
      {
        ...TILESET,
        // embed relative path for Phaser
      },
    ],
  };

  const jsonPath = path.join(outDir, `${name}.json`);
  fs.writeFileSync(jsonPath, JSON.stringify(map, null, 2));
  console.log('wrote', jsonPath);

  // Minimal TMX for Tiled CLI export verification
  const tmx = `<?xml version="1.0" encoding="UTF-8"?>
<map version="1.10" tiledversion="1.12.2" orientation="orthogonal" renderorder="right-down" width="${w}" height="${h}" tilewidth="16" tileheight="16" infinite="0" nextlayerid="4" nextobjectid="${objects.length + 1}">
 <tileset firstgid="1" source="village.tsx"/>
 <layer id="1" name="ground" width="${w}" height="${h}">
  <data encoding="csv">
${ground.join(',')}
  </data>
 </layer>
 <layer id="2" name="obstacles" width="${w}" height="${h}">
  <properties>
   <property name="collides" type="bool" value="true"/>
  </properties>
  <data encoding="csv">
${obstacles.join(',')}
  </data>
 </layer>
 <objectgroup id="3" name="objects">
${objects
  .map(
    (o) =>
      `  <object id="${o.id}" name="${o.name}" type="${o.type}" x="${o.x}" y="${o.y}" width="${o.width}" height="${o.height}"/>`,
  )
  .join('\n')}
 </objectgroup>
</map>
`;
  fs.writeFileSync(path.join(tmxDir, `${name}.tmx`), tmx);
  return map;
}

// TSX tileset for Tiled
fs.writeFileSync(
  path.join(tmxDir, 'village.tsx'),
  `<?xml version="1.0" encoding="UTF-8"?>
<tileset version="1.10" tiledversion="1.12.2" name="village" tilewidth="16" tileheight="16" tilecount="128" columns="16">
 <image source="../public/assets/tilesets/village.png" width="256" height="128"/>
</tileset>
`,
);

// --- Maps ---

// 1. House 12x8
makeMap('house', 12, 8, (m) => {
  m.fillG(0, 0, 11, 7, 16); // wood floor
  m.borderO(11);
  m.setO(2, 2, 18); // bed
  m.setO(3, 2, 18);
  m.setO(9, 2, 28); // calendar
  m.setO(5, 4, 22); // table
  m.setO(1, 5, 23); // chair
  // clear door at bottom
  m.setO(5, 7, 0);
  m.setO(6, 7, 0);
  m.setG(5, 7, 17);
  m.setG(6, 7, 17);
  m.spawn(6, 5);
  m.door('to_plaza', 5, 7, 'PlazaScene', 'from_house');
  m.door('to_plaza2', 6, 7, 'PlazaScene', 'from_house');
  m.interact('bed', 2, 2, 'bed');
  m.interact('calendar', 9, 2, 'calendar');
});

// 2. Plaza 30x20 hub
makeMap('plaza', 30, 20, (m) => {
  m.fillG(0, 0, 29, 19, 1);
  // path cross
  m.fillG(13, 0, 16, 19, 3);
  m.fillG(0, 8, 29, 11, 3);
  // flower beds
  m.fillO(10, 6, 12, 7, 12 + 0); // use flower tile 7
  m.setO(10, 6, 7);
  m.setO(11, 6, 7);
  m.setO(12, 6, 7);
  m.setO(10, 7, 7);
  m.setO(11, 7, 7);
  m.setO(12, 7, 7);
  // board
  m.setO(15, 5, 29);
  // tree + birds
  m.setO(20, 4, 10);
  m.setO(20, 5, 9);
  m.setO(21, 4, 10);
  // mailbox
  m.setO(4, 10, 30);
  // houses facades as doors targets
  m.setO(5, 2, 13);
  m.setO(6, 2, 13);
  m.setO(5, 3, 12);
  m.setO(6, 3, 14); // door visual
  m.setO(22, 2, 13);
  m.setO(23, 2, 13);
  m.setO(22, 3, 12);
  m.setO(23, 3, 14);
  m.setO(8, 14, 13);
  m.setO(9, 14, 13);
  m.setO(8, 15, 12);
  m.setO(9, 15, 14);
  // walls edge soft
  for (let x = 0; x < 30; x++) {
    m.setO(x, 0, 35);
    m.setO(x, 19, 35);
  }
  // openings
  m.setO(14, 0, 0);
  m.setO(15, 0, 0);
  m.setO(14, 19, 0);
  m.setO(15, 19, 0);
  m.setO(0, 9, 0);
  m.setO(0, 10, 0);
  m.setO(29, 9, 0);
  m.setO(29, 10, 0);

  m.spawn(15, 12);
  m.npc('afu', 15, 6);
  m.npc('dabao', 5, 11);
  m.npc('twinbirds', 20, 3);
  m.npc('extra1', 18, 14);
  m.npc('extra4', 10, 12);

  m.door('from_house', 6, 4, 'HouseScene', 'spawn'); // visual marker only when entering - actual door below
  // Fix: doors as interact targets
  // Re-define doors properly
  m.objects.length = m.objects.filter((o) => o.type !== 'door').length; // no, just add
});

// Fix plaza - rebuild cleanly
function rebuildPlaza() {
  return makeMap('plaza', 30, 20, (m) => {
    m.fillG(0, 0, 29, 19, 1);
    m.fillG(13, 0, 16, 19, 3);
    m.fillG(0, 8, 29, 11, 3);
    // flower bed tiles
    for (const [x, y] of [
      [10, 6],
      [11, 6],
      [12, 6],
      [10, 7],
      [11, 7],
      [12, 7],
    ]) {
      m.setO(x, y, 7);
    }
    m.setO(15, 5, 29); // board
    m.setO(20, 4, 10);
    m.setO(20, 5, 9);
    m.setO(4, 10, 30); // mailbox
    // house doors on north
    m.setO(5, 2, 13);
    m.setO(6, 2, 13);
    m.setO(5, 3, 12);
    m.setO(6, 3, 14);
    m.setO(22, 2, 13);
    m.setO(23, 2, 13);
    m.setO(22, 3, 12);
    m.setO(23, 3, 14);
    // clinic south-west
    m.setO(3, 15, 12);
    m.setO(4, 15, 14);
    // bakery east-ish
    m.setO(25, 8, 12);
    m.setO(25, 9, 14);
    // shop west
    m.setO(2, 8, 12);
    m.setO(2, 9, 14);
    // ribbon posts
    m.setO(12, 10, 33);
    m.setO(17, 10, 33);
    m.setO(12, 13, 33);
    m.setO(17, 13, 33);

    m.spawn(15, 12);
    // named entry spawns as objects
    m.objects.push({
      id: 100,
      name: 'from_house',
      type: 'spawn',
      x: 6 * 16,
      y: 5 * 16,
      width: 16,
      height: 16,
    });
    m.objects.push({
      id: 101,
      name: 'from_bakery',
      type: 'spawn',
      x: 24 * 16,
      y: 9 * 16,
      width: 16,
      height: 16,
    });
    m.objects.push({
      id: 102,
      name: 'from_shop',
      type: 'spawn',
      x: 3 * 16,
      y: 9 * 16,
      width: 16,
      height: 16,
    });
    m.objects.push({
      id: 103,
      name: 'from_clinic',
      type: 'spawn',
      x: 4 * 16,
      y: 14 * 16,
      width: 16,
      height: 16,
    });
    m.objects.push({
      id: 104,
      name: 'from_lake',
      type: 'spawn',
      x: 15 * 16,
      y: 17 * 16,
      width: 16,
      height: 16,
    });
    m.objects.push({
      id: 105,
      name: 'from_field',
      type: 'spawn',
      x: 15 * 16,
      y: 2 * 16,
      width: 16,
      height: 16,
    });
    m.objects.push({
      id: 106,
      name: 'from_well',
      type: 'spawn',
      x: 27 * 16,
      y: 10 * 16,
      width: 16,
      height: 16,
    });
    m.objects.push({
      id: 107,
      name: 'from_fog',
      type: 'spawn',
      x: 2 * 16,
      y: 16 * 16,
      width: 16,
      height: 16,
    });

    m.npc('afu', 15, 6);
    m.npc('dabao', 5, 11);
    m.npc('twinbirds', 20, 3);
    m.npc('extra1', 18, 14);
    m.npc('extra4', 10, 12);

    m.door('house', 6, 3, 'HouseScene', 'spawn');
    m.door('bakery', 25, 9, 'BakeryScene', 'spawn');
    m.door('shop', 2, 9, 'ShopScene', 'spawn');
    m.door('clinic', 4, 15, 'ClinicScene', 'spawn');
    m.door('lake', 15, 19, 'LakeScene', 'spawn');
    m.door('field', 15, 0, 'FieldScene', 'spawn');
    m.door('well', 29, 10, 'WellScene', 'spawn');
    m.door('fog', 0, 16, 'FogScene', 'spawn');

    m.interact('board', 15, 5, 'board');
    m.interact('flowers', 11, 6, 'flowers');
    m.interact('mailbox', 4, 10, 'mailbox');
    m.interact('ribbon1', 12, 10, 'ribbon');
    m.interact('ribbon2', 17, 10, 'ribbon');
    m.interact('ribbon3', 12, 13, 'ribbon');
    m.interact('ribbon4', 17, 13, 'ribbon');
  });
}
rebuildPlaza();

// 3. Bakery 15x10
makeMap('bakery', 15, 10, (m) => {
  m.fillG(0, 0, 14, 9, 16);
  m.borderO(12);
  m.fillO(2, 2, 6, 3, 19); // counter
  m.setO(12, 2, 20); // oven
  m.setO(10, 2, 42); // recipe paper
  m.setO(4, 6, 22);
  m.setO(5, 7, 0);
  m.setO(6, 9, 0);
  m.setO(7, 9, 0);
  m.spawn(7, 7);
  m.npc('yuanyuan', 4, 4);
  m.door('to_plaza', 7, 9, 'PlazaScene', 'from_bakery');
  m.interact('recipe', 10, 2, 'recipe');
  m.interact('counter', 4, 3, 'bread');
  m.interact('knead', 3, 3, 'knead');
});

// 4. Shop 15x10
makeMap('shop', 15, 10, (m) => {
  m.fillG(0, 0, 14, 9, 17);
  m.borderO(12);
  for (let x = 2; x <= 12; x++) {
    m.setO(x, 2, 21);
    m.setO(x, 4, 21);
  }
  m.setO(7, 9, 0);
  m.setO(8, 9, 0);
  m.spawn(7, 7);
  m.npc('mimi', 7, 5);
  m.door('to_plaza', 7, 9, 'PlazaScene', 'from_shop');
  m.interact('shelf', 5, 2, 'shelf');
});

// 5. Clinic 12x8
makeMap('clinic', 12, 8, (m) => {
  m.fillG(0, 0, 11, 7, 17);
  m.borderO(12);
  m.setO(3, 2, 22);
  m.setO(4, 2, 43); // form
  m.setO(8, 3, 18);
  m.setO(5, 7, 0);
  m.setO(6, 7, 0);
  m.spawn(6, 5);
  m.npc('fu', 3, 3);
  m.door('to_plaza', 6, 7, 'PlazaScene', 'from_clinic');
  m.interact('form', 4, 2, 'form');
});

// 6. Lake 24x16
makeMap('lake', 24, 16, (m) => {
  m.fillG(0, 0, 23, 15, 1);
  m.fillG(0, 8, 23, 15, 5); // water
  m.fillG(0, 8, 23, 8, 6);
  m.fillO(8, 4, 10, 5, 23); // bench area chairs
  m.setO(9, 4, 23);
  m.setO(12, 6, 31); // picnic
  m.setO(13, 6, 31);
  // path from north
  m.fillG(11, 0, 12, 7, 3);
  m.setO(11, 0, 0);
  m.setO(12, 0, 0);
  m.spawn(11, 2);
  m.npc('gui', 9, 5);
  m.npc('extra3', 18, 4);
  m.door('to_plaza', 11, 0, 'PlazaScene', 'from_lake');
  m.interact('picnic', 12, 6, 'picnic');
  m.interact('bench', 9, 4, 'bench');
});

// 7. Field 20x14
makeMap('field', 20, 14, (m) => {
  m.fillG(0, 0, 19, 13, 1);
  m.fillG(9, 10, 10, 13, 3);
  for (const [x, y] of [
    [3, 3],
    [5, 4],
    [7, 3],
    [12, 5],
    [14, 3],
    [4, 7],
    [15, 7],
    [8, 6],
  ]) {
    m.setO(x, y, 8); // berry
  }
  m.setO(10, 2, 32); // pot
  m.spawn(10, 11);
  m.npc('extra2', 6, 8);
  m.door('to_plaza', 10, 13, 'PlazaScene', 'from_field');
  m.interact('berry1', 3, 3, 'berry');
  m.interact('berry2', 5, 4, 'berry');
  m.interact('berry3', 7, 3, 'berry');
  m.interact('berry4', 12, 5, 'berry');
  m.interact('berry5', 14, 3, 'berry');
  m.interact('pot', 10, 2, 'pot');
});

// 8. Well 14x10
makeMap('well', 14, 10, (m) => {
  m.fillG(0, 0, 13, 9, 1);
  m.fillG(0, 4, 3, 5, 3);
  m.setO(7, 4, 24); // well
  m.setO(6, 4, 24);
  m.setO(7, 5, 24);
  m.setO(6, 5, 24);
  m.spawn(2, 5);
  m.npc('extra5', 10, 7);
  m.door('to_plaza', 0, 5, 'PlazaScene', 'from_well');
  m.interact('well', 7, 4, 'well');
});

// 9. Fog 20x8
makeMap('fog', 20, 8, (m) => {
  m.fillG(0, 0, 19, 7, 1);
  m.fillG(0, 3, 5, 4, 3);
  for (let x = 12; x < 20; x++) {
    for (let y = 0; y < 8; y++) m.setO(x, y, 25);
  }
  m.spawn(3, 4);
  m.door('to_plaza', 0, 4, 'PlazaScene', 'from_fog');
  m.interact('fogwall', 12, 4, 'fogwall');
});

// 10. White layer corridor 20x6 + ward via objects
makeMap('white', 20, 14, (m) => {
  m.fillG(0, 0, 19, 13, 27);
  m.fillO(0, 0, 19, 0, 26);
  m.fillO(0, 5, 19, 5, 26);
  m.fillO(0, 0, 0, 5, 26);
  m.fillO(19, 0, 19, 5, 26);
  // ward below
  m.fillG(5, 6, 14, 13, 27);
  m.fillO(5, 6, 14, 6, 26);
  m.fillO(5, 13, 14, 13, 26);
  m.fillO(5, 6, 5, 13, 26);
  m.fillO(14, 6, 14, 13, 26);
  m.setO(10, 5, 0); // opening
  m.setO(9, 9, 18); // bed
  m.setO(11, 8, 43);
  m.spawn(2, 3);
  m.interact('record', 11, 8, 'record');
  m.door('to_ward', 10, 5, 'WhiteScene', 'ward');
  m.objects.push({
    id: 200,
    name: 'ward',
    type: 'spawn',
    x: 10 * 16,
    y: 8 * 16,
    width: 16,
    height: 16,
  });
});

console.log('All 10 maps generated.');
