import { getFormProps, useForm } from "@conform-to/react";
import { parseWithZod } from "@conform-to/zod";
import { EnvelopeIcon } from "@heroicons/react/20/solid";
import { type ActionFunction, type LoaderFunctionArgs, json, redirect } from "@remix-run/node";
import { Form, useActionData } from "@remix-run/react";
import { typedjson, useTypedLoaderData } from "remix-typedjson";
import simplur from "simplur";
import { z } from "zod";
import { BackgroundWrapper } from "~/components/BackgroundWrapper";
import { AppContainer, MainCenteredContainer } from "~/components/layout/AppLayout";
import { Button } from "~/components/primitives/Buttons";
import { Fieldset } from "~/components/primitives/Fieldset";
import { FormError } from "~/components/primitives/FormError";
import { FormTitle } from "~/components/primitives/FormTitle";
import { Header2 } from "~/components/primitives/Headers";
import { InputGroup } from "~/components/primitives/InputGroup";
import { Paragraph } from "~/components/primitives/Paragraph";
import {
  acceptInvite,
  declineInvite,
  ENV_SETUP_INCOMPLETE,
  getUsersInvites,
  isAcceptInviteFormError,
} from "~/models/member.server";
import { redirectWithErrorMessage, redirectWithSuccessMessage } from "~/models/message.server";
import { requireUser } from "~/services/session.server";
import { invitesPath, rootPath } from "~/utils/pathBuilder";

export const loader = async ({ request }: LoaderFunctionArgs) => {
  const user = await requireUser(request);

  //if there are no invites left we should redirect to the orgs page
  const invites = await getUsersInvites({ email: user.email });
  if (invites.length === 0) {
    throw redirect(rootPath());
  }

  return typedjson({ invites });
};

const schema = z.object({
  inviteId: z.string(),
  organizationId: z.string().optional(),
});

export const action: ActionFunction = async ({ request }) => {
  const user = await requireUser(request);

  const formData = await request.formData();
  const submission = parseWithZod(formData, { schema });

  if (submission.status !== "success") {
    return json(submission.reply());
  }

  const intent = formData.get("intent");

  try {
    if (intent === "accept") {
      const { remainingInvites, organization } = await acceptInvite({
        inviteId: submission.value.inviteId,
        organizationId: submission.value.organizationId,
        user: { id: user.id, email: user.email },
      });

      if (remainingInvites.length === 0) {
        return redirectWithSuccessMessage(rootPath(), request, `You joined ${organization.title}`);
      } else {
        return redirectWithSuccessMessage(
          invitesPath(),
          request,
          `You joined ${organization.title}`
        );
      }
    } else if (intent === "decline") {
      const { remainingInvites, organization } = await declineInvite({
        inviteId: submission.value.inviteId,
        user: { id: user.id, email: user.email },
      });
      if (remainingInvites.length === 0) {
        return redirectWithSuccessMessage(
          rootPath(),
          request,
          `You declined the invite for ${organization.title}`
        );
      } else {
        return redirectWithSuccessMessage(
          invitesPath(),
          request,
          `You declined the invite for ${organization.title}`
        );
      }
    }
  } catch (error) {
    if (isAcceptInviteFormError(error)) {
      // Membership may already exist while the invite is still present if env
      // provisioning failed. With no invites left, the loader would redirect
      // and discard a 400 FormError — send the user to orgs with a toast instead.
      if (error.message === ENV_SETUP_INCOMPLETE) {
        const remainingInvites = await getUsersInvites({ email: user.email });
        if (remainingInvites.length === 0) {
          return redirectWithErrorMessage(rootPath(), request, error.message, {
            ephemeral: false,
          });
        }
      }

      return json(
        {
          intent: intent,
          payload: submission.payload,
          error: { "": [error.message] },
        },
        { status: 400 }
      );
    }
    throw error;
  }
};

export default function Page() {
  const { invites } = useTypedLoaderData<typeof loader>();
  const lastSubmission = useActionData();

  const [form, _fields] = useForm({
    id: "accept-invite",
    // TODO: type this
    lastResult: lastSubmission as any,
    onValidate({ formData }) {
      return parseWithZod(formData, { schema });
    },
  });

  return (
    <AppContainer className="bg-background-deep">
      <BackgroundWrapper>
        <MainCenteredContainer
          variant="onboarding"
          className="max-w-104 rounded-lg border border-grid-bright bg-background-dimmed p-5 shadow-lg"
        >
          <div>
            <FormTitle
              LeadingIcon={<EnvelopeIcon className="size-6 text-cyan-500" />}
              className="mb-0 text-sky-500"
              title={simplur`You have ${invites.length} new invitation[|s]`}
            />
            <FormError>{form.errors}</FormError>
            {invites.map((invite) => (
              <Form key={invite.id} method="post" {...getFormProps(form)}>
                <Fieldset>
                  <InputGroup className="flex items-center justify-between border-b border-background-bright py-4">
                    <div className="flex flex-col gap-y-0.5 overflow-hidden">
                      <Header2 className="truncate">{invite.organization.title}</Header2>
                      <Paragraph variant="small" className="truncate">
                        Invited by {invite.inviter.displayName ?? invite.inviter.email}
                      </Paragraph>
                      <input name="inviteId" type="hidden" value={invite.id} />
                      <input name="organizationId" type="hidden" value={invite.organizationId} />
                    </div>
                    <div className="flex flex-col gap-y-1">
                      <Button type="submit" name="intent" value="accept" variant={"primary/small"}>
                        Accept
                      </Button>
                      <Button
                        type="submit"
                        name="intent"
                        value="decline"
                        variant={"secondary/small"}
                      >
                        Decline
                      </Button>
                    </div>
                  </InputGroup>
                </Fieldset>
              </Form>
            ))}
          </div>
        </MainCenteredContainer>
      </BackgroundWrapper>
    </AppContainer>
  );
}
