import type { Metadata } from "next";
import { notFound, redirect } from "next/navigation";
import { Suspense } from "react";

import {
  LatestRelease,
  LatestReleaseSkeleton,
} from "@/components/releases/latest-release";
import { RepositoryAbout } from "@/components/repositories/repository-about";
import { RepositoryFrame } from "@/components/repositories/repository-frame";
import {
  RepositoryLanguages,
  RepositoryLanguagesSkeleton,
} from "@/components/repositories/repository-languages";
import { createServerClient, getServerSession } from "@/lib/api/server";

import { ImportBanner } from "./import-banner";

export async function generateMetadata({
  params,
}: LayoutProps<"/[username]/[repo]">): Promise<Metadata> {
  const { username, repo } = await params;
  const client = await createServerClient();
  const { data } = await client.GET("/api/repositories/{username}/{slug}", {
    params: { path: { username, slug: repo } },
  });

  const title = `${username}/${data?.slug ?? repo}`;
  const description = data?.description ?? `${title} on Ghost`;

  return {
    title,
    description,
    openGraph: { title, description, url: `/${username}/${repo}` },
  };
}

export default async function RepositoryLayout({
  params,
  children,
}: LayoutProps<"/[username]/[repo]">) {
  const { username, repo } = await params;

  const [session, client] = await Promise.all([
    getServerSession(),
    createServerClient(),
  ]);

  const repository = await client.GET("/api/repositories/{username}/{slug}", {
    params: { path: { username, slug: repo } },
  });

  if (repository.response.status === 404) notFound();
  if (repository.error || !repository.data) {
    throw new Error(`Failed to load ${username}/${repo}`);
  }
  // ponytail: an old name after a rename or transfer redirects to the repository root, since a layout does not know the rest of the path; a proxy could keep it.
  if (repository.data.owner !== username || repository.data.slug !== repo) {
    redirect(`/${repository.data.owner}/${repository.data.slug}`);
  }

  const [branches, pulls, issues, watch, imported] = await Promise.all([
    client.GET("/api/repositories/{username}/{slug}/branches", {
      params: { path: { username, slug: repo } },
    }),
    client.GET("/api/repositories/{username}/{repo}/pulls", {
      params: { path: { username, repo }, query: { state: "open", limit: 1 } },
    }),
    client.GET("/api/repositories/{username}/{repo}/issues", {
      params: { path: { username, repo }, query: { state: "open", limit: 1 } },
    }),
    session
      ? client.GET("/api/repositories/{username}/{repo}/subscription", {
          params: { path: { username, repo } },
        })
      : null,
    client.GET("/api/repositories/{username}/{repo}/import", {
      params: { path: { username, repo } },
    }),
  ]);

  return (
    <RepositoryFrame
      viewer={session?.user.username ?? ""}
      viewerRole={repository.data.viewerRole}
      username={username}
      slug={repository.data.slug}
      name={repository.data.name}
      description={repository.data.description}
      visibility={repository.data.visibility}
      defaultBranch={branches.data?.defaultBranch ?? null}
      branches={branches.data?.branches}
      starCount={repository.data.starCount}
      viewerHasStarred={repository.data.viewerHasStarred}
      watchLevel={watch?.data?.level ?? null}
      forkCount={repository.data.forkCount}
      openPullRequestCount={pulls.data?.total}
      openIssueCount={issues.data?.openCount}
      parent={repository.data.parent}
      sidebar={
        <>
          <RepositoryAbout
            username={username}
            slug={repository.data.slug}
            description={repository.data.description}
            starCount={repository.data.starCount}
            forkCount={repository.data.forkCount}
          />

          <Suspense fallback={<LatestReleaseSkeleton />}>
            <LatestRelease username={username} repo={repository.data.slug} />
          </Suspense>

          {/* its own fetch: a first-ever language count of a big repository
              must not hold up the file listing */}
          <Suspense fallback={<RepositoryLanguagesSkeleton />}>
            <Languages username={username} repo={repo} />
          </Suspense>
        </>
      }
    >
      {imported.data && imported.data.status !== "succeeded" && (
        <ImportBanner
          username={username}
          repo={repository.data.slug}
          viewerRole={repository.data.viewerRole}
          initial={imported.data}
        />
      )}
      {children}
    </RepositoryFrame>
  );
}

async function Languages({
  username,
  repo,
}: {
  username: string;
  repo: string;
}) {
  const client = await createServerClient();
  const { data } = await client.GET(
    "/api/repositories/{username}/{slug}/languages",
    { params: { path: { username, slug: repo } } },
  );

  return data ? <RepositoryLanguages languages={data} /> : null;
}
