import { html, LitElement, nothing, type TemplateResult } from 'lit'
import { Router, type SharePublisher, type ShareView } from '@civility/ui'
import app from '../store/app.ts'
import { bookmarkDisplayName, toSharedBookmark } from '../store/schema.ts'
import { setMapNav } from '../utils/nav.ts'
import { icon } from '../utils/icon.ts'
import {
  downloadBookmark,
  type ExportFormat,
  renderExportDialog,
} from '../utils/bookmark-export.ts'

/** Fired when the header's edit button is pressed. */
export const BOOKMARK_EDIT_EVENT = 'bookmark-edit'

interface HeaderAction {
  key: string
  label: string
  iconName: string
  activeClass?: string
  toggle?: boolean
  pressed?: boolean
  /** Stays a standalone header button instead of folding into the menu. */
  pinned?: boolean
  onClick: () => void
}

/**
 * Title bar for `/bookmark/{id}`: back, the location's name, and the actions
 * that apply to the whole record.
 *
 * Everything except "edit" acts on the store directly, so it
 * needs nothing from the page.
 *
 * Only Favorite and Visited stay as standalone buttons; every other action
 * lives behind the `more-vertical` menu, keeping the header at a fixed 3
 * icons regardless of viewport width.
 */
export class MBookmarkHeader extends LitElement {
  static override properties = {
    bookmarkId: { attribute: 'bookmark-id' },
  }

  declare bookmarkId: string
  #shareOpen = false
  #shareView: ShareView | null = null
  #exportOpen = false
  #menuOpen = false

  protected override createRenderRoot() {
    return this
  }

  #onAppUpdate = () => this.requestUpdate()

  override connectedCallback() {
    super.connectedCallback()
    app.addEventListener(this.#onAppUpdate)
    document.addEventListener('click', this.#onDocumentClick)
  }

  override disconnectedCallback() {
    super.disconnectedCallback()
    app.removeEventListener(this.#onAppUpdate)
    document.removeEventListener('click', this.#onDocumentClick)
  }

  // Any click that isn't inside the overflow menu dismisses it.
  #onDocumentClick = (e: Event) => {
    if (!this.#menuOpen) return
    const menu = this.querySelector('.header-menu')
    if (menu && !e.composedPath().includes(menu)) {
      this.#menuOpen = false
      this.requestUpdate()
    }
  }

  #toggleMenu = (e: Event) => {
    e.stopPropagation()
    this.#menuOpen = !this.#menuOpen
    this.requestUpdate()
  }

  #bookmark() {
    return app.bookmarks.find((b) => b.id === this.bookmarkId)
  }

  #sharePublisher: SharePublisher = {
    publish: async () => {
      const { id, url } = await app.publishBookmark(this.bookmarkId)
      const b = this.#bookmark()
      this.#shareView = {
        id,
        url,
        label: b ? bookmarkDisplayName(b) : undefined,
      }
      return this.#shareView
    },
    revoke: async (id: string) => {
      await app.revokeShare(id)
      this.#shareView = null
    },
  }

