import { unmaskFragment } from "@/gql";
import type { ImageFragment, SearchQuery } from "@/gql/graphql";
import { ImageFragmentDefinition, stripHtml } from "@/lib/common";
import { formatDate, formatOccurrenceMonths } from "@/lib/date";
import Link from "next/link";
import { Image } from "../general/Image";
import styles from "./searchContainer.module.scss";

export type SearchResult = SearchQuery["results"][number];

const PAGE_TYPES = {
  NewsPage: "Nyhet",
  EventPage: "Arrangement",
  GenericPage: "Underside",
  VenuePage: "Lokale",
  AssociationPage: "Forening",
} as const;

type SupportedTypename = keyof typeof PAGE_TYPES;
type SupportedResult = Extract<SearchResult, { __typename: SupportedTypename }>;

function capitalizeFirstLetter(s: string) {
  return s.charAt(0).toUpperCase() + s.slice(1);
}

function isSupported(result: SearchResult): result is SupportedResult {
  return result.__typename in PAGE_TYPES && "id" in result && !!result.id;
}

function getResultType(result: SupportedResult): string {
  if (result.__typename === "AssociationPage" && result.associationType) {
    return capitalizeFirstLetter(result.associationType);
  }
  return PAGE_TYPES[result.__typename];
}

function getResultImage(result: SupportedResult): ImageFragment | null {
  switch (result.__typename) {
    case "NewsPage":
    case "EventPage":
    case "VenuePage":
      return unmaskFragment(ImageFragmentDefinition, result.featuredImage);
    case "AssociationPage":
      return unmaskFragment(ImageFragmentDefinition, result.logo);
    default:
      return null;
  }
}

function getResultDate(result: SupportedResult): string | null {
  if (result.__typename === "EventPage") {
    const starts = result.occurrences
      .map((o) => o.start)
      .filter((s): s is string => !!s);
    if (starts.length === 0) return null;
    if (starts.length === 1) return formatDate(starts[0], "d. MMMM yyyy");
    return formatOccurrenceMonths(starts);
  }
  if (result.__typename === "NewsPage" && result.firstPublishedAt) {
    return formatDate(result.firstPublishedAt, "d. MMMM yyyy");
  }
  return null;
}

function getResultSnippet(result: SupportedResult): string | null {
  switch (result.__typename) {
    case "NewsPage":
    case "AssociationPage":
      return result.excerpt ?? null;
    case "EventPage":
      return result.subtitle ?? null;
    case "GenericPage":
      return result.lead ? stripHtml(result.lead).trim() : null;
    default:
      return null;
  }
}

function highlight(text: string, query: string): React.ReactNode {
  if (query.length < 2) return text;
  const escaped = query.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
  const pattern = new RegExp(escaped, "gi");
  const nodes: React.ReactNode[] = [];
  let lastIndex = 0;
  for (const match of text.matchAll(pattern)) {
    if (match.index > lastIndex) {
      nodes.push(text.slice(lastIndex, match.index));
    }
    nodes.push(<mark key={`m-${match.index}`}>{match[0]}</mark>);
    lastIndex = match.index + match[0].length;
  }
  if (lastIndex < text.length) {
    nodes.push(text.slice(lastIndex));
  }
  return nodes;
}

export function SearchResults({
  results,
  totalCount,
  query,
}: {
  results: SearchResult[];
  totalCount: number;
  query: string;
}) {
  if (!results.length) {
    return (
      <div className={styles.noResults} aria-live="polite">
        <p className={styles.noResultsHeading}>Ingen treff på «{query}»</p>
      </div>
    );
  }
  const supportedResults = results.filter(isSupported);
  const truncated = totalCount > results.length;
  return (
    <div>
      <p className={styles.resultsCounter} aria-live="polite">
        {truncated
          ? `Viser de første ${results.length} av ${totalCount} treff — prøv et mer spesifikt søk.`
          : `${results.length} resultater`}
      </p>
      {supportedResults.map((result) => (
        <ResultRow key={result.id} result={result} query={query} />
      ))}
    </div>
  );
}

function ResultRow({
  result,
  query,
}: {
  result: SupportedResult;
  query: string;
}) {
  const image = getResultImage(result);
  const snippet = getResultSnippet(result);
  const date = getResultDate(result);
  const resultType = getResultType(result);
  const link = result.url;

  const body = (
    <div className={styles.resultItem}>
      <div className={styles.resultBody}>
        <span className={styles.suphead}>{resultType}</span>
        <h2 className={styles.title}>{highlight(result.title, query)}</h2>
        {date && <p className={styles.date}>{date}</p>}
        {snippet && (
          <p className={styles.snippet}>{highlight(snippet, query)}</p>
        )}
      </div>
      {image?.url && (
        <div className={styles.thumb}>
          <Image
            src={image.url}
            alt={image.alt ?? ""}
            width={image.width}
            height={image.height}
            sizes="100px"
          />
        </div>
      )}
    </div>
  );

  if (link) {
    return <Link href={link}>{body}</Link>;
  }
  return body;
}
