import { Command } from '@cliffy/command'
import { join } from '@std/path'
import { PBF_DIR } from '../shared/paths.ts'
import { run } from '../shared/run.ts'

const PLANET_PBF = join(PBF_DIR, 'planet-latest.osm.pbf')
const TRIMMED_PBF = join(PBF_DIR, 'planet-trimmed.osm.pbf')

/**
 * osmium tags-filter expressions covering every tag read by process.world.lua.
 *
 * Format: <object-type>/<key>=<value> or <object-type>/<key>
 *   n = node, w = way, r = relation, a = area (way or relation)
 *
 * We intentionally omit buildings, addresses, shop/amenity POIs, minor roads,
 * and anything else that only appears at z12+ in the regional tile pipeline.
 */
const FILTER_EXPRESSIONS = `
# ── Nodes ──────────────────────────────────────────────────────────────────
# place nodes (countries, states, cities, towns, villages)
n/place=continent
n/place=country
n/place=state
n/place=province
n/place=city
n/place=town
n/place=village

# natural peaks & volcanoes
n/natural=peak
n/natural=volcano

# ── Ways ───────────────────────────────────────────────────────────────────
# Administrative boundaries
w/boundary=administrative
w/boundary=national_park

# Major roads only (motorway, trunk, primary, secondary + their links)
w/highway=motorway
w/highway=motorway_link
w/highway=trunk
w/highway=trunk_link
w/highway=primary
w/highway=primary_link
w/highway=secondary
w/highway=secondary_link

# Ferry routes
w/route=ferry

# Main-line railways
w/railway=rail
w/railway=narrow_gauge
w/railway=preserved
w/railway=funicular

# Rivers & named waterways
w/waterway=river
w/waterway=canal

# Water polygons
w/natural=water
w/water

# Landcover
w/natural=wood
w/natural=wetland
w/natural=beach
w/natural=sand
w/natural=dune
w/natural=glacier
w/natural=ice_shelf
w/natural=bare_rock
w/natural=scree
w/natural=fell
w/natural=grassland
w/natural=grass
w/natural=heath
w/natural=meadow
w/natural=scrub
w/natural=shrubbery
w/natural=tundra
w/natural=bay

# Landuse (landcover-mapped keys)
w/landuse=forest
w/landuse=farmland
w/landuse=farm
w/landuse=orchard
w/landuse=vineyard
w/landuse=allotments
w/landuse=village_green
w/landuse=recreation_ground

# Landuse (landuse layer keys)
w/landuse=school
w/landuse=university
w/landuse=hospital
w/landuse=railway
w/landuse=cemetery
w/landuse=military
w/landuse=residential
w/landuse=commercial
w/landuse=industrial
w/landuse=retail
w/landuse=stadium
w/landuse=pitch

# Leisure (landcover + park)
w/leisure=park
w/leisure=garden
w/leisure=nature_reserve
w/leisure=stadium
w/leisure=pitch

# Parks / nature reserves via boundary
w/boundary=national_park
w/leisure=nature_reserve

# Place islands (rendered as centroids)
w/place=island

# ── Relations ──────────────────────────────────────────────────────────────
# Administrative boundaries (required for boundary layer)
r/type=boundary
r/boundary=administrative
r/boundary=national_park
r/leisure=nature_reserve
`.trim()

export const trimWorldCmd = new Command()
  .name('trim:world')
  .description(
    'Trim planet-latest.osm.pbf to only the data needed for world basemap tiles ' +
      '(removes buildings, POIs, minor roads, addresses, etc.). ' +
      `Output: ${TRIMMED_PBF}`,
  )
  .option('--overwrite', 'Overwrite existing trimmed PBF if it exists.', {
    default: false,
  })
  .action(async ({ overwrite }) => {
    // Check source file exists
    if (!(await Deno.stat(PLANET_PBF).catch(() => null))) {
      throw new Error(
        `Planet PBF not found: ${PLANET_PBF}\n` +
          `Download it from https://planet.openstreetmap.org and place it at that path.`,
      )
    }

    // Check output doesn't already exist unless --overwrite
    if (!overwrite && (await Deno.stat(TRIMMED_PBF).catch(() => null))) {
      console.log(`Trimmed PBF already exists: ${TRIMMED_PBF}`)
      console.log(`Use --overwrite to replace it.`)
      Deno.exit(0)
    }

    // Write expressions to a temp file so we don't hit shell argument limits
    // and so the list stays readable.
    const tmpExpressions = join(PBF_DIR, '.trim-world-expressions.tmp')
    await Deno.writeTextFile(tmpExpressions, FILTER_EXPRESSIONS)

    const { size: inputSize } = await Deno.stat(PLANET_PBF)
    const inputSizeStr = (inputSize / 1024 ** 3).toFixed(1)
    console.log(`Input:  ${PLANET_PBF} (${inputSizeStr} GB)`)
    console.log(`Output: ${TRIMMED_PBF}`)
    console.log(`Running osmium tags-filter — this will take a while...`)

    try {
      await run('osmium', [
        'tags-filter',
        '--progress',
        '--expressions',
        tmpExpressions,
        '--output',
        TRIMMED_PBF,
        ...(overwrite ? ['--overwrite'] : []),
        PLANET_PBF,
      ])
    } finally {
      await Deno.remove(tmpExpressions).catch(() => {})
    }

    const { size: outputSize } = await Deno.stat(TRIMMED_PBF)
    const outputSizeStr = outputSize < 1024 ** 3
      ? `${Math.round(outputSize / 1024 ** 2)} MB`
      : `${(outputSize / 1024 ** 3).toFixed(1)} GB`
    const reduction = (((inputSize - outputSize) / inputSize) * 100).toFixed(0)

    console.log(`\nDone!`)
    console.log(`Output: ${TRIMMED_PBF} (${outputSizeStr})`)
    console.log(`Reduced planet by ~${reduction}%.`)
    console.log(
      `\nNext step: deno task data build:world\n` +
        `(build:world will automatically use the trimmed file when present)`,
    )
  })
