import { html, nothing, type TemplateResult } from 'lit'
import type { SharedBookmark } from '../store/schema.ts'

export type ExportFormat = 'geojson' | 'gpx' | 'kml'

/** Lets the export dialog offer "all" vs. "only what's on screen" when a
 * viewport filter is active on the list beside the map. */
export interface ExportScopeOption {
  allCount: number
  visibleCount: number
  scope: 'all' | 'visible'
  onScopeChange: (scope: 'all' | 'visible') => void
}

function escapeXml(s: string): string {
  return s
    .replace(/&/g, '&amp;')
    .replace(/</g, '&lt;')
    .replace(/>/g, '&gt;')
    .replace(/"/g, '&quot;')
    .replace(/'/g, '&apos;')
}

/** GPX/KML have no dedicated address/links fields, so fold them into the notes. */
function notes(point: SharedBookmark): string {
  const parts: string[] = []
  if (point.address) parts.push(point.address)
  if (point.description) parts.push(point.description)
  for (const [label, url] of Object.entries(point.links ?? {})) {
    parts.push(`${label}: ${url}`)
  }
  return parts.join('\n\n')
}

function toGeoJson(points: SharedBookmark[]): string {
  const features = points.map((point) => {
    const properties: Record<string, unknown> = { name: point.name }
    if (point.address) properties.address = point.address
    if (point.description) properties.description = point.description
    if (point.links) properties.links = point.links
    if (point.visited) properties.visited = point.visited
    return {
      type: 'Feature' as const,
      geometry: { type: 'Point' as const, coordinates: point.coordinates },
      properties,
    }
  })

  // A bare Feature for one point preserves the exact output a single
  // bookmark export has always produced; a FeatureCollection otherwise.
  return JSON.stringify(
    features.length === 1
      ? features[0]
      : { type: 'FeatureCollection', features },
    null,
    2,
  )
}

function toGpx(points: SharedBookmark[]): string {
  const wpts = points.map((point) => {
    const [lng, lat] = point.coordinates
    const desc = notes(point)
    return `  <wpt lat="${lat}" lon="${lng}">
    <name>${escapeXml(point.name)}</name>${
      desc ? `\n    <desc>${escapeXml(desc)}</desc>` : ''
    }
  </wpt>`
  }).join('\n')

  return `<?xml version="1.0" encoding="UTF-8"?>
<gpx version="1.1" creator="MapsApp" xmlns="http://www.topografix.com/GPX/1/1">
${wpts}
</gpx>
`
}

function toKml(points: SharedBookmark[]): string {
  const placemarks = points.map((point) => {
    const [lng, lat] = point.coordinates
    const desc = notes(point)
    return `  <Placemark>
    <name>${escapeXml(point.name)}</name>${
      desc ? `\n    <description>${escapeXml(desc)}</description>` : ''
    }
    <Point>
      <coordinates>${lng},${lat}</coordinates>
    </Point>
  </Placemark>`
  }).join('\n')

  return `<?xml version="1.0" encoding="UTF-8"?>
<kml xmlns="http://www.opengis.net/kml/2.2">
${placemarks}
</kml>
`
}

function serialize(points: SharedBookmark[], format: ExportFormat): string {
  switch (format) {
    case 'geojson':
      return toGeoJson(points)
    case 'gpx':
      return toGpx(points)
    case 'kml':
      return toKml(points)
  }
}

function mimeFor(format: ExportFormat): string {
  switch (format) {
    case 'geojson':
      return 'application/geo+json'
    case 'gpx':
      return 'application/gpx+xml'
    case 'kml':
      return 'application/vnd.google-earth.kml+xml'
  }
}

function filenameFor(title: string, format: ExportFormat): string {
  const slug = title.trim().toLowerCase()
    .replace(/[^a-z0-9]+/g, '-')
    .replace(/^-+|-+$/g, '') || 'bookmark'
  return `${slug}.${format}`
}

function download(content: string, mime: string, filename: string): void {
  const blob = new Blob([content], { type: mime })
  const url = URL.createObjectURL(blob)
  const a = document.createElement('a')
  a.href = url
  a.download = filename
  a.click()
  URL.revokeObjectURL(url)
}

/** Triggers a browser download of one location as GeoJSON, GPX, or KML. */
export function downloadBookmark(
  point: SharedBookmark,
  format: ExportFormat,
): void {
  download(
    serialize([point], format),
    mimeFor(format),
    filenameFor(point.name, format),
  )
}

/** Triggers a browser download of a whole list of locations as one file. */
export function downloadBookmarks(
  points: SharedBookmark[],
  name: string,
  format: ExportFormat,
): void {
  download(
    serialize(points, format),
    mimeFor(format),
    filenameFor(name, format),
  )
}

/**
 * The "export as…" dialog, shared by the owner's header and the public share
 * page — both just need to hand it an open flag and two callbacks. Passing
 * `scopeOption` adds an "all vs. visible now" toggle above the format
 * buttons, for the list share page's viewport filter.
 */
export function renderExportDialog(
  open: boolean,
  onExport: (format: ExportFormat) => void,
  onDismiss: () => void,
  scopeOption?: ExportScopeOption,
): TemplateResult {
  return html`
    <ui-dialog ?open="${open}" @dismiss="${onDismiss}">
      <dialog>
        <article class="bm-dialog">
          <h2 class="bm-dialog-title">Export location${scopeOption
            ? 's'
            : ''}</h2>
          <p>Save ${scopeOption
            ? 'these locations'
            : 'this location'} as a file to open in another app.</p>
          ${scopeOption
            ? html`
              <div class="bm-scope-toggle">
                <button
                  type="button"
                  class="bm-scope-btn"
                  aria-pressed="${scopeOption.scope === 'all'}"
                  @click="${() => scopeOption.onScopeChange('all')}"
                >
                  All ${scopeOption.allCount}
                </button>
                <button
                  type="button"
                  class="bm-scope-btn"
                  aria-pressed="${scopeOption.scope === 'visible'}"
                  @click="${() => scopeOption.onScopeChange('visible')}"
                >
                  Visible now (${scopeOption.visibleCount})
                </button>
              </div>
            `
            : nothing}
          <div class="bm-form-actions bm-export-actions">
            <button
              type="button"
              class="action action--compact"
              @click="${() => onExport('geojson')}"
            >
              GeoJSON
            </button>
            <button
              type="button"
              class="action action--compact"
              @click="${() => onExport('gpx')}"
            >
              GPX
            </button>
            <button
              type="button"
              class="action action--compact"
              @click="${() => onExport('kml')}"
            >
              KML
            </button>
            <button type="button" class="link" @click="${onDismiss}">
              Close
            </button>
          </div>
        </article>
      </dialog>
    </ui-dialog>
  `
}
