import type { Metadata } from "next";
import Image from "next/image";
import Link from "next/link";
import { notFound } from "next/navigation";
import { Suspense } from "react";
import { ChevronRight } from "lucide-react";

import { CastGrid, DetailHero } from "@/components/media/detail-hero";
import { ScrollRail } from "@/components/media/scroll-rail";
import { ContentRow } from "@/components/home/content-row";
import {
  getSeasons,
  getTvCredits,
  getTvDetail,
  getTvRecommendations,
  getTvSimilar,
} from "@/services/tmdb/titles";
import { isValidTmdbId, watchHref, profileUrl, tmdbImage } from "@/lib/utils";
import { buildTvJsonLd, socialImages } from "@/lib/seo";

export const revalidate = 3600;

export async function generateMetadata({
  params,
}: {
  params: Promise<{ id: string }>;
}): Promise<Metadata> {
  const { id } = await params;
  if (!isValidTmdbId(id)) return { title: "Not found" };

  const show = await getTvDetail(Number(id));
  if (!show) return { title: "Not found" };

  const title = `${show.name} (${show.first_air_date?.slice(0, 4) || "—"})`;
  const description =
    show.overview?.slice(0, 300) ||
    `Watch ${show.name} — ${show.number_of_seasons} seasons on FlixTV.`;

  return {
    title,
    description,
    alternates: { canonical: `/tv/${show.id}` },
    openGraph: {
      type: "video.tv_show",
      title,
      description,
      images: socialImages(show.backdrop_path),
    },
    twitter: {
        card: "summary_large_image",
        title,
        description,
        images: socialImages(show.backdrop_path),
      },
  };
}

export default async function TvDetailPage({
  params,
}: {
  params: Promise<{ id: string }>;
}) {
  const { id } = await params;
  if (!isValidTmdbId(id)) notFound();

  const show = await getTvDetail(Number(id));
  if (!show) notFound();

  const jsonLd = buildTvJsonLd(show);
  const secondary = `${show.number_of_seasons} ${
    show.number_of_seasons === 1 ? "season" : "seasons"
  } • ${show.number_of_episodes} episodes`;

  /*
    The season rail for the download modal.

    Read from TMDB rather than counted, so the rail is right for a two-season
    sitcom and a thirty-season show alike, and stays right when a show gains a
    season. `getSeasons` reads the same cached detail response `getTvDetail`
    already fetched above, so this is a cache read and not a second TMDB call.

    Reduced to the three fields the rail renders — TMDB's season objects also
    carry a poster path, an overview, an air date and a vote average, none of
    which this view shows, and all of which would otherwise be serialised into
    the HTML for nothing.
  */
  const heroSeasons = (await getSeasons(show.id))
    .filter((season) => season.season_number > 0)
    .map((season) => ({
      seasonNumber: season.season_number,
      seasonName: season.name?.trim() || `Season ${season.season_number}`,
      episodeCount: season.episode_count,
    }));

  return (
    <main className="pb-24">
      <script
        type="application/ld+json"
        dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd).replace(/</g, "\\u003c") }}
      />

      <DetailHero
        id={show.id}
        mediaType="tv"
        title={show.name}
        originalTitle={show.original_name}
        tagline={show.tagline ?? null}
        overview={show.overview}
        posterPath={show.poster_path}
        backdropPath={show.backdrop_path}
        logoPath={show.logo_path ?? null}
        releaseDate={show.first_air_date}
        runtimeLabel={
          show.episode_run_time?.[0] ? `${show.episode_run_time[0]}m / episode` : null
        }
        voteAverage={show.vote_average}
        voteCount={show.vote_count}
        status={show.status}
        originalLanguage={show.original_language}
        genres={show.genres}
        companies={show.networks ?? []}
        countries={show.production_countries ?? []}
        watchHref={watchHref("tv", show.id, { season: 1, episode: 1 })}
        noJsHref="/tv"
        secondaryMeta={secondary}
        seasons={heroSeasons}
      />

      {/*
        Seasons / Cast / Recommended / More like this deliberately drop the
        `max-w-*` cap the hero uses: these are rails, so every extra pixel of
        viewport becomes one more card instead of empty margin. Each rail
        carries its own gutters and edge fades.
      */}
      <div className="w-full">
        <Suspense fallback={<SectionSkeleton />}>
          <TvSeasons tmdbId={show.id} />
        </Suspense>

        <Suspense fallback={<SectionSkeleton />}>
          <TvExtras tmdbId={show.id} />
        </Suspense>
      </div>
    </main>
  );
}

