'use client';

import { type ReactNode, useEffect, useMemo, useState } from 'react';

import { useLocale, useTranslations } from 'next-intl';

import { ArrowUpRight, MapPin, Play } from 'lucide-react';

import ShareButtons from '@/components/ShareButtons';
import { extractYouTubeId } from '@/components/YoutubeVideo';
import KeywordTag from '@/components/common/KeywordTag';
import Modal from '@/components/common/Modal';
import Section from '@/components/common/Section';
import { useVideoPlayer } from '@/components/common/VideoModalPlayer';
import CreationDescription from '@/components/creations/CreationDescription';
import CreationMediaGallery from '@/components/creations/CreationMediaGallery';
import CreatorIdentityRow from '@/components/creators/CreatorIdentityRow';
import FavoriteCreationButton from '@/components/favorites/FavoriteCreationButton';
import CreationMap, { convertMapData } from '@/components/maps/CreationMap';
import { Heading, Text } from '@/components/ui/typography';
import { cn } from '@/lib/utils';
import {
  compareCreationAttributeKeys,
  getCreationAttributeLabel,
} from '@/utils/creationAttributes';
import {
  getCreationCreatorDisplayName,
  getCreationCreatorProfessionDisplayName,
} from '@/utils/creationHelpers';
import { removeHtmlTags } from '@/utils/creatorHelpers';
import { getSearchUrl } from '@/utils/generalUtils';
import { buildStaticMapUrl } from '@/utils/staticMapUrl';

import { CreationPageItem } from '@/types/Creation';


import SpecList, { type Spec } from './SpecList';
import { ImageObject } from '@/types/Common';

/* ============================================================================
   CREATION DETAILS  —  the "details + description" block of the work page.
   ----------------------------------------------------------------------------
   The catalogue body of a single creation: the title cluster (branch / title /
   year / byline), a key-value spec rail with subject tags + actions, and the
   free-text description beside it.

   Layout adapts to data, not the other way around:
     • WITH a description  → two columns (fixed spec rail | fluid prose).
     • WITHOUT one         → single column, the spec rail spreads to 2-up.
   So sparse records stay short instead of leaving a column of dead space.
   ========================================================================== */

/* A leírás "rövid" küszöbe: e fölött saját, teljes szélességű sávba kerül
   (read-more-ral), alatta a spec rail alá simul. Tiszta (HTML nélküli) hossz. */
const LONG_DESCRIPTION_CHARS = 320;

/* related row — the making-of video and the place-of-origin map share one row
   shape under a "Kapcsolódó" list. A list (not a 2-up grid) so it reads cleanly
   whether there are two items, one, or — once another opens — more, without a
   lone card looking stranded. The thumbnail is a small still (YouTube hq /
   static Google map); when it is missing or fails to load the icon fallback
   stands in, so a map without a static image still looks intentional. */
function RelatedItem({
  thumbnailSrc,
  fallbackIcon,
  title,
  subtitle,
  subtitleLines = 1,
  showPlayOverlay = false,
  ariaLabel,
  onClick,
}: {
  thumbnailSrc: string | null;
  fallbackIcon: ReactNode;
  title: string;
  subtitle?: ReactNode;
  subtitleLines?: 1 | 2;
  showPlayOverlay?: boolean;
  ariaLabel: string;
  onClick: () => void;
}) {
  const [imageFailed, setImageFailed] = useState(false);
  const showImage = Boolean(thumbnailSrc) && !imageFailed;

  return (
    <button
      type="button"
      onClick={onClick}
      aria-label={ariaLabel}
      className="flex w-full items-center gap-3 py-3 text-left transition-colors hover:bg-mma-cyan/20 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-mma-blue"
    >
      <span className="relative flex h-[96px] w-[128px] flex-none items-center justify-center overflow-hidden rounded bg-brand-primary/10">
        {showImage ? (
          // eslint-disable-next-line @next/next/no-img-element
          <img
            src={thumbnailSrc as string}
            alt=""
            onError={() => setImageFailed(true)}
            className="h-full w-full object-cover"
          />
        ) : (
          <span className="text-brand-primary/40">{fallbackIcon}</span>
        )}
        {showPlayOverlay && showImage && (
          <span className="absolute inset-0 flex items-center justify-center">
            <span className="flex h-9 w-9 items-center justify-center rounded-full bg-black/50">
              <Play className="h-4 w-4 fill-white text-white translate-x-0.5" aria-hidden="true" />
            </span>
          </span>
        )}
      </span>

      <span className="flex min-w-0 flex-1 flex-col gap-1">
        <Text as="span" variant="bodySm" tone="strong" className="leading-snug">
          {title}
        </Text>
        {subtitle && (
          <Text
            as="span"
            variant="bodySm"
            tone="muted"
            className={cn(
              'leading-relaxed [&_strong]:font-semibold [&_strong]:text-brand-primary',
              subtitleLines === 2 ? 'line-clamp-2' : 'line-clamp-1'
            )}
          >
            {subtitle}
          </Text>
        )}
      </span>
    </button>
  );
}