  #openShare = async () => {
    const record = await app.shareForBookmark(this.bookmarkId)
    const url = record ? await app.shareUrlFor(record.id) : undefined
    const b = this.#bookmark()
    this.#shareView = record && url
      ? { id: record.id, url, label: b ? bookmarkDisplayName(b) : undefined }
      : null
    this.#shareOpen = true
    this.requestUpdate()
  }

  #toggleVisited = async () => {
    const b = this.#bookmark()
    if (!b) return
    await app.updateBookmark(b.id, { visited: !b.visited })
  }

  #toggleFavorite = async () => {
    const b = this.#bookmark()
    if (!b) return
    await app.updateBookmark(b.id, { favorite: !b.favorite })
  }

  #openExport = () => {
    this.#exportOpen = true
    this.requestUpdate()
  }

  #dismissExport = () => {
    this.#exportOpen = false
    this.requestUpdate()
  }

  #exportBookmark = (format: ExportFormat) => {
    const b = this.#bookmark()
    if (!b) return
    downloadBookmark(toSharedBookmark(b), format)
    this.#dismissExport()
  }

  #showOnMap = () => {
    const b = this.#bookmark()
    if (!b) return
    const [lng, lat] = b.geometry.coordinates
    setMapNav({ lat, lng, zoom: b.zoom, bookmarkId: b.id })
    globalThis.location.hash = '#!/'
  }

  #actions(): HeaderAction[] {
    const b = this.#bookmark()
    if (!b) return []
    return [
      {
        key: 'favorite',
        label: b.favorite ? 'Favorited' : 'Mark favorite',
        iconName: 'heart',
        activeClass: 'bm-icon-btn--favorite-on',
        toggle: true,
        pressed: b.favorite,
        pinned: true,
        onClick: this.#toggleFavorite,
      },
      {
        key: 'visited',
        label: b.visited ? 'Visited' : 'Mark visited',
        iconName: b.visited ? 'check-circle' : 'circle',
        activeClass: 'bm-icon-btn--on',
        toggle: true,
        pressed: b.visited,
        pinned: true,
        onClick: this.#toggleVisited,
      },
      {
        key: 'map',
        label: 'Show on map',
        iconName: 'navigation',
        onClick: this.#showOnMap,
      },
      {
        key: 'share',
        label: 'Share',
        iconName: 'share-2',
        onClick: this.#openShare,
      },
      {
        key: 'export',
        label: 'Export',
        iconName: 'download',
        onClick: this.#openExport,
      },
      {
        key: 'edit',
        label: 'Edit',
        iconName: 'edit',
        onClick: () =>
          globalThis.dispatchEvent(new CustomEvent(BOOKMARK_EDIT_EVENT)),
      },
    ]
  }

  #chooseFromMenu(action: HeaderAction) {
    action.onClick()
    this.#menuOpen = false
    this.requestUpdate()
  }

  override render(): TemplateResult {
    const b = this.#bookmark()
    const actions = this.#actions()
    const visible = actions.filter((a) => a.pinned)
    const collapsed = actions.filter((a) => !a.pinned)

    return html`
      <button
        class="header-back"
        aria-label="Go back"
        @click="${() => Router.goBack('#!/bookmarks')}"
      >
        ${icon('arrow-left', '20px')}
      </button>
      <h1>${b ? bookmarkDisplayName(b) : 'Location'}</h1>
      ${b
        ? html`
          <div class="header-actions">
            ${visible.map((a) =>
              html`
                <button
                  class="bm-icon-btn${a.pressed && a.activeClass
                    ? ` ${a.activeClass}`
                    : ''}"
                  aria-pressed="${a.toggle ? String(!!a.pressed) : nothing}"
                  aria-label="${a.label}"
                  title="${a.label}"
                  @click="${a.onClick}"
                >
                  ${icon(a.iconName, '18px')}
                </button>
              `
            )}
            ${collapsed.length
              ? html`
                <div class="header-menu">
                  <button
                    class="bm-icon-btn"
                    aria-label="More actions"
                    aria-expanded="${this.#menuOpen}"
                    @click="${this.#toggleMenu}"
                  >
                    ${icon('more-vertical', '18px')}
                  </button>
                  ${this.#menuOpen
                    ? html`
                      <div class="header-menu-panel" role="menu">
                        ${collapsed.map((a) =>
                          html`
                            <button
                              class="header-menu-item"
                              role="menuitem"
                              @click="${() => this.#chooseFromMenu(a)}"
                            >
                              ${icon(a.iconName, '18px')}
                              <span>${a.label}</span>
                            </button>
                          `
                        )}
                      </div>
                    `
                    : nothing}
                </div>
              `
              : nothing}
          </div>
        `
        : nothing}

      <civ-share-dialog
        .open="${this.#shareOpen}"
        .methods="${this.#sharePublisher}"
        .share="${this.#shareView}"
        heading="Share location"
        @on-published="${() => this.requestUpdate()}"
        @on-revoked="${() => {
          this.#shareView = null
          this.requestUpdate()
        }}"
        @dismiss="${() => {
          this.#shareOpen = false
          this.requestUpdate()
        }}"
      ></civ-share-dialog>

      ${renderExportDialog(
        this.#exportOpen,
        this.#exportBookmark,
        this.#dismissExport,
      )}
    `
  }
}

customElements.define('m-bookmark-header', MBookmarkHeader)
