import sharp from "sharp";
import path from "path";
import { fileURLToPath } from "url";
import { CLIENT } from "../src/client.ts";

const __dirname = path.dirname(fileURLToPath(import.meta.url));
const OUT_PATH = path.join(__dirname, "../public/map.png");
const TILE_SIZE = 256;
const GRID = 5;
const OUTPUT_W = 600;
const OUTPUT_H = 400;

// Decode a Plus Code short code given a rough reference lat/lng
function decodePlusCode(shortCode: string, refLat: number, refLng: number): { lat: number; lng: number } {
  const ALPHABET = "23456789CFGHJMPQRVWX";
  const val = (c: string) => ALPHABET.indexOf(c.toUpperCase());

  // Expand short code by prepending the right 4-char prefix for refLat/refLng
  const n2 = Math.pow(2, 0); // unused but kept for clarity
  const prefixLat = Math.floor((refLat + 90) / 20) * 20 - 90;
  const prefixLng = Math.floor((refLng + 180) / 20) * 20 - 180;
  const c1 = ALPHABET[Math.floor((refLat + 90) / 20)];
  const c2 = ALPHABET[Math.floor((refLng + 180) / 20)];
  const c3 = ALPHABET[Math.floor(((refLat + 90) % 20))];
  const c4 = ALPHABET[Math.floor(((refLng + 180) % 20))];
  const full = c1 + c2 + c3 + c4 + shortCode;

  // Strip the +
  const digits = full.replace("+", "");

  // Decode pairs
  let lat = -90, lng = -180;
  let latRes = 20, lngRes = 20;

  for (let i = 0; i < Math.min(digits.length, 10); i += 2) {
    if (i < 2) { latRes = 20; lngRes = 20; }
    else if (i < 4) { latRes = 1; lngRes = 1; }
    else if (i < 6) { latRes = 1/20; lngRes = 1/20; }
    else if (i < 8) { latRes = 1/400; lngRes = 1/400; }
    else { latRes = 1/8000; lngRes = 1/8000; }

    lat += val(digits[i]) * latRes;
    lng += val(digits[i + 1]) * lngRes;
  }

  // Return center of the cell
  return { lat: lat + latRes / 2, lng: lng + lngRes / 2 };
}

const { plusCode, zoom } = CLIENT.location;
// Approximate center of Dillon, SC as reference
const { lat, lng } = decodePlusCode(plusCode, 34.42, -79.37);
console.log(`Decoded ${plusCode} → lat=${lat.toFixed(6)}, lng=${lng.toFixed(6)}`);

function latLngToTile(lat: number, lng: number, z: number) {
  const x = Math.floor(((lng + 180) / 360) * Math.pow(2, z));
  const y = Math.floor(
    ((1 - Math.log(Math.tan((lat * Math.PI) / 180) + 1 / Math.cos((lat * Math.PI) / 180)) / Math.PI) / 2) * Math.pow(2, z)
  );
  return { x, y };
}

function latLngToPixelOffset(lat: number, lng: number, z: number) {
  const n = Math.pow(2, z);
  const xFrac = ((lng + 180) / 360) * n;
  const yFrac = ((1 - Math.log(Math.tan((lat * Math.PI) / 180) + 1 / Math.cos((lat * Math.PI) / 180)) / Math.PI) / 2) * n;
  return { px: (xFrac % 1) * TILE_SIZE, py: (yFrac % 1) * TILE_SIZE };
}

async function fetchTile(z: number, x: number, y: number): Promise<Buffer> {
  const url = `https://tile.openstreetmap.org/${z}/${x}/${y}.png`;
  const res = await fetch(url, { headers: { "User-Agent": "StorageWiseRemotionVideoTool/1.0" } });
  if (!res.ok) throw new Error(`Tile fetch failed: ${res.status}`);
  return Buffer.from(await res.arrayBuffer());
}

console.log(`Fetching ${GRID}x${GRID} tiles at zoom ${zoom}...`);
const center = latLngToTile(lat, lng, zoom);
const offset = latLngToPixelOffset(lat, lng, zoom);
const half = Math.floor(GRID / 2);
const stitchSize = GRID * TILE_SIZE;

const composites: { input: Buffer; left: number; top: number }[] = [];
for (let row = 0; row < GRID; row++) {
  for (let col = 0; col < GRID; col++) {
    process.stdout.write(`  tile ${col + 1 + row * GRID}/${GRID * GRID}\r`);
    const buf = await fetchTile(zoom, center.x + (col - half), center.y + (row - half));
    composites.push({ input: buf, left: col * TILE_SIZE, top: row * TILE_SIZE });
  }
}

const cropLeft = Math.min(Math.max(0, Math.round(half * TILE_SIZE + offset.px - OUTPUT_W / 2)), stitchSize - OUTPUT_W);
const cropTop  = Math.min(Math.max(0, Math.round(half * TILE_SIZE + offset.py - OUTPUT_H / 2)), stitchSize - OUTPUT_H);

await sharp({ create: { width: stitchSize, height: stitchSize, channels: 4, background: "#f0ece4" } })
  .composite(composites)
  .extract({ left: cropLeft, top: cropTop, width: OUTPUT_W, height: OUTPUT_H })
  .png()
  .toFile(OUT_PATH);

console.log(`\nSaved to public/map.png (${OUTPUT_W}x${OUTPUT_H})`);
