import { LitElement, html, css, nothing } from 'lit';
import { customElement, property, state, query } from 'lit/decorators.js';
import { classMap } from 'lit/directives/class-map.js';
import { designTokens } from '../../styles/tokens.css';
import { srOnly } from '../../styles/sr-only.css';
import { unownedLabel, unownedStyles } from '@utils/ownership';
import {
LookupReleaseGroup,
BrowseReleases,
GetThumbnail,
} from '@go/explore/service.js';
import {
GetAlbumTracks,
GetAlbumCompleteness,
GetFilePathsByRecordingMBIDs,
} from '@go/library/library.js';
import * as library from '@go/library/models.js';
import type * as download from '@go/download/models.js';
import type * as explore from '@go/explore/models.js';
type MBReleaseGroup = explore.MBReleaseGroup;
type MBRelease = explore.MBRelease;
type MBTrack = explore.MBTrack;
import { exploreCache } from '../../store/explore-cache';
import { libraryStore } from '../../store/library-store';
import { creditLink, exploreLinkStyles } from '../../utils/explore-link';
import { creditStore } from '@store/credit-store';
import { describeError } from '../../utils/describe-error';
import { EventsOn } from '@runtime/runtime';
import { Events } from '../../events';
import '@awesome.me/webawesome/dist/components/icon/icon.js';
import '../library-status-indicator/library-status-indicator.js';
import { libraryStatusFor } from '@utils/library-status';
import { ICON_AUTOTAG } from '@utils/icon-language';
import type { LibraryStatus } from '../library-status-indicator/library-status-indicator.js';
import '../catalog-scope-notice/catalog-scope-notice.js';
import type { CatalogScope } from '../catalog-scope-notice/catalog-scope-notice.js';
import '@awesome.me/webawesome/dist/components/button/button.js';
import '../download-picker/download-picker';
import { downloadStore } from '../../store/download-store';
import { MatchForAlbum, ApplyAsync } from '@go/autotagservice/service.js';
import type * as autotagservice from '@go/autotagservice/models.js';
import { confirmAction } from '../confirm-dialog/confirm-dialog';
import { queueStore } from '../../store/queue-store';
import type { QueueSource } from '../../store/queue-store';
import { notificationStore } from '../../store/notification-store';
import '../notifications/inline-notice';
import {
ContextMenuController,
contextMenuStyles,
isContextMenuKey,
} from '@utils/context-menu-controller.js';
import type { ContextMenuHost, MenuTarget } from '@utils/context-menu-controller.js';
import '@awesome.me/webawesome/dist/components/popup/popup.js';
import type WaPopup from '@awesome.me/webawesome/dist/components/popup/popup.js';
import type { MenuSurface } from '../menu-surface/menu-surface';
import '../menu-surface/menu-surface';
import '@awesome.me/webawesome/dist/components/dropdown-item/dropdown-item.js';
import { dictByName } from '@utils/binding';
import type { TrackDetails } from '@components/track-details/track-details.js';
import { showTrackDetailsForPath } from '@utils/track-details-opener.js';
import '@components/playlist-picker/playlist-picker.js';
import {
ICON_CAN_REQUEST,
ICON_PLAYLIST,
ICON_QUEUE,
ICON_REQUESTED,
} from '@utils/icon-language';
/**
* The region the album header's own failures are rendered in.
*
* "Inline" says *not global*, not *where* — so the region is named
* once, here, rather than spelled at each call site.
*/
export const ExploreAlbumRegion = 'explore-album';
/**
* How long to wait on a background release browse that has reported
* neither success nor failure before treating it as gone. See
* `armReleasesFallback` for why this is generous.
*/
const RELEASES_FALLBACK_MS = 60000;
/* ── Utility functions (duplicated per Knowledge Pattern #9 — no cross-component imports) ── */
/** The earliest release date in a group, for tie-breaking. */
function earliestDate(releases: MBRelease[]): string {
let earliest = '\uffff';
for (const r of releases) {
const d = r.date || '\uffff';
if (d < earliest) earliest = d;
}
return earliest;
}
function extractYear(dateStr: string): string {
if (!dateStr) return '';
return dateStr.substring(0, 4);
}
/**
* Convert a duration in milliseconds to a human-readable "m:ss" string.
* Returns "0:00" for zero/negative/NaN values.
*/
function formatDuration(ms: number): string {
if (!ms || ms <= 0) return '0:00';
const totalSeconds = Math.round(ms / 1000);
const minutes = Math.floor(totalSeconds / 60);
const seconds = totalSeconds % 60;
return `${minutes}:${String(seconds).padStart(2, '0')}`;
}
/* ── Types ── */
interface ReleaseCluster {
representative: MBRelease;
allReleases: MBRelease[];
fingerprint: string;
/** Standard-version score, computed by buildClusters. */
score: number;
}
/** Synthetic kinds — virtual entries pinned at the top of the dropdown. */
type SyntheticKind = 'standard' | 'library';
/** Unified version-entry shape covering both synthetic and real clusters. */
interface VersionEntry {
/** Stable identifier for the dropdown