import {
  CONTEXT_KINDS,
  contextPathForKind,
  MAX_CONTEXT_DOCUMENT_CHARACTERS,
  type PreparedPublicGitSource,
  type PublicGitImportResult,
  type RepositoryContextDocument,
} from "@code/primitives/project";

const GITHUB_HOST = "github.com";
const REQUEST_HEADERS = {
  Accept: "application/vnd.github+json",
  "User-Agent": "zopu-project-importer",
} as const;

const candidatePaths = {
  agents: ["AGENTS.md"],
  business: ["business.md", "docs/business.md"],
  design: ["design.md", "docs/design.md"],
  product: ["product.md", "docs/product.md"],
  readme: ["README.md", "README", "readme.md"],
  tech: ["tech.md", "docs/tech.md"],
} as const;

const encodePath = (path: string) =>
  path
    .split("/")
    .map((segment) => encodeURIComponent(segment))
    .join("/");

const fetchTextCandidate = async (
  repositoryPath: string,
  branch: string,
  path: string
): Promise<string | null> => {
  const url = `https://raw.githubusercontent.com/${encodePath(repositoryPath)}/${encodePath(branch)}/${encodePath(path)}`;
  const response = await fetch(url, { headers: REQUEST_HEADERS });
  if (response.status === 404) {
    return null;
  }
  if (!response.ok) {
    throw new Error(`GitHub returned ${response.status} for ${path}`);
  }
  const content = await response.text();
  if (content.trim().length === 0) {
    return null;
  }
  if (content.length > MAX_CONTEXT_DOCUMENT_CHARACTERS) {
    throw new Error(`${path} exceeds the 200000 character import limit`);
  }
  return content;
};

const fetchContextDocument = async (
  source: PreparedPublicGitSource,
  branch: string,
  kind: (typeof CONTEXT_KINDS)[number]
): Promise<RepositoryContextDocument | null> => {
  for (const candidate of candidatePaths[kind]) {
    const content = await fetchTextCandidate(
      source.repositoryPath,
      branch,
      candidate
    );
    if (content) {
      return {
        content,
        kind,
        path: contextPathForKind(kind),
      };
    }
  }
  return null;
};

const readDefaultBranch = async (
  source: PreparedPublicGitSource
): Promise<string> => {
  if (source.host !== GITHUB_HOST) {
    throw new Error("Repository import currently supports public GitHub URLs");
  }
  const response = await fetch(
    `https://api.github.com/repos/${encodePath(source.repositoryPath)}`,
    { headers: REQUEST_HEADERS }
  );
  if (!response.ok) {
    throw new Error(
      response.status === 404
        ? "Public GitHub repository not found"
        : `GitHub returned ${response.status} while reading the repository`
    );
  }
  const metadata = (await response.json()) as { default_branch?: unknown };
  if (
    typeof metadata.default_branch !== "string" ||
    metadata.default_branch.length === 0
  ) {
    throw new Error("GitHub did not return a default branch");
  }
  return metadata.default_branch;
};

export const inspectPublicGit = async (
  source: PreparedPublicGitSource
): Promise<PublicGitImportResult> => {
  const defaultBranch = await readDefaultBranch(source);
  const candidates = await Promise.all(
    CONTEXT_KINDS.map((kind) =>
      fetchContextDocument(source, defaultBranch, kind)
    )
  );
  const documents = candidates.filter(
    (document): document is RepositoryContextDocument => document !== null
  );
  return {
    defaultBranch,
    documents,
    warnings: CONTEXT_KINDS.filter(
      (kind) => !documents.some((document) => document.kind === kind)
    ).map((kind) => ({
      message: "Canonical context file was not found in the repository",
      path: contextPathForKind(kind),
    })),
  };
};