/* long-description teaser — keeps the prose under the spec rail like the short
   note, but collapsed to a few lines with a read-more that opens the full text
   in a modal instead of a full-width band. The heading is supplied by the
   surrounding DetailSection. */
function DescriptionTeaser({
  preview,
  readMoreLabel,
  onOpen,
}: {
  preview: string;
  readMoreLabel: string;
  onOpen: () => void;
}) {
  return (
    <div>
      <div className="relative">
        <Text
          as="p"
          variant="bodySm"
          tone="muted"
          className="line-clamp-3 whitespace-pre-line leading-normal"
        >
          {preview}
        </Text>
        <div
          aria-hidden="true"
          className="pointer-events-none absolute inset-x-0 bottom-0 h-8 max-w-[68ch] bg-gradient-to-t from-white to-transparent"
        />
      </div>
      <button
        type="button"
        onClick={onOpen}
        className="mt-3 inline-flex items-center gap-1.5 text-[11px] font-bold uppercase tracking-widest text-brand-primary/60 transition-colors hover:text-brand-primary focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand-blue focus-visible:ring-offset-2 rounded-sm"
      >
        {readMoreLabel}
        <ArrowUpRight className="h-3.5 w-3.5" />
      </button>
    </div>
  );
}

/* detail section — a soft eyebrow heading over a top-bordered content block.
   The shared frame for "Tárgyszavak", "A műről" and "Kapcsolódó" so the left
   column reads as a consistent stack of labelled groups. `contentClassName`
   overrides the default top padding (e.g. the related list pads its own rows). */
function DetailSection({
  heading,
  children,
  contentClassName = 'pt-3',
}: {
  heading: string;
  children: ReactNode;
  contentClassName?: string;
}) {
  return (
    <div>
      <Text as="div" variant="eyebrow" tone="soft" className="mb-2">
        {heading}
      </Text>
      <div className={cn('border-t border-brand-primary/10', contentClassName)}>{children}</div>
    </div>
  );
}

