import { html, nothing, type TemplateResult } from 'lit'
import type { SharedBookmark } from '../store/schema.ts'
import { icon } from './icon.ts'
import { linkIconName } from './link-icon.ts'
import { type Bounds, pointInBounds } from './bounds.ts'
import type {
  MiniMapViewportDetail,
  MMiniMap,
} from '../components/m-mini-map.ts'

export interface ListDetailHost {
  requestUpdate(): void
  querySelector<E extends Element = Element>(selector: string): E | null
}

/**
 * Shared behavior behind a "mini-map + list of bookmarks" view: tracks the
 * mini-map's viewport to filter the list to what's on screen, and which
 * rows have their detail expanded. Used by both the public share page
 * (`routes/share-view.ts`) and the owner's list detail page
 * (`routes/list.ts`) so the two don't drift.
 *
 * A plain class rather than a Lit mixin — the two hosts don't otherwise
 * share a base class, and this only needs `requestUpdate`/`querySelector`
 * from whichever `LitElement` owns it.
 */
export class ListDetailController {
  #host: ListDetailHost
  #bounds: Bounds | null = null
  /** Keyed by bookmark identity, not list index — index shifts as the
   * viewport filter narrows and widens the list. */
  #expanded = new Set<string>()

  constructor(host: ListDetailHost) {
    this.#host = host
  }

  /** Current mini-map viewport, or null until it reports its first bounds. */
  get bounds(): Bounds | null {
    return this.#bounds
  }

  /** Every bookmark until the map reports its first viewport, then only
   * those within it. */
  visibleBookmarks(all: SharedBookmark[]): SharedBookmark[] {
    const bounds = this.#bounds
    if (!bounds) return all
    return all.filter((b) => pointInBounds(b.coordinates, bounds))
  }

  onViewport = (e: CustomEvent<MiniMapViewportDetail>): void => {
    this.#bounds = e.detail.bounds
    this.#host.requestUpdate()
  }

  focus(coordinates: [number, number]): void {
    this.#host.querySelector<MMiniMap>('m-mini-map')?.focusPoint(coordinates)
  }

  resetView(): void {
    this.#host.querySelector<MMiniMap>('m-mini-map')?.resetView()
  }

  itemKey(b: SharedBookmark): string {
    return `${b.coordinates.join(',')}|${b.name}`
  }

  toggleDetail(key: string): void {
    if (this.#expanded.has(key)) this.#expanded.delete(key)
    else this.#expanded.add(key)
    this.#host.requestUpdate()
  }

  renderListItem(b: SharedBookmark): TemplateResult {
    const [lng, lat] = b.coordinates
    // Only http(s) links are rendered — a shared payload is untrusted, so
    // anything that could carry a javascript:/data: href is dropped.
    const links = Object.entries(b.links ?? {}).filter(([, url]) =>
      /^https?:\/\//i.test(url)
    )
    const hasDetail = !!(b.address || b.description || links.length)
    const key = this.itemKey(b)
    const expanded = hasDetail && this.#expanded.has(key)

    return html`
      <li class="share-view-list-row">
        <button
          class="share-view-list-item"
          @click="${() => this.focus(b.coordinates)}"
        >
          <span class="share-view-list-name">
            ${b.name} ${b.favorite
              ? html`
                <span class="bm-favorite-badge" title="Favorite">${icon(
                  'heart',
                  '1em',
                )}</span>
              `
              : nothing} ${b.visited
              ? html`
                <span class="share-view-visited" title="Visited">${icon(
                  'check-circle',
                  '1em',
                )}</span>
              `
              : nothing}
          </span>
          <span class="share-view-list-meta">
            ${b.address ?? `${lat.toFixed(5)}, ${lng.toFixed(5)}`}
          </span>
        </button>
        ${hasDetail
          ? html`
            <button
              type="button"
              class="share-view-list-toggle"
              aria-expanded="${expanded}"
              aria-label="${expanded ? 'Hide details' : 'Show details'}"
              @click="${() => this.toggleDetail(key)}"
            >${icon('chevron-down', '16px')}</button>
          `
          : nothing}
        ${expanded
          ? html`
            <div class="share-view-list-detail">
              ${b.address
                ? html`<p class="share-view-address">${b.address}</p>`
                : nothing}
              ${b.description
                ? html`
                  <p class="share-view-description">${b.description}</p>
                `
                : nothing}
              ${links.length
                ? html`
                  <ul class="bookmark-page-links">
                    ${links.map(([label, url]) =>
                      html`
                        <li>
                          <a
                            class="bookmark-page-link"
                            href="${url}"
                            target="_blank"
                            rel="noopener noreferrer"
                          >
                            ${icon(linkIconName(url), '16px')}
                            <span>${label}</span>
                          </a>
                        </li>
                      `
                    )}
                  </ul>
                `
                : nothing}
            </div>
          `
          : nothing}
      </li>
    `
  }
}
