import { star, unstar } from '../api/subsonicStarRating';
import { getArtist, getArtistInfo } from '../api/subsonicArtists';
import type { SubsonicArtist, SubsonicAlbum, SubsonicArtistInfo } from '../api/subsonicTypes';
import { useEffect, useState, useMemo } from 'react';
import { useParams, useNavigate } from 'react-router-dom';
import { ndListAlbumsByArtistRole } from '../api/navidromeBrowse';
import AlbumCard from '../components/AlbumCard';
import { ArtistHeroCover } from '../cover/artistHero';
import { coverArtRef } from '../cover/ref';
import { useCoverLightboxSrc } from '../cover/lightbox';
import { ArrowLeft, Users, ExternalLink, Heart, Feather, Share2 } from 'lucide-react';
import { open } from '@tauri-apps/plugin-shell';
import { usePlayerStore } from '../store/playerStore';
import { useAuthStore } from '../store/authStore';
import { useTranslation } from 'react-i18next';
import { copyEntityShareLink } from '../utils/share/copyEntityShareLink';
import { showToast } from '../utils/ui/toast';
import { sanitizeHtml } from '../utils/sanitizeHtml';
import { usePerfProbeFlags } from '../utils/perf/perfFlags';
import { albumGridWarmCovers } from '../cover/layoutSizes';
import { VirtualCardGrid } from '../components/VirtualCardGrid';