const CreationDetails: React.FC<{ creation: CreationPageItem }> = ({ creation }) => {
  const tDetails = useTranslations('creationPage.details');
  const tHowItWasMade = useTranslations('creationPage.howItWasMade');
  const tMedia = useTranslations('creationPage.media');
  const tAccessibility = useTranslations('accessibility');
  const locale = useLocale();
  const { openVideo } = useVideoPlayer();
  const description = creation.leiras?.trim() ? creation.leiras : null;
  const [mapOpen, setMapOpen] = useState(false);
  const [descriptionOpen, setDescriptionOpen] = useState(false);
  // Resolve the canonical share URL on the client (locale-aware, includes query).
  const [shareUrl, setShareUrl] = useState('');
  useEffect(() => {
    if (typeof window !== 'undefined') setShareUrl(window.location.href);
  }, []);

  const mediaImages = useMemo(
    () =>
      [creation.fokep, ...(creation.tovabbiKepek ?? [])].filter((image): image is ImageObject =>
        Boolean(image?.id)
      ),
    [creation.fokep, creation.tovabbiKepek]
  );

  const mediaVideo = useMemo(() => {
    const video = creation.hivatkozasLista?.find(
      (item) =>
        item.tipus === 'alkotasrol_szolo_video' &&
        item.status === 'public' &&
        Boolean(item.hivatkozas)
    );

    if (!video) return null;

    return {
      id: video.hivatkozas,
      source: video.forrasa,
      rightsHolder: video.jogtulajdonos,
    };
  }, [creation.hivatkozasLista]);

  const makingOfVideo = useMemo(() => {
    const video = creation.hivatkozasLista?.find(
      (item) =>
        item.tipus === 'alkotas_video' && item.status === 'public' && Boolean(item.hivatkozas)
    );

    return video?.hivatkozas ?? null;
  }, [creation.hivatkozasLista]);
  // Gate the making-of card on a resolvable YouTube id (the modal player needs
  // it); the hq still doubles as the card thumbnail.
  const makingOfYouTubeId = useMemo(
    () => (makingOfVideo ? extractYouTubeId(makingOfVideo) : null),
    [makingOfVideo]
  );

  const makingOfThumbnail = makingOfYouTubeId
    ? `https://i.ytimg.com/vi/${makingOfYouTubeId}/hqdefault.jpg`
    : null;
  // "{artist} {profession} <bold>{title}</bold> című alkotásáról beszél…" — the
  // title is emphasised via the <bold> chunk; styling lives on the card.
  const creatorName = getCreationCreatorDisplayName(creation);
  const modalTitle = [creatorName, creation.nev].filter(Boolean).join(': ');

  const makingOfDescription = tHowItWasMade.rich('description', {
    artist: getCreationCreatorDisplayName(creation),
    profession: getCreationCreatorProfessionDisplayName(creation),
    title: creation.nev,
    bold: (chunks) => <strong>{chunks}</strong>,
  });
  // gallery (right column) — only real images/video; the making-of card lives
  // in the left column now, so it must not drive the two-column layout
  const hasGallery = mediaImages.length > 0 || Boolean(mediaVideo);

  const hasMap = Boolean(creation.keletkezesHelyeKoordinatak?.trim());
  // static still of the place of origin — same custom style as the live map, so
  // the thumbnail and the modal read as the same place (frozen vs. interactive).
  const mapThumbnail = useMemo(
    () => buildStaticMapUrl({ coordinates: creation.keletkezesHelyeKoordinatak }),
    [creation.keletkezesHelyeKoordinatak]
  );
  const mapData = useMemo(
    () =>
      hasMap
        ? convertMapData(
            [
              {
                alkotasAzonosito: creation.alkotasAzonosito,
                alkoto: creation.alkoto,
                cardType: 'creation',
                fokep: creation.fokep?.key ?? null,
                fokepId: creation.fokep?.id ?? null,
                fokepNev: creation.fokep?.fileName ?? null,
                hivatkozas: null,
                id: creation.fokep?.id ?? 0,
                koordinatak: creation.keletkezesHelyeKoordinatak,
                label: creation.label,
                labelYear: null,
                nev: creation.nev ?? null,
                telepules: creation.keletkezesHelyeCity ?? null,
                kepek: [],
                hivatkozasLista: [],
              },
            ],
            ['labelYear']
          )
        : [],
    [hasMap, creation]
  );

  /* spec rail — attributes ordered ki? → mit? → hol? → egyéb, then place of
     origin, then the identifier. Attributes are grouped/relabelled (typos →
     canonical); the literal `NULL`-named ones surface as "Egyéb". Place of
     origin comes from a separate field; it gets a map pin and links to a
     town-scoped search like place names elsewhere on the site. */
  const specs = useMemo<Spec[]>(() => {
    const rows: Spec[] = [...creation.attributumok]
      .filter((attr) => attr.attributumErtek?.trim())
      .sort((a, b) => compareCreationAttributeKeys(a.attributumNeve, b.attributumNeve))
      .map((attr) => ({
        label: getCreationAttributeLabel(attr.attributumNeve, locale),
        value: attr.attributumErtek,
      }));

    const city = creation.keletkezesHelyeCity?.trim();
    if (city) {
      rows.push({
        label: tDetails('placeOfOrigin'),
        value: city,
        href: getSearchUrl(city, 'alkotas', null, city, 'telepules'),
      });
    }

    rows.push({
      label: tDetails('identifier'),
      value: creation.alkotasAzonosito,
      mono: true,
      copyable: true,
    });
    return rows;
  }, [
    creation.attributumok,
    creation.keletkezesHelyeCity,
    creation.alkotasAzonosito,
    tDetails,
    locale,
  ]);

  const isLongDescription = useMemo(
    () => (description ? removeHtmlTags(description).length > LONG_DESCRIPTION_CHARS : false),
    [description]
  );

  // plain-text lead-in for the collapsed long-description teaser
  const descriptionPreview = useMemo(() => {
    if (!description) return '';
    const withBreaks = description
      .replace(/<br\s*\/?>/gi, '\n')
      .replace(/<\/p>/gi, '\n\n')
      .replace(/<p[^>]*>/gi, '');
    const text = removeHtmlTags(withBreaks)
      .replace(/\n{3,}/g, '\n\n')
      .trim();
    return text.length > 220 ? `${text.slice(0, 220).trim()}…` : text;
  }, [description]);

  return (
    <Section surface="white" spacing="compact">
      <div
        className={cn(
          'grid items-start gap-8',
          hasGallery ? 'lg:grid-cols-[2fr_3fr]' : 'lg:grid-cols-1'
        )}
      >
        {/* LEFT — all details. Without a gallery this is the whole page, so it is
            held to a readable measure instead of sprawling full-width. */}
        <div
          className={cn(
            'flex min-w-0 flex-col gap-8 order-2 lg:order-1',
            !hasGallery && 'lg:max-w-4xl'
          )}
        >
          {/* title cluster — favourite sits top-right, like a "save" affordance,
              kept apart from the share row below */}
          <header className="flex items-start justify-between gap-4">
            <div className="min-w-0 space-y-3">
              {/* <Eyebrow tone="soft">{tDetails('branch')}</Eyebrow> */}
              <Heading as="h1" variant="subsectionTitle">
                {creation.nev}
              </Heading>

              {creation.label && (
                <Text as="div" variant="lead" tone="muted">
                  {creation.label}
                </Text>
              )}
            </div>
            <FavoriteCreationButton creation={creation} className="shrink-0" />
          </header>

          {creation.alkoto?.length ? (
            <div className="flex flex-wrap gap-x-5 gap-y-2">
              {creation.alkoto.map((creator) => {
                const creatorIdentity = {
                  alkotoAzonosito: creator.alkotoAzonosito,
                  nev: creator.megjelenitendoNev,
                  szakma: creator.szakma,
                  profilkep: creator.profilkep,
                };

                return (
                  <CreatorIdentityRow
                    key={creator.alkotoAzonosito || creator.megjelenitendoNev}
                    creator={creatorIdentity}
                    size="xl"
                    className="w-fit"
                    href={
                      creator.alkotoAzonosito
                        ? { pathname: '/alkoto/[id]', params: { id: creator.alkotoAzonosito } }
                        : undefined
                    }
                  />
                );
              })}
            </div>
          ) : null}

          <SpecList items={specs} columns={hasGallery ? 1 : 2} />

          {creation.targyszo && creation.targyszo.length > 0 && (
            <DetailSection heading={tDetails('keywords')}>
              <div className="flex flex-wrap gap-2">
                {creation.targyszo.map((targyszo) => (
                  <KeywordTag
                    key={targyszo.id}
                    label={targyszo.nev}
                    href={{
                      pathname: '/kereses',
                      query: {
                        tipus: 'alkotas',
                        targyszo: targyszo.id,
                        cim: targyszo.nev,
                      },
                    }}
                  />
                ))}
              </div>
            </DetailSection>
          )}

          {/* short note flows in-column; long prose collapses to a teaser that
              opens the full text in a modal */}
          {description && (
            <DetailSection heading={tMedia('descriptionEyebrow')}>
              {isLongDescription ? (
                <DescriptionTeaser
                  preview={descriptionPreview}
                  readMoreLabel={tMedia('readMore')}
                  onOpen={() => setDescriptionOpen(true)}
                />
              ) : (
                <CreationDescription html={description} />
              )}
            </DetailSection>
          )}

          {/* related — making-of video + place-of-origin map as a compact list
              under one heading; each row opens the live thing in a modal */}
          {(makingOfYouTubeId || hasMap) && (
            <DetailSection
              heading={tMedia('relatedHeading')}
              contentClassName="divide-y divide-brand-primary/10"
            >
              {makingOfYouTubeId && (
                <RelatedItem
                  thumbnailSrc={makingOfThumbnail}
                  fallbackIcon={<Play className="h-5 w-5 fill-current" aria-hidden="true" />}
                  title={tHowItWasMade('title')}
                  subtitle={makingOfDescription}
                  subtitleLines={2}
                  showPlayOverlay
                  ariaLabel={tAccessibility('playVideo', { title: tHowItWasMade('title') })}
                  onClick={() =>
                    openVideo({ videoId: makingOfVideo!, title: tHowItWasMade('title') })
                  }
                />
              )}

              {hasMap && (
                <RelatedItem
                  thumbnailSrc={mapThumbnail}
                  fallbackIcon={<MapPin className="h-6 w-6" aria-hidden="true" />}
                  title={tMedia('mapTitle')}
                  subtitle={tMedia('mapAction')}
                  ariaLabel={tMedia('mapOpen')}
                  onClick={() => setMapOpen(true)}
                />
              )}
            </DetailSection>
          )}

          {/* share — page-level action, parked at the end as its own labelled
              section so it matches the rest of the stack */}
          {shareUrl && (
            <DetailSection heading={tMedia('shareHeading')}>
              <div className="flex flex-wrap items-center gap-3">
                <ShareButtons
                  url={shareUrl}
                  title={creation.nev ?? ''}
                  styleVariant="minimal"
                  showFacebook
                  showEmail
                  showCopyLink
                  showNativeShare
                />
              </div>
            </DetailSection>
          )}
        </div>

        {/* RIGHT — gallery. Sticks while the taller details column scrolls past,
            so a long left side no longer leaves the image stranded at the top. */}
        {hasGallery && (
          <div className="min-w-0 order-1 lg:order-2 lg:sticky lg:top-24 lg:self-start">
            <CreationMediaGallery images={mediaImages} video={mediaVideo} title={creation.nev} />
          </div>
        )}
      </div>

      {/* map opens here at full size; the live map mounts only while open */}
      {hasMap && (
        <Modal
          isOpen={mapOpen}
          onClose={() => setMapOpen(false)}
          title={modalTitle}
          className="h-[85vh] w-[92vw] max-w-[1100px] overflow-hidden rounded-lg"
        >
          <div className="h-full w-full">
            <CreationMap
              data={mapData}
              fitBoundsKey={creation.alkotasAzonosito}
              enablePointCards={false}
            />
          </div>
        </Modal>
      )}

      {/* the full long description lives in a modal instead of a full-width band */}
      {description && isLongDescription && (
        <Modal
          isOpen={descriptionOpen}
          onClose={() => setDescriptionOpen(false)}
          title={modalTitle}
          className="w-[92vw] max-w-[760px] overflow-hidden rounded-lg"
        >
          <div className="px-6 py-6">
            <CreationDescription html={description} />
          </div>
        </Modal>
      )}
    </Section>
  );
};

export default CreationDetails;
