import { prisma, type PostWithRelations } from "~/utils/prisma.server";
import type {
  ActionFunction,
  ActionFunctionArgs,
  LoaderFunction,
  LoaderFunctionArgs,
  MetaFunction,
} from "@remix-run/node";
import { json, redirect } from "@remix-run/node";
import {
  Form,
  Link,
  useActionData,
  useLoaderData,
  useRouteLoaderData,
} from "@remix-run/react";
import { Button } from "~/components/Button";
import { Card } from "~/components/Card";
import { FormLabel, TextArea } from "~/components/Form";
import { Post } from "~/components/Post";
import { SubTitle, Text, Title } from "~/components/Typography";
import { like, unLike } from "~/models/like.server";
import { createPost } from "~/models/post.server";
import { commitSession, getSession } from "~/utils/session.server";
import type { RootLoaderTypes } from "~/root";
import { repost, unRepost } from "~/models/repost.server";
import { reply } from "~/models/reply.server";

export const meta: MetaFunction<typeof loader> = () => {
  return [{ title: `Home | Twitter Clone` }];
};

export async function action({ request }: ActionFunctionArgs) {
  const session = await getSession(request.headers.get("Cookie"));
  if (!session.has("userId")) {
    return redirect("/");
  }

  const userId = session.get("userId");

  const formData = await request
    .formData()
    .then((data) => Object.fromEntries(data));

  switch (formData.intent) {
    case "newpost":
      const postErrors = await createPost(String(formData.post), userId);

      if (Object.values(postErrors).some(Boolean)) {
        return postErrors;
      }

      session.flash("globalMessage", "Post created!");

      break;
    case "like":
      const likeErrors = await like(
        Number(formData.postId),
        userId,
        Number(formData.replyId)
      );

      if (Object.values(likeErrors).some(Boolean)) {
        return likeErrors;
      }

      session.flash("globalMessage", "Post liked!");

      break;
    case "unlike":
      const unLikeErrors = await unLike(
        Number(formData.postId),
        userId,
        Number(formData.replyId)
      );

      if (Object.values(unLikeErrors).some(Boolean)) {
        return unLikeErrors;
      }

      session.flash("globalMessage", "Post un-liked!");

      break;
    case "repost":
      const repostErrors = await repost(
        Number(formData.postId),
        userId,
        Number(formData.replyId)
      );

      if (Object.values(repostErrors).some(Boolean)) {
        return repostErrors;
      }

      session.flash("globalMessage", "Post reposted!");

      break;
    case "unrepost":
      const unrepostErrors = await unRepost(
        Number(formData.postId),
        userId,
        Number(formData.replyId)
      );

      if (Object.values(unrepostErrors).some(Boolean)) {
        return unrepostErrors;
      }

      session.flash("globalMessage", "Post un-reposted!");

      break;
    case "reply":
      const replyErrors = await reply(
        String(formData.reply),
        Number(formData.postId),
        Number(formData.replyId),
        userId
      );

      if (Object.values(replyErrors).some(Boolean)) {
        return replyErrors;
      }

      session.flash("globalMessage", "Replied to post!");

      break;
    default:
      return;
  }

  return json(
    {},
    {
      headers: {
        "Set-Cookie": await commitSession(session),
      },
    }
  );
}

export async function loader({ request }: LoaderFunctionArgs) {
  const session = await getSession(request.headers.get("Cookie"));
  if (!session.has("userId")) {
    return redirect("/");
  }
  const userId = session.get("userId");
  const following = await prisma.user.findFirst({
    where: { id: userId },
    select: { following: { select: { id: true } } },
  });
  const followingArr = [...following.following.map((user) => user.id)];

  const feed = await prisma.post.findMany({
    where: {
      OR: [
        {
          userId: {
            in: followingArr,
          },
        },
        {
          reposts: {
            some: {
              userId: {
                in: followingArr,
              },
            },
          },
        },
      ],
    },
    select: {
      id: true,
      text: true,
      createdAt: true,
      author: {
        select: {
          username: true,
          name: true,
          pfp: true,
        },
      },
      reposts: {
        select: {
          userId: true,
          user: {
            select: {
              id: true,
              username: true,
              pfp: true,
              name: true,
            },
          },
        },
      },
      likes: {
        select: {
          userId: true,
          user: {
            select: {
              id: true,
              username: true,
              pfp: true,
              name: true,
            },
          },
        },
      },
      _count: {
        select: {
          replies: {
            where: {
              parentReplyId: null,
            },
          },
        },
      },
    },
    orderBy: {
      createdAt: "desc",
    },
  });

  feed.forEach((post) => {
    const filter = post.reposts.filter((user) =>
      followingArr.includes(user.userId)
    );
    const reposters = filter.map((repost) => repost.user);
    post.reposters = reposters;
  });

  return feed;
}

export default function Index() {
  const rootData = useRouteLoaderData<RootLoaderTypes>("root");
  const data: PostWithRelations[] = useLoaderData<LoaderFunction>();
  const errors = useActionData<ActionFunction>();

  return (
    <div className="flex flex-col gap-5">
      <Title>Home</Title>
      <Card className="!gap-0">
        {rootData?.id ? (
          <>
            <SubTitle>New post</SubTitle>
            <Form method="POST">
              <input type="hidden" name="intent" value={"newpost"} />
              <div className="flex flex-col gap-3">
                <FormLabel>
                  <Text>Post body</Text>
                  <TextArea name="post" />
                  <Text type="error">{errors?.body ? errors.body : ""}</Text>
                </FormLabel>
                <Button type="submit">Post</Button>
              </div>
            </Form>
          </>
        ) : (
          ""
        )}
      </Card>
      <SubTitle>Feed</SubTitle>
      {data.length > 0 ? (
        <Card className="!p-0 !gap-0 divide-y">
          {data.map((post) => (
            <Post userId={rootData?.id} key={post.id} post={post} />
          ))}
        </Card>
      ) : (
        <Text className="flex gap-1" type="error">
          Feed is empty,{" "}
          <Link to={`/users`}>
            <Text type="link">follow someone</Text>
          </Link>{" "}
          so their recent posts will show up here!
        </Text>
      )}
    </div>
  );
}
