import { useForm } from "@conform-to/react";
import { parse } from "@conform-to/zod";
import { LockOpenIcon, UserPlusIcon } from "@heroicons/react/20/solid";
import { Form, useActionData } from "@remix-run/react";
import { ActionFunction, LoaderFunctionArgs, json } from "@remix-run/server-runtime";
import { useState } from "react";
import { UseDataFunctionReturn, typedjson, useTypedLoaderData } from "remix-typedjson";
import invariant from "tiny-invariant";
import { z } from "zod";
import { UserAvatar } from "~/components/UserProfilePhoto";
import { AdminDebugTooltip } from "~/components/admin/debugTooltip";
import { PageBody, PageContainer } from "~/components/layout/AppLayout";
import {
  Alert,
  AlertCancel,
  AlertContent,
  AlertDescription,
  AlertFooter,
  AlertHeader,
  AlertTitle,
  AlertTrigger,
} from "~/components/primitives/Alert";
import { Button, ButtonContent, LinkButton } from "~/components/primitives/Buttons";
import { DateTime } from "~/components/primitives/DateTime";
import { Header2, Header3 } from "~/components/primitives/Headers";
import { InfoPanel } from "~/components/primitives/InfoPanel";
import { NamedIcon } from "~/components/primitives/NamedIcon";
import { NavBar, PageAccessories, PageTitle } from "~/components/primitives/PageHeader";
import { Paragraph } from "~/components/primitives/Paragraph";
import * as Property from "~/components/primitives/PropertyTable";
import { SimpleTooltip } from "~/components/primitives/Tooltip";
import { $replica } from "~/db.server";
import { useOrganization } from "~/hooks/useOrganizations";
import { useUser } from "~/hooks/useUser";
import { removeTeamMember } from "~/models/member.server";
import { redirectWithSuccessMessage } from "~/models/message.server";
import { TeamPresenter } from "~/presenters/TeamPresenter.server";
import { requireUserId } from "~/services/session.server";
import {
  inviteTeamMemberPath,
  organizationTeamPath,
  resendInvitePath,
  revokeInvitePath,
  v3BillingPath,
} from "~/utils/pathBuilder";

const Params = z.object({
  organizationSlug: z.string(),
});

export const loader = async ({ request, params }: LoaderFunctionArgs) => {
  const userId = await requireUserId(request);
  const { organizationSlug } = Params.parse(params);

  const organization = await $replica.organization.findFirst({
    where: { slug: organizationSlug },
    select: { id: true },
  });

  if (!organization) {
    throw new Response("Not Found", { status: 404 });
  }

  const presenter = new TeamPresenter();
  const result = await presenter.call({
    userId,
    organizationId: organization.id,
  });

  if (!result) {
    throw new Response("Not Found", { status: 404 });
  }

  return typedjson(result);
};

const schema = z.object({
  memberId: z.string(),
});

export const action: ActionFunction = async ({ request, params }) => {
  const userId = await requireUserId(request);
  const { organizationSlug } = params;
  invariant(organizationSlug, "organizationSlug not found");

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

  if (!submission.value || submission.intent !== "submit") {
    return json(submission);
  }

  try {
    const deletedMember = await removeTeamMember({
      userId,
      memberId: submission.value.memberId,
      slug: organizationSlug,
    });

    if (deletedMember.userId === userId) {
      return redirectWithSuccessMessage("/", request, `You left the organization`);
    }

    return redirectWithSuccessMessage(
      organizationTeamPath(deletedMember.organization),
      request,
      `Removed ${deletedMember.user.name ?? "member"} from team`
    );
  } catch (error: any) {
    return json({ errors: { body: error.message } }, { status: 400 });
  }
};

type Member = UseDataFunctionReturn<typeof loader>["members"][number];
type Invite = UseDataFunctionReturn<typeof loader>["invites"][number];