export default function ComposerDetail() {
  const { t } = useTranslation();
  const perfFlags = usePerfProbeFlags();
  const { id } = useParams<{ id: string }>();
  const navigate = useNavigate();
  const [artist, setArtist] = useState<SubsonicArtist | null>(null);
  const [albums, setAlbums] = useState<SubsonicAlbum[]>([]);
  const [info, setInfo] = useState<SubsonicArtistInfo | null>(null);
  const [loading, setLoading] = useState(true);
  const [isStarred, setIsStarred] = useState(false);
  const [bioExpanded, setBioExpanded] = useState(false);
  const [headerCoverFailed, setHeaderCoverFailed] = useState(false);
  const [openedLink, setOpenedLink] = useState<string | null>(null);

  const setStarredOverride = usePlayerStore(s => s.setStarredOverride);
  const musicLibraryFilterVersion = useAuthStore(s => s.musicLibraryFilterVersion);

  // Subsonic `getArtist.view` only follows AlbumArtist relations, so for a
  // composer-only credit it returns the right name + bio but zero albums.
  // Native API `/api/album?_filters={"role_composer_id":"<id>"}` is the only
  // endpoint that walks the participants graph for non-AlbumArtist roles.
  useEffect(() => {
    if (!id) return;
    let cancelled = false;
    setLoading(true);
    Promise.all([
      getArtist(id).catch(() => null),
      ndListAlbumsByArtistRole(id, 'composer', 0, 500).catch(err => {
        console.warn('[psysonic] composer albums load failed:', err);
        return [] as SubsonicAlbum[];
      }),
    ]).then(([artistData, composerAlbums]) => {
      if (cancelled) return;
      if (artistData) {
        setArtist(artistData.artist);
        setIsStarred(!!artistData.artist.starred);
      }
      setAlbums(composerAlbums);
      setLoading(false);
    });
    return () => { cancelled = true; };
  }, [id, musicLibraryFilterVersion]);

  // Bio + Last.fm image — Last.fm matches by name, so well-known composers
  // (Bach, Mozart, Chopin) hit; obscure ones get an empty bio. Failure is
  // silent — we just show the initial-letter avatar instead.
  // Bio is library-independent (Last.fm is global), so this effect tracks
  // [id] only — keeping the bio visible across music-library scope changes.
  // The info reset lives here, not in the load effect, or a scope bump would
  // wipe the bio without re-fetching it.
  useEffect(() => {
    if (!id) return;
    let cancelled = false;
    setInfo(null);
    getArtistInfo(id, { similarArtistCount: 0 })
      .then(i => { if (!cancelled) setInfo(i ?? null); })
      .catch(() => { if (!cancelled) setInfo(null); });
    return () => { cancelled = true; };
  }, [id]);

  useEffect(() => {
    setHeaderCoverFailed(false);
  }, [id]);

  const coverId = artist?.coverArt || artist?.id || '';
  const coverFallbackRef = useMemo(
    () => (coverId ? coverArtRef(coverId) : null),
    [coverId],
  );
  const { open: openLightbox, lightbox } = useCoverLightboxSrc(coverFallbackRef, {
    alt: artist?.name ?? t('composerDetail.unknownComposer'),
  });

  const toggleStar = async () => {
    if (!artist) return;
    const next = !isStarred;
    setIsStarred(next);
    setStarredOverride(artist.id, next);
    try {
      const meta = {
        serverId: artist.serverId,
        name: artist.name,
        albumCount: artist.albumCount,
      };
      if (next) await star(artist.id, 'artist', meta);
      else await unstar(artist.id, 'artist', meta);
    } catch (err) {
      console.warn('[psysonic] composer star failed:', err);
      setIsStarred(!next);
      setStarredOverride(artist.id, !next);
    }
  };

  const openLink = (url: string, key: string) => {
    setOpenedLink(key);
    open(url).catch(() => {});
    setTimeout(() => setOpenedLink(null), 2500);
  };

  const handleShareComposer = async () => {
    if (!id || !artist) return;
    try {
      const ok = await copyEntityShareLink('composer', artist.id);
      if (ok) showToast(t('contextMenu.shareCopied'));
      else showToast(t('contextMenu.shareCopyFailed'), 4000, 'error');
    } catch {
      showToast(t('contextMenu.shareCopyFailed'), 4000, 'error');
    }
  };

  if (loading) {
    return (
      <div className="content-body" style={{ display: 'flex', justifyContent: 'center', padding: '4rem' }}>
        <div className="spinner" />
      </div>
    );
  }

  // Real not-found only when neither metadata nor works came back. If getArtist
  // failed but ndListAlbumsByArtistRole succeeded, render a degraded header so
  // a flaky Subsonic endpoint doesn't hide the works the user came here for.
  if (!artist && albums.length === 0) {
    return (
      <div className="content-body">
        <div style={{ textAlign: 'center', padding: '4rem', color: 'var(--text-muted)' }}>
          {t('composerDetail.notFound')}
        </div>
      </div>
    );
  }

  const displayName = artist?.name || t('composerDetail.unknownComposer');
  const wikiUrl = artist?.name
    ? `https://en.wikipedia.org/wiki/${encodeURIComponent(artist.name)}`
    : '';

  const hasHeroImage = Boolean(
    info?.largeImageUrl || info?.mediumImageUrl || coverId,
  );

  return (
    <div className="content-body animate-fade-in">
      <button
        className="btn btn-ghost"
        onClick={() => navigate(-1)}
        style={{ marginBottom: '1.5rem', display: 'flex', alignItems: 'center', gap: '0.5rem' }}
      >
        <ArrowLeft size={16} /> <span>{t('composerDetail.back')}</span>
      </button>

      {lightbox}

      <div className="artist-detail-header">
        <div className="artist-detail-avatar" style={{ position: 'relative' }}>
          {hasHeroImage && !headerCoverFailed && id ? (
            <button
              className="artist-detail-avatar-btn"
              onClick={openLightbox}
              aria-label={displayName}
            >
              <ArtistHeroCover
                artistId={id}
                artistInfo={info}
                coverFallback={coverFallbackRef}
                displayCssPx={300}
                surface="sparse"
                alt={displayName}
                style={{ width: '100%', height: '100%', objectFit: 'cover' }}
                onError={() => setHeaderCoverFailed(true)}
              />
            </button>
          ) : (
            <Feather size={64} color="var(--text-muted)" />
          )}
        </div>

        <div className="artist-detail-meta">
          <h1 className="page-title" style={{ fontSize: '3rem', marginBottom: '0.25rem' }}>
            {displayName}
          </h1>
          <div style={{ color: 'var(--text-secondary)', fontSize: '1rem', marginBottom: '1rem', display: 'flex', alignItems: 'center', gap: '0.5rem' }}>
            <Users size={14} />
            <span>{t('composerDetail.workCount', { count: albums.length })}</span>
          </div>

          <div style={{ display: 'flex', gap: '8px', flexWrap: 'wrap' }}>
            {wikiUrl && (
              <div className="artist-detail-links">
                <button className="artist-ext-link" onClick={() => openLink(wikiUrl, 'wiki')}>
                  <ExternalLink size={14} />
                  {openedLink === 'wiki' ? t('artistDetail.openedInBrowser') : 'Wikipedia'}
                </button>
              </div>
            )}

            {artist && (
              <button
                className="artist-ext-link"
                onClick={toggleStar}
                data-tooltip={isStarred ? t('artistDetail.favoriteRemove') : t('artistDetail.favoriteAdd')}
                style={{ color: isStarred ? 'var(--accent)' : 'inherit', border: isStarred ? '1px solid var(--accent)' : undefined }}
              >
                <Heart size={14} fill={isStarred ? 'currentColor' : 'none'} />
                {t('artistDetail.favorite')}
              </button>
            )}

            {artist && (
              <button
                type="button"
                className="artist-ext-link"
                onClick={handleShareComposer}
                aria-label={t('composerDetail.shareComposer')}
                data-tooltip={t('composerDetail.shareComposer')}
              >
                <Share2 size={14} />
              </button>
            )}
          </div>
        </div>
      </div>

      {info?.biography && (
        <div className="np-info-card artist-bio-card" style={{ marginTop: '2rem' }}>
          <div className="np-card-header">
            <h3 className="np-card-title">{t('composerDetail.about')}</h3>
          </div>
          <div className="np-artist-bio-row">
            <div className="np-bio-wrap">
              <div
                className={`np-bio-text${bioExpanded ? ' expanded' : ''}`}
                dangerouslySetInnerHTML={{ __html: sanitizeHtml(info.biography) }}
              />
              <button className="np-bio-toggle" onClick={() => setBioExpanded(v => !v)}>
                {bioExpanded ? t('nowPlaying.showLess') : t('nowPlaying.readMore')}
              </button>
            </div>
          </div>
        </div>
      )}

      <h2 className="section-title" style={{ marginTop: '2rem', marginBottom: '1rem' }}>
        {t('composerDetail.works')}
      </h2>
      {albums.length === 0 ? (
        <div style={{ textAlign: 'center', padding: '3rem', color: 'var(--text-muted)' }}>
          {t('composerDetail.noWorks')}
        </div>
      ) : (
        <VirtualCardGrid
          items={albums}
          itemKey={(a, i) => `${a.id}-${i}`}
          rowVariant="album"
          disableVirtualization={perfFlags.disableMainstageVirtualLists}
          layoutSignal={albums.length}
          warmGridCovers={albumGridWarmCovers()}
          renderItem={a => <AlbumCard album={a} />}
        />
      )}
    </div>
  );
}
