import { html, LitElement, type TemplateResult } from 'lit'
import app from '../store/app.ts'
import { type Bookmark, type BookmarkList } from '../store/schema.ts'
import { parseGeoJson } from '../store/adapters/geojson.ts'
import { parseGpx } from '../store/adapters/gpx.ts'
import { parseKml, parseKmz } from '../store/adapters/kml.ts'
import type { ImportResult } from '../store/adapters/types.ts'
import { icon } from '../utils/icon.ts'
import { setEditMode } from '../utils/list-edit-mode.ts'
import {
  BOOKMARKS_IMPORT_EVENT,
  BOOKMARKS_NEW_BOOKMARK_EVENT,
  BOOKMARKS_NEW_LIST_EVENT,
} from '../components/m-bookmarks-header.ts'
import '../components/m-bookmarks-list.ts'

export class BookmarksPage extends LitElement {
  private bookmarks: Bookmark[] = []
  private lists: BookmarkList[] = []
  private showAddBookmark = false
  private showAddList = false
  private pendingImport: ImportResult | null = null
  private importError: string | null = null
  private importTargetListId: string | null = null

  protected override createRenderRoot() {
    return this
  }

  override connectedCallback() {
    super.connectedCallback()
    setEditMode(false)
    this.#sync()
    app.addEventListener(this.#onUpdate)
    globalThis.addEventListener(BOOKMARKS_NEW_LIST_EVENT, this.#onNewList)
    globalThis.addEventListener(
      BOOKMARKS_NEW_BOOKMARK_EVENT,
      this.#onNewBookmark,
    )
    globalThis.addEventListener(BOOKMARKS_IMPORT_EVENT, this.#onImportChosen)
  }

  override disconnectedCallback() {
    super.disconnectedCallback()
    app.removeEventListener(this.#onUpdate)
    globalThis.removeEventListener(BOOKMARKS_NEW_LIST_EVENT, this.#onNewList)
    globalThis.removeEventListener(
      BOOKMARKS_NEW_BOOKMARK_EVENT,
      this.#onNewBookmark,
    )
    globalThis.removeEventListener(
      BOOKMARKS_IMPORT_EVENT,
      this.#onImportChosen,
    )
  }

  #onUpdate = () => {
    this.#sync()
    this.requestUpdate()
  }

  // The action buttons live in the header, a separate router landmark.
  #onNewList = () => {
    this.showAddList = !this.showAddList
    this.showAddBookmark = false
    this.requestUpdate()
  }

  #onNewBookmark = () => {
    this.showAddBookmark = !this.showAddBookmark
    this.showAddList = false
    this.requestUpdate()
  }

  #onImportChosen = () => {
    this.renderRoot.querySelector<HTMLInputElement>('.bm-file-input')?.click()
  }

  #sync() {
    this.bookmarks = app.bookmarks
    this.lists = app.bookmarkLists
  }

