import {
  ActionFunctionArgs,
  LoaderFunctionArgs,
  redirect,
  useLoaderData,
} from "react-router-dom";
// TODO: https://github.com/Daninet/hash-wasm probably
// since this one wasn't updated in 3 years
import sha256 from "sha256-wasm";
import {
  createDiscordChannelPageURL,
  createFileSearchPageURL,
} from "#lib/urls";
import { fetchSearchFileByHash } from "#api/files";
import { PageSkeleton } from "#components/pages";
import { CardList, PostCard } from "#components/cards";
import { KemonoLink } from "#components/links";
import { FileSearchForm } from "#entities/files";

interface IProps {
  hash?: string;
  result?: Awaited<ReturnType<typeof fetchSearchFileByHash>>;
}

export function SearchFilesPage() {
  const { hash, result } = useLoaderData() as IProps;
  const title = "Search files";
  const heading = "Search Files";

  return (
    <PageSkeleton name="file-hash-search" title={title} heading={heading}>
      <FileSearchForm id="file-search" hash={hash} />

      {!result?.posts.length ? (
        <div className="no-results">
          <h2 className="site-section__subheading">
            Nobody here but us chickens!
          </h2>
          <p className="subtitle">There are no posts for your query.</p>
        </div>
      ) : (
        <CardList>
          {result.posts.map((post, index) => (
            <PostCard
              key={index}
              // @ts-expect-error custom endpoint meme type
              post={post}
            />
          ))}
        </CardList>
      )}

      {result?.discord_posts && result?.discord_posts.length !== 0 && (
        <>
          <h2>Discord</h2>
          {result.discord_posts.map((post, index) => (
            <p key={index}>
              <KemonoLink
                url={String(
                  createDiscordChannelPageURL(post.server, post.channel)
                )}
              >
                Server {post.server} channel {post.channel}
              </KemonoLink>
            </p>
          ))}
        </>
      )}
    </PageSkeleton>
  );
}

export async function loader({ request }: LoaderFunctionArgs): Promise<IProps> {
  const searchParams = new URL(request.url).searchParams;

  const hash = searchParams.get("hash")?.trim();

  if (!hash) {
    return {};
  }

  const result = await fetchSearchFileByHash(hash);

  return { hash, result };
}

export async function action({ request }: ActionFunctionArgs) {
  if (request.method !== "POST") {
    throw new Error("Unknown method");
  }

  const data = await request.formData();

  const file = data.get("file") as File | null;
  let hash = data.get("hash") as string | null;

  if (!file && !hash) {
    throw new Error("Neither file nor hash is provided");
  }

  if (file) {
    hash = await getFileHash(file);
  }

  if (hash && !hash.match(/[A-Fa-f0-9]{64}/)) {
    throw new Error("Hash is not a valid SHA256 value.");
  }

  await fetchSearchFileByHash(hash as string);

  return redirect(String(createFileSearchPageURL(hash as string)));
}

async function getFileHash(file: File) {
  const fileSize = file.size;
  const chunkSize = 1024 * 1024; // 1Mi
  let offset = 0;
  let hash = new sha256();

  while (offset < fileSize) {
    const arr = new Uint8Array(
      await file.slice(offset, chunkSize + offset).arrayBuffer()
    );
    hash.update(arr);
    offset += chunkSize;
  }

  return hash.digest("hex");
}
