import type { Metadata } from "next";
import { notFound, redirect } from "next/navigation";
import { cache } from "react";
import { PortableText } from "@portabletext/react";
import { defineQuery } from "next-sanity";
import { sanity } from "@/lib/sanity";
import { portableTextComponents } from "@/lib/portableTextComponents";
import { ReleaseCover } from "@/components/release/ReleaseCover";
import { AnimatedText } from "@/components/AnimatedText";
import { urlFor } from "@/lib/sanityImage";
import { ARTIST_PLACEHOLDER_SRC } from "@/lib/constants";
import { ArrowLink } from "@/components/ArrowLink";
import { BookletLink } from "@/components/release/BookletLink";
import { Breadcrumb } from "@/components/Breadcrumb";
import { formatYears, type ArtistCardData, type ComposerCardData } from "@/components/artist/types";
import { ArtistCardCompact } from "@/components/artist/ArtistCardCompact";
import { Tracklist, type TrackData } from "@/components/release/Tracklist";
import { DetailTable } from "@/components/release/DetailTable";
import type { SanityImageSource } from "@sanity/image-url";
import { getProductByEan, getProductByCatalogNo } from "@/lib/medusa";
import { matchVariants } from "@/lib/variants";
import { FormatSelector } from "@/components/release/FormatSelector";
import { parseDuration, toISO8601Duration } from "@/lib/duration";

export const dynamicParams = true;
export const revalidate = 86400;

type Review = {
  quote?: string;
  author?: string;
};

type Credit = {
  role?: string;
  name?: string;
};

type EquipmentItem = {
  type?: string;
  name?: string;
};

type ReleaseDetail = {
  name?: string;
  albumArtist?: string;
  label?: string;
  catalogNo?: string;
  upc?: string;
  slug?: string;
  releaseDate?: string;
  shortDescription?: string;
  description?: any;
  albumCover?: SanityImageSource;
  format?: string;
  genre?: string[];
  spotifyUrl?: string;
  appleMusicUrl?: string;
  tidalUrl?: string;
  artists?: ArtistCardData[];
  composers?: ComposerCardData[];
  arrangers?: ComposerCardData[];
  tracks?: TrackData[];
  reviews?: Review[];
  credits?: Credit[];
  recordingDate?: string;
  recordingLocation?: string;
  recordingFormat?: string;
  masteringFormat?: string;
  bookletPdfUrl?: string;
  equipment?: EquipmentItem[];
};

const RELEASE_DETAIL_QUERY = defineQuery(`
*[_type == "release" && slug.current == $slug][0]{
  name,
  albumArtist,
  label,
  catalogNo,
  upc,
  "slug": slug.current,
  releaseDate,
  shortDescription,
  description,
  albumCover,
  format,
  genre[],
  spotifyUrl,
  appleMusicUrl,
  tidalUrl,
  "artists": artists[]->{
    _id,
    name,
    role,
    "slug": slug.current,
    image
  },
  "composers": tracks[].work->composer->{
    _id,
    name,
    sortKey,
    "slug": slug.current,
    birthYear,
    deathYear,
    image
  },
  "arrangers": tracks[].work->arranger->{
    _id,
    name,
    sortKey,
    "slug": slug.current,
    birthYear,
    deathYear,
    image
  },
  tracks[]{
    "workId": work->_id,
    "workTitle": work->title,
    "composerName": work->composer->name,
    "arrangerName": work->arranger->name,
    movement,
    displayTitle,
    duration,
    artist,
    "previewMp3Url": previewMp3.asset->url
  },
  reviews[]{ quote, author },
  credits[]{ role, name },
  recordingDate,
  recordingLocation,
  recordingFormat,
  masteringFormat,
  "bookletPdfUrl": bookletPdf.asset->url,
  equipment[]{ type, name }
}
`);

const getRelease = cache(async (slug: string) => {
  try {
    const release = await sanity.fetch<ReleaseDetail>(RELEASE_DETAIL_QUERY, { slug });
    return release;
  } catch (error) {
    console.error("Failed to fetch release:", error);
    return null;
  }
});