  async #handleImport(e: Event) {
    const input = e.target as HTMLInputElement
    const file = input.files?.[0]
    if (!file) return
    input.value = ''
    this.importError = null
    this.importTargetListId = null
    try {
      if (file.name.endsWith('.kmz')) {
        this.pendingImport = parseKmz(await file.arrayBuffer())
      } else if (file.name.endsWith('.gpx')) {
        this.pendingImport = parseGpx(await file.text())
      } else if (file.name.endsWith('.json')) {
        this.pendingImport = parseGeoJson(await file.text())
      } else {
        this.pendingImport = parseKml(await file.text())
      }
      const existing = app.bookmarks
      for (const bm of this.pendingImport.bookmarks) {
        bm.isDuplicate = existing.some(
          (e) =>
            Math.abs(e.geometry.coordinates[1] - bm.lat) < 0.0001 &&
            Math.abs(e.geometry.coordinates[0] - bm.lng) < 0.0001,
        )
      }
    } catch (err) {
      this.importError = err instanceof Error
        ? err.message
        : 'Failed to parse file'
    }
    this.requestUpdate()
  }

  async #confirmImport() {
    if (!this.pendingImport) return
    const { folders, bookmarks } = this.pendingImport
    const targetListId = this.importTargetListId
    this.pendingImport = null
    this.importTargetListId = null
    this.requestUpdate()
    const listIdMap = new Map<string, string>()
    for (const folder of folders) {
      listIdMap.set(folder.tempId, await app.addList(folder.name))
    }
    for (const bm of bookmarks) {
      if (bm.isDuplicate) continue
      const listId = targetListId ??
        (bm.folderTempId ? (listIdMap.get(bm.folderTempId) ?? null) : null)
      const categories = listId ? [listId] : []
      await app.addBookmark(bm.lat, bm.lng, 12, bm.properties, categories)
    }
  }

  async #submitAddList(e: Event) {
    e.preventDefault()
    const form = e.target as HTMLFormElement
    const fd = new FormData(form)
    const name = (fd.get('name') as string).trim()
    const color = (fd.get('color') as string) || undefined
    if (!name) return
    await app.addList(name, color)
    form.reset()
    this.showAddList = false
    this.requestUpdate()
  }

  async #submitAddBookmark(e: Event) {
    e.preventDefault()
    const form = e.target as HTMLFormElement
    const fd = new FormData(form)
    const name = (fd.get('name') as string).trim()
    const lat = parseFloat(fd.get('lat') as string)
    const lng = parseFloat(fd.get('lng') as string)
    const zoom = parseFloat(fd.get('zoom') as string) || 12
    const listId = (fd.get('listId') as string) || null
    const categories = listId ? [listId] : []
    if (!name || isNaN(lat) || isNaN(lng)) return
    await app.addBookmark(lat, lng, zoom, { name }, categories)
    form.reset()
    this.showAddBookmark = false
    this.requestUpdate()
  }

  override render(): TemplateResult {
    const unlisted = this.bookmarks
      .filter((b) => (b.categories || []).length === 0)
    const hasLists = this.lists.length > 0
    const isEmpty = this.bookmarks.length === 0 && !hasLists

    return html`
      <input
        class="bm-file-input"
        type="file"
        accept=".kml,.kmz,.gpx,.json"
        style="display:none"
        @change="${this.#handleImport}"
      >
      ${this.importError
        ? html`
          <p class="bm-import-error">${this.importError}</p>
        `
        : ''} ${this.showAddList
        ? html`
          <form class="bm-add-form" @submit="${this.#submitAddList}">
            <input
              name="name"
              type="text"
              placeholder="List name"
              required
              autocomplete="off"
              autofocus
            >
            <div class="bm-folder-color-row">
              <label for="add-list-color">Color</label>
              <input
                id="add-list-color"
                name="color"
                type="color"
                value="#e05c2a"
              >
            </div>
            <div class="bm-form-actions">
              <button type="submit">Add</button>
              <button
                type="button"
                @click="${() => {
                  this.showAddList = false
                  this.requestUpdate()
                }}"
              >
                Cancel
              </button>
            </div>
          </form>
        `
        : ''} ${isEmpty
        ? html`
          <p class="bm-empty">No bookmarks yet. Add one with the button above.</p>
        `
        : ''} ${this.lists.map((list) => {
          const count = this.bookmarks.filter((b) =>
            b.categories.includes(list.id)
          ).length
          return html`
            <a class="bm-folder" href="#!/list/${list.id}">
              ${list.color
                ? html`
                  <span
                    class="bm-folder-swatch"
                    style="background:${list.color}"
                    aria-hidden="true"
                  ></span>
                `
                : ''}
              <span class="bm-folder-name">${list.name}</span>
              ${app.isListShared(list.id)
                ? html`
                  <span class="bm-share-badge" title="Shared">${icon(
                    'share-2',
                    '0.9em',
                  )}</span>
                `
                : ''}
              <span class="bm-folder-count">${count}</span>
            </a>
          `
        })} ${unlisted.length > 0
        ? html`
          <div class="bm-section">
            ${hasLists
              ? html`
                <h3 class="bm-section-title">Not in a list</h3>
              `
              : ''}
            <m-bookmarks-list
              .bookmarks="${unlisted}"
              .lists="${this.lists}"
            ></m-bookmarks-list>
          </div>
        `
        : ''} ${this.showAddBookmark
        ? html`
          <form class="bm-add-form bm-add-form--bookmark" @submit="${this
            .#submitAddBookmark}">
            <h3 class="bm-section-title">Add Bookmark</h3>
            <input
              name="name"
              type="text"
              placeholder="Name"
              required
              autocomplete="off"
              autofocus
            >
            <div class="bm-coord-row">
              <input name="lat" type="number" step="any" placeholder="Latitude" required>
              <input name="lng" type="number" step="any" placeholder="Longitude" required>
              <input
                name="zoom"
                type="number"
                step="any"
                placeholder="Zoom"
                min="1"
                max="22"
              >
            </div>
            ${hasLists
              ? html`
                <select name="listId">
                  <option value="">Not in a list</option>
                  ${this.lists.map((c) =>
                    html`
                      <option value="${c.id}">${c.name}</option>
                    `
                  )}
                </select>
              `
              : ''}
            <div class="bm-form-actions">
              <button type="submit">Save</button>
              <button
                type="button"
                @click="${() => {
                  this.showAddBookmark = false
                  this.requestUpdate()
                }}"
              >
                Cancel
              </button>
            </div>
          </form>
        `
        : ''}

      <ui-dialog
        ?open="${this.pendingImport !== null}"
        @dismiss="${() => {
          this.pendingImport = null
          this.importTargetListId = null
          this.requestUpdate()
        }}"
      >
        <dialog>
          <article class="bm-dialog">
            ${this.pendingImport
              ? html`
                <h2 class="bm-dialog-title">Import Preview</h2>
                <p class="bm-import-summary">
                  ${(() => {
                    const dupes = this.pendingImport!.bookmarks.filter(
                      (b) => b.isDuplicate,
                    ).length
                    const fresh = this.pendingImport!.bookmarks.length - dupes
                    const folderCount = this.pendingImport!.folders.length
                    const parts = []
                    if (fresh > 0) {
                      parts.push(
                        `${fresh} bookmark${fresh === 1 ? '' : 's'} to import`,
                      )
                    }
                    if (dupes > 0) {
                      parts.push(
                        `${dupes} duplicate${
                          dupes === 1 ? '' : 's'
                        } will be skipped`,
                      )
                    }
                    if (folderCount > 0) {
                      parts.push(
                        `${folderCount} list${folderCount === 1 ? '' : 's'}`,
                      )
                    }
                    return parts.join(' · ') || 'Nothing to import'
                  })()}
                </p>
                <div class="bm-import-preview">
                  ${this.pendingImport.folders.map((folder) => {
                    const items = this.pendingImport!.bookmarks.filter(
                      (b) => b.folderTempId === folder.tempId,
                    )
                    return html`
                      <div class="bm-import-group">
                        <p class="bm-import-group-name">${folder.name}</p>
                        ${items.map((b) => this.#renderImportItem(b))}
                      </div>
                    `
                  })} ${this.pendingImport.bookmarks.filter(
                      (b) => b.folderTempId === null,
                    ).length > 0
                    ? html`
                      <div class="bm-import-group">
                        ${this.pendingImport.folders.length > 0
                          ? html`
                            <p class="bm-import-group-name">Not in a list</p>
                          `
                          : ''} ${this.pendingImport.bookmarks
                          .filter((b) => b.folderTempId === null)
                          .map((b) => this.#renderImportItem(b))}
                      </div>
                    `
                    : ''}
                </div>
                ${this.pendingImport.folders.length === 0 &&
                    this.lists.length > 0
                  ? html`
                    <select
                      .value="${this.importTargetListId ?? ''}"
                      @change="${(e: Event) => {
                        this.importTargetListId =
                          (e.target as HTMLSelectElement).value || null
                      }}"
                    >
                      <option value="">Not in a list</option>
                      ${this.lists.map(
                        (c) =>
                          html`
                            <option
                              value="${c.id}"
                              ?selected="${this.importTargetListId ===
                                c.id}"
                            >
                              ${c.name}
                            </option>
                          `,
                      )}
                    </select>
                  `
                  : ''}
                <div class="bm-form-actions">
                  <button @click="${this.#confirmImport}">Import</button>
                  <button
                    type="button"
                    @click="${() => {
                      this.pendingImport = null
                      this.importTargetListId = null
                      this.requestUpdate()
                    }}"
                  >
                    Cancel
                  </button>
                </div>
              `
              : ''}
          </article>
        </dialog>
      </ui-dialog>
    `
  }

  #renderImportItem(b: ImportResult['bookmarks'][number]): TemplateResult {
    const name = b.properties.displayName ?? b.properties.name ??
      b.properties.address?.displayText ??
      `${b.lat.toFixed(4)}, ${b.lng.toFixed(4)}`
    return html`
      <div
        class="bm-import-item${b.isDuplicate
          ? ' bm-import-item--duplicate'
          : ''}"
      >
        <span class="bm-item-name">${name}</span>
        <span class="bm-item-coords">${b.lat.toFixed(4)}, ${b.lng.toFixed(
          4,
        )}</span>
        ${b.isDuplicate
          ? html`
            <span class="bm-import-dupe-badge">exists</span>
          `
          : ''}
      </div>
    `
  }
}

customElements.define('r-bookmarks', BookmarksPage)