/**
 * Season picker.
 *
 * Only the season summaries are fetched here (one cheap request); individual
 * episode lists are loaded by the watch page, which is the only place that
 * needs them. A `still_path` is used as the tile art when available so the
 * picker is not a wall of identical season posters.
 */
async function TvSeasons({ tmdbId }: { tmdbId: number }) {
  const seasons = await getSeasons(tmdbId);
  const real = seasons.filter((s) => s.season_number > 0);

  if (real.length === 0) return null;

  return (
    <section className="mt-12">
      <h2 className="mb-4 flex items-center gap-3 font-display text-lg font-bold tracking-[-0.02em] text-ink md:text-xl">
        <span aria-hidden className="row-tick" />
        Seasons
      </h2>

      {/*
        `ScrollRail` adds the left/right arrows and owns the scroll measurement;
        the season cards are passed through as children, so they still render on
        the server and their width, gap and artwork are untouched.
      */}
      <ScrollRail
        label="Seasons"
        className="no-scrollbar rail -mx-4 flex gap-3.5 overflow-x-auto px-4 pb-1 sm:-mx-6 sm:gap-4 sm:px-6 lg:-mx-10 lg:px-10 xl:-mx-12 xl:px-12"
      >
        {real.map((season) => {
          const still = season.poster_path ? tmdbImage(season.poster_path, "w342") : null;
          const href = watchHref("tv", tmdbId, {
            season: season.season_number,
            episode: 1,
          });

          return (
            <li
              key={season.id}
              className="w-[150px] shrink-0 sm:w-[172px] md:w-[190px] lg:w-[206px]"
            >
              <Link href={href} className="group block">
                <div className="relative aspect-2/3 overflow-hidden rounded-lg bg-surface-2 shadow-card ring-1 ring-white/5 transition-all duration-300 group-hover:ring-white/25">
                  {still ? (
                    <Image
                      src={still}
                      alt=""
                      fill
                      loading="lazy"
                      sizes="206px"
                      className="object-cover transition-transform duration-500 group-hover:scale-105"
                    />
                  ) : (
                    <div className="grid size-full place-items-center bg-linear-to-br from-surface-2 to-surface-3">
                      <span className="font-display text-2xl font-extrabold text-ink-faint">
                        S{season.season_number}
                      </span>
                    </div>
                  )}

                  <span
                    aria-hidden
                    className="pointer-events-none absolute inset-0 bg-linear-to-t from-black/88 via-black/10 to-transparent"
                  />

                  <div className="absolute inset-x-0 bottom-0 p-3">
                    <p className="text-[13px] font-bold text-white">
                      Season {season.season_number}
                    </p>
                    <p className="mt-0.5 flex items-center gap-1 text-[11px] font-medium text-white/65">
                      {season.episode_count} episodes
                      <ChevronRight className="size-3" />
                    </p>
                  </div>
                </div>
              </Link>
              <p className="sr-only">
                Season {season.season_number}, {season.episode_count} episodes
              </p>
            </li>
          );
        })}
      </ScrollRail>
    </section>
  );
}

async function TvExtras({ tmdbId }: { tmdbId: number }) {
  const [credits, recommendations, similar] = await Promise.all([
    getTvCredits(tmdbId),
    getTvRecommendations(tmdbId),
    getTvSimilar(tmdbId),
  ]);

  const cast = (credits?.cast ?? []).slice(0, 18).map((c) => ({
    id: c.id,
    name: c.name,
    character: c.character,
    profilePath: profileUrl(c.profile_path),
  }));

  return (
    <>
      <CastGrid people={cast} />
      {recommendations.length > 0 ? (
        <ContentRow title="Recommended" items={recommendations} variant="poster" />
      ) : null}
      {similar.length > 0 ? (
        <ContentRow title="More like this" items={similar} variant="backdrop" />
      ) : null}
    </>
  );
}

function SectionSkeleton() {
  return (
    <section className="mt-12" aria-busy="true">
      <div className="mb-4 h-6 w-32 rounded-sm bg-surface-2" />
      <div className="flex gap-4 overflow-hidden">
        {Array.from({ length: 8 }, (_, i) => (
          <div key={i} className="aspect-2/3 w-[170px] shrink-0 rounded-lg bg-surface-2" />
        ))}
      </div>
    </section>
  );
}