const RELEASE_SLUGS_QUERY = defineQuery(
  `*[_type == "release" && defined(slug.current)]{ "slug": slug.current }`,
);

export async function generateStaticParams() {
  const slugs = await sanity.fetch<Array<{ slug: string }>>(RELEASE_SLUGS_QUERY);

  return slugs.map((r) => ({ slug: r.slug }));
}

export async function generateMetadata({
  params,
}: {
  params: Promise<{ slug: string }>;
}): Promise<Metadata> {
  const { slug: rawSlug } = await params;
  const slug = rawSlug.toLowerCase();

  const release = await getRelease(slug);
  if (!release) notFound();

  const description =
    release.shortDescription ??
    `Listen to ${release.name}${release.albumArtist ? ` by ${release.albumArtist}` : ""} on TRPTK.`;

  const ogImage = release.albumCover
    ? urlFor(release.albumCover).width(1200).height(1200).url()
    : undefined;

  return {
    title: `${release.albumArtist ? `${release.albumArtist} • ` : ""}${release.name}`,
    description,
    alternates: { canonical: `/release/${slug}` },
    openGraph: {
      title: release.name,
      description: `${release.albumArtist || "TRPTK"}${release.label ? ` - ${release.label}` : ""}`,
      type: "music.album",
      ...(ogImage && {
        images: [
          {
            url: ogImage,
            width: 1200,
            height: 1200,
            alt: `${release.name} by ${release.albumArtist}`,
          },
        ],
      }),
    },
    twitter: {
      card: "summary_large_image",
      title: release.name,
      description,
      ...(ogImage && { images: [ogImage] }),
    },
  };
}

function formatReleaseDate(dateString?: string) {
  if (!dateString) return null;
  return new Intl.DateTimeFormat("en-US", {
    day: "numeric",
    month: "long",
    year: "numeric",
  }).format(new Date(dateString));
}

function dedupeById<T extends { _id: string }>(items: (T | null)[]): T[] {
  const seen = new Set<string>();
  return items.filter((item): item is T => {
    if (!item || !item._id || seen.has(item._id)) return false;
    seen.add(item._id);
    return true;
  });
}