export default function Page() {
  const { members, invites, limits } = useTypedLoaderData<typeof loader>();
  const user = useUser();
  const organization = useOrganization();

  const requiresUpgrade = limits.used >= limits.limit;

  return (
    <PageContainer>
      <NavBar>
        <PageTitle title="Team" />

        <PageAccessories>
          <AdminDebugTooltip>
            <Property.Table>
              <Property.Item>
                <Property.Label>Org ID</Property.Label>
                <Property.Value>{organization.id}</Property.Value>
              </Property.Item>

              {members.map((member) => (
                <Property.Item key={member.id}>
                  <Property.Label>{member.user.name}</Property.Label>
                  <Property.Value>
                    <div className="flex items-center gap-2">
                      <Paragraph variant="extra-small/bright/mono">
                        {member.user.email} - {member.user.id}
                      </Paragraph>
                    </div>
                  </Property.Value>
                </Property.Item>
              ))}
            </Property.Table>
          </AdminDebugTooltip>
        </PageAccessories>
      </NavBar>
      <PageBody>
        <Header2>
          Members ({limits.used}/{limits.limit})
        </Header2>
        <ul className="divide-ui-border flex w-full max-w-md flex-col divide-y border-b border-grid-bright">
          {members.map((member) => (
            <li key={member.user.id} className="flex items-center gap-x-4 py-4">
              <UserAvatar
                avatarUrl={member.user.avatarUrl}
                name={member.user.name}
                className="h-10 w-10"
              />
              <div className="flex flex-col gap-0.5">
                <Header3>
                  {member.user.name}{" "}
                  {member.user.id === user.id && <span className="text-text-dimmed">(You)</span>}
                </Header3>
                <Paragraph variant="small">{member.user.email}</Paragraph>
              </div>
              <div className="flex grow items-center justify-end gap-4">
                <LeaveRemoveButton userId={user.id} member={member} memberCount={members.length} />
              </div>
            </li>
          ))}
        </ul>

        {invites.length > 0 && (
          <>
            <Header2 className="mt-4">Pending invites</Header2>
            <ul className="flex w-full max-w-md flex-col divide-y divide-charcoal-800 border-b border-charcoal-800">
              {invites.map((invite) => (
                <li key={invite.id} className="flex items-center gap-4 py-4">
                  <div className="rounded-md border border-charcoal-750 bg-charcoal-800 p-1.5">
                    <NamedIcon name="envelope" className="h-7 w-7" />
                  </div>
                  <div className="flex flex-col gap-0.5">
                    <Header3>{invite.email}</Header3>
                    <Paragraph variant="small">
                      Invite sent {<DateTime date={invite.updatedAt} />}
                    </Paragraph>
                  </div>
                  <div className="flex grow items-center justify-end gap-x-2">
                    <ResendButton invite={invite} />
                    <RevokeButton invite={invite} />
                  </div>
                </li>
              ))}
            </ul>
          </>
        )}

        {requiresUpgrade ? (
          <InfoPanel
            variant="upgrade"
            icon={LockOpenIcon}
            iconClassName="text-indigo-500"
            title="Unlock more team members"
            to={v3BillingPath(organization)}
            buttonLabel="Upgrade"
          >
            <Paragraph variant="small">
              You've used all {limits.limit} of your available team members. Upgrade your plan to
              enable more.
            </Paragraph>
          </InfoPanel>
        ) : (
          <div className="mt-4 flex max-w-md justify-end">
            <LinkButton
              to={inviteTeamMemberPath(organization)}
              variant={"primary/small"}
              LeadingIcon={UserPlusIcon}
            >
              Invite a team member
            </LinkButton>
          </div>
        )}
      </PageBody>
    </PageContainer>
  );
}

function LeaveRemoveButton({
  userId,
  member,
  memberCount,
}: {
  userId: string;
  member: Member;
  memberCount: number;
}) {
  const organization = useOrganization();

  if (userId === member.user.id) {
    if (memberCount === 1) {
      return (
        <SimpleTooltip
          button={
            <ButtonContent variant="minimal/small" className="cursor-not-allowed">
              Leave team
            </ButtonContent>
          }
          content="An organization requires at least 1 team member"
        />
      );
    }

    //you leave the team
    return (
      <LeaveTeamModal
        member={member}
        buttonText="Leave team"
        title="Are you sure you want to leave the team?"
        description={`You will no longer have access to ${organization.title}. To regain access, you will need to be invited again.`}
        actionText="Leave team"
      />
    );
  }

  //you remove another member
  return (
    <LeaveTeamModal
      member={member}
      buttonText="Remove from team"
      title={`Are you sure you want to remove ${member.user.name ?? "them"} from the team?`}
      description={`They will no longer have access to ${organization.title}. To regain access, you will need to invite them again.`}
      actionText="Remove from team"
    />
  );
}

function LeaveTeamModal({
  member,
  buttonText,
  title,
  description,
  actionText,
}: {
  member: Member;
  buttonText: string;
  title: string;
  description: string;
  actionText: string;
}) {
  const [open, setOpen] = useState(false);
  const lastSubmission = useActionData();

  const [form, { memberId }] = useForm({
    id: "remove-member",
    // TODO: type this
    lastSubmission: lastSubmission as any,
    onValidate({ formData }) {
      return parse(formData, { schema });
    },
  });

  return (
    <Alert open={open} onOpenChange={(o) => setOpen(o)}>
      <AlertTrigger asChild>
        <Button variant="tertiary/small">{buttonText}</Button>
      </AlertTrigger>
      <AlertContent>
        <AlertHeader>
          <AlertTitle>{title}</AlertTitle>
          <AlertDescription>{description}</AlertDescription>
        </AlertHeader>
        <AlertFooter>
          <AlertCancel asChild>
            <Button variant="tertiary/small">Cancel</Button>
          </AlertCancel>
          <Form method="post" {...form.props} onSubmit={() => setOpen(false)}>
            <input type="hidden" value={member.id} name="memberId" />
            <Button type="submit" variant="danger/small" form={form.props.id}>
              {actionText}
            </Button>
          </Form>
        </AlertFooter>
      </AlertContent>
    </Alert>
  );
}

function ResendButton({ invite }: { invite: Invite }) {
  return (
    <Form method="post" action={resendInvitePath()} className="flex">
      <input type="hidden" value={invite.id} name="inviteId" />
      <Button type="submit" variant="tertiary/small">
        Resend invite
      </Button>
    </Form>
  );
}

function RevokeButton({ invite }: { invite: Invite }) {
  const organization = useOrganization();

  return (
    <Form method="post" action={revokeInvitePath()} className="flex">
      <input type="hidden" value={invite.id} name="inviteId" />
      <input type="hidden" value={organization.slug} name="slug" />
      <Button
        type="submit"
        variant="danger/small"
        LeadingIcon="trash-can"
        leadingIconClassName="text-white"
      />
    </Form>
  );
}