export default async function ReleasePage({ params }: { params: Promise<{ slug: string }> }) {
  const { slug } = await params;
  if (!slug) notFound();

  const normalizedSlug = slug.toLowerCase();
  if (slug !== normalizedSlug) {
    redirect(`/release/${normalizedSlug}`);
  }

  const release = await getRelease(slug);
  if (!release) notFound();

  const displayName = release.name ?? "";
  const displayArtist = release.albumArtist ?? "";
  const displayDate = formatReleaseDate(release.releaseDate);
  const artists = release.artists
    ? dedupeById(release.artists).sort((a, b) => (a.name ?? "").localeCompare(b.name ?? ""))
    : [];
  const composers = dedupeById([...(release.composers ?? []), ...(release.arrangers ?? [])]).sort(
    (a, b) => (a.sortKey ?? "").localeCompare(b.sortKey ?? ""),
  );
  const hasCreditsInfo =
    (release.credits && release.credits.length > 0) ||
    release.recordingDate ||
    release.recordingLocation ||
    release.recordingFormat ||
    release.masteringFormat ||
    release.releaseDate ||
    release.bookletPdfUrl;
  const hasEquipment = release.equipment && release.equipment.length > 0;

  // Medusa product lookup — match by UPC/EAN, fallback to catalogue number
  let formatGroups: Awaited<ReturnType<typeof matchVariants>> = [];
  if (release.catalogNo) {
    const product = release.upc
      ? await getProductByEan(release.upc)
      : await getProductByCatalogNo(release.catalogNo);
    if (product) {
      formatGroups = matchVariants(release.catalogNo, product.variants);
    }
  }

  const albumUrl = `https://trptk.com/release/${slug}`;

  const formatMap: Record<string, string> = {
    album: "AlbumRelease",
    ep: "EPRelease",
    single: "SingleRelease",
  };

  const genreLabelMap: Record<string, string> = {
    earlyMusic: "Early Music",
    baroque: "Baroque",
    classical: "Classical",
    romantic: "Romantic",
    contemporary: "Contemporary",
    worldMusic: "World Music",
    jazz: "Jazz",
    crossover: "Crossover",
    electronic: "Electronic",
    minimal: "Minimal",
    popRock: "Pop / Rock",
  };

  const sameAs = [release.spotifyUrl, release.appleMusicUrl, release.tidalUrl].filter(
    Boolean,
  ) as string[];

  const jsonLd = {
    "@context": "https://schema.org",
    "@type": "MusicAlbum",
    "@id": albumUrl,
    name: release.name,
    url: albumUrl,
    ...(release.shortDescription && { description: release.shortDescription }),
    ...(release.albumArtist && {
      byArtist: { "@type": "MusicGroup", name: release.albumArtist },
    }),
    ...(release.albumCover && {
      image: urlFor(release.albumCover).width(800).url(),
    }),
    ...(release.releaseDate && { datePublished: release.releaseDate }),
    ...(release.catalogNo && { catalogNumber: release.catalogNo }),
    ...(release.label === "TRPTK" && {
      recordLabel: { "@type": "Organization", name: "TRPTK" },
    }),
    ...(release.format &&
      formatMap[release.format] && {
        albumReleaseType: `https://schema.org/${formatMap[release.format]}`,
      }),
    ...(release.genre?.length && {
      genre: release.genre.map((g) => genreLabelMap[g] || g),
    }),
    ...(sameAs.length > 0 && { sameAs }),
    ...(release.tracks?.length && {
      numTracks: release.tracks.length,
      track: release.tracks.map((t, i) => {
        const seconds = parseDuration(t.duration);
        const iso = toISO8601Duration(seconds);
        return {
          "@type": "MusicRecording",
          name: t.displayTitle || t.workTitle,
          position: i + 1,
          ...(iso && { duration: iso }),
          inAlbum: { "@id": albumUrl },
        };
      }),
    }),
  };

  return (
    <>
      <script
        type="application/ld+json"
        dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd).replace(/</g, "\\u003c") }}
      />
      <main className="mx-auto my-auto max-w-300 px-6 py-12 font-silka md:px-8 md:py-16 2xl:max-w-400">
        {/* Hero */}
        <div className="mx-auto grid grid-cols-1 gap-10 sm:grid-cols-2 sm:gap-8 md:gap-12 lg:gap-20">
          <div className="flex-1 content-center">
            <ReleaseCover
              src={release.albumCover ? urlFor(release.albumCover).url() : ARTIST_PLACEHOLDER_SRC}
              alt={`Album cover for ${displayName} by ${displayArtist}`}
            />
          </div>

          <div className="flex-1 content-center">
            <Breadcrumb crumbs={[{ label: "Releases", href: "/releases" }]} />
            <AnimatedText
              text={displayName}
              as="h1"
              className="mb-2 font-argesta text-3xl break-words"
            />
            <AnimatedText
              text={displayArtist}
              as="h2"
              className="font-silka text-base break-words text-lightsec dark:text-darksec"
              delay={0.25}
            />
            {formatGroups.length > 0 && (
              <FormatSelector groups={formatGroups} releaseDate={release.releaseDate} />
            )}
          </div>
        </div>

        {/* Description & Tracklist */}
        {(release.description || true) && (
          <section className="mt-16 grid grid-cols-1 gap-10 sm:mt-20 sm:gap-12 md:gap-16 lg:grid-cols-2 lg:gap-20">
            <div>
              <AnimatedText text="About the album" as="h2" className="mb-4 font-argesta text-2xl" />
              {release.description ? (
                <article className="prose max-w-none text-lighttext dark:text-darktext dark:prose-invert prose-h2:!mt-[3em] prose-h2:font-silkasb prose-h2:text-base prose-strong:font-silkasb">
                  <PortableText value={release.description} components={portableTextComponents} />
                </article>
              ) : (
                <p className="text-lightsec dark:text-darksec">No description available yet.</p>
              )}
            </div>

            <div>
              <AnimatedText text="Tracklist" as="h2" className="mb-4 font-argesta text-2xl" />
              <Tracklist
                tracks={release.tracks ?? []}
                albumCover={release.albumCover}
                albumArtist={displayArtist}
                releaseSlug={slug}
              />
            </div>
          </section>
        )}

        <section className="mt-16 grid grid-cols-1 gap-16 sm:mt-20 md:grid-cols-2 md:gap-12 lg:gap-20">
          {/* Artists */}
          {artists.length > 0 && (
            <div className="">
              <AnimatedText text="Artists" as="h2" className="mb-4 font-argesta text-2xl" />
              <ul className="grid grid-cols-1 gap-4 sm:grid-cols-2 md:grid-cols-1 lg:grid-cols-2">
                {artists.map((artist) => (
                  <ArtistCardCompact
                    key={artist._id}
                    name={artist.name}
                    subtitle={artist.role}
                    image={artist.image}
                    href={artist.slug ? `/artist/${artist.slug}` : "/artists/"}
                  />
                ))}
              </ul>
            </div>
          )}

          {/* Composers */}
          {composers.length > 0 && (
            <div className="">
              <AnimatedText text="Composers" as="h2" className="mb-4 font-argesta text-2xl" />
              <ul className="grid grid-cols-1 gap-4 sm:grid-cols-2 md:grid-cols-1 lg:grid-cols-2">
                {composers.map((composer) => (
                  <ArtistCardCompact
                    key={composer._id}
                    name={composer.name}
                    subtitle={formatYears(composer.birthYear, composer.deathYear)}
                    image={composer.image}
                    href={composer.slug ? `/composer/${composer.slug}` : "/composers/"}
                    label="Composer"
                  />
                ))}
              </ul>
            </div>
          )}
        </section>

        {/* Reviews */}
        {release.reviews && release.reviews.length > 0 && (
          <section className="mx-auto my-32 max-w-150 sm:my-40">
            <div className="grid grid-cols-1 gap-12">
              {release.reviews.map((review, i) => (
                <blockquote key={i} className="rounded-xl text-center">
                  {review.quote && (
                    <p className="leading-relaxed text-lighttext dark:text-darktext">
                      &ldquo;{review.quote}&rdquo;
                    </p>
                  )}
                  {review.author && (
                    <footer className="mt-2 text-sm text-lightsec dark:text-darksec">
                      {review.author}
                    </footer>
                  )}
                </blockquote>
              ))}
            </div>
          </section>
        )}

        {/* Credits & Equipment */}
        {(hasCreditsInfo || hasEquipment) && (
          <section className="mt-16 grid grid-cols-1 gap-16 sm:mt-20 md:grid-cols-2 md:gap-12 lg:gap-20">
            {/* Credits */}
            {hasCreditsInfo && (
              <div>
                <AnimatedText text="Credits" as="h2" className="mb-4 font-argesta text-2xl" />
                <DetailTable
                  rows={[
                    ...(release.credits?.map((c) => ({ label: c.role ?? "", value: c.name })) ??
                      []),
                    { label: "Recording date", value: release.recordingDate },
                    { label: "Recording location", value: release.recordingLocation },
                    { label: "Recording format", value: release.recordingFormat },
                    { label: "Mastering format", value: release.masteringFormat },
                    { label: "Release date", value: displayDate ?? undefined },
                    {
                      label: "Booklet",
                      value: release.bookletPdfUrl ? (
                        <BookletLink slug={release.slug!} />
                      ) : undefined,
                    },
                  ]}
                />
              </div>
            )}

            {/* Equipment */}
            {hasEquipment && (
              <div>
                <AnimatedText
                  text="Technical specifications"
                  as="h2"
                  className="mb-4 font-argesta text-2xl"
                />
                <DetailTable
                  rows={release.equipment!.map((item) => ({
                    label: item.type ?? "",
                    value: item.name,
                  }))}
                />
              </div>
            )}
          </section>
        )}
      </main>
    </>
  );
}
