/**
 * Copyright (c) 2023-present Plane Software, Inc. and contributors
 * SPDX-License-Identifier: AGPL-3.0-only
 * See the LICENSE file for details.
 */

import { useMemo, useState } from "react";
import { useSearchParams } from "next/navigation";
import { CalendarDays } from "lucide-react";
import { observer } from "mobx-react";
import { EUserPermissions } from "@plane/constants";
import { useTranslation } from "@plane/i18n";
import { PriorityIcon, StateGroupIcon } from "@plane/propel/icons";
import { TOAST_TYPE, setToast } from "@plane/propel/toast";
import type {
  IState,
  TExternalContourBoardDirection,
  TExternalContourRequest,
  TIssue,
} from "@plane/types";
import { Avatar } from "@plane/ui";
import { cn, renderFormattedDate, renderFormattedPayloadDate } from "@plane/utils";
import { DateDropdown } from "@/components/dropdowns/date";
import { ButtonAvatars } from "@/components/dropdowns/member/avatar";
import { MemberDropdown } from "@/components/dropdowns/member/dropdown";
import { MemberDropdownBase } from "@/components/dropdowns/member/base";
import { PriorityDropdown } from "@/components/dropdowns/priority";
import { WorkItemStateDropdownBase } from "@/components/dropdowns/state/base";
import { StateDropdown } from "@/components/dropdowns/state/dropdown";
import { useAppRouter } from "@/hooks/use-app-router";
import { useMember } from "@/hooks/store/use-member";
import { useProjectExternalContoursBoard } from "@/hooks/store/use-project-external-contours-board";
import { useProjectExternalContours } from "@/hooks/store/use-project-external-contours";
import { useProjectState } from "@/hooks/store/use-project-state";
import { useUserPermissions } from "@/hooks/store/user";
import { IssueService } from "@/services/issue/issue.service";

type Props = {
  direction: TExternalContourBoardDirection;
  projectId: string;
  request: TExternalContourRequest;
  workspaceSlug: string;
};

const issueService = new IssueService();

const basePillClasses =
  "inline-flex min-h-9 items-center gap-1.5 rounded-full border-0 px-2.5 py-1 text-[11px] font-medium shadow-none outline-none transition-colors";

const buildSourceStateMap = (
  states: { id: string; name: string; color: string; group: IState["group"] }[] | undefined,
  projectId: string | null
) =>
  Object.fromEntries(
    (states ?? []).map((state, index) => [
      state.id,
      {
        id: state.id,
        color: state.color,
        default: false,
        description: "",
        group: state.group,
        name: state.name,
        order: index + 1,
        project_id: projectId ?? "",
        sequence: index + 1,
        workspace_id: "",
      } satisfies IState,
    ])
  );

const resolveRequestStatus = (issue: TExternalContourRequest["issue"], fallbackStatus: TExternalContourRequest["status"]) => {
  const stateGroup = issue.state_detail?.group;
  if (!stateGroup) return fallbackStatus;
  return stateGroup === "completed" || stateGroup === "cancelled" ? "closed" : "open";
};

export const ExternalContoursBoardItem = observer(function ExternalContoursBoardItem(props: Props) {
  const { direction, projectId, request, workspaceSlug } = props;
  const router = useAppRouter();
  const searchParams = useSearchParams();
  const { t } = useTranslation();
  const { getUserDetails, workspace } = useMember();
  const { getProjectRoleByWorkspaceSlugAndProjectId } = useUserPermissions();
  const { getStateById, getProjectStateIds } = useProjectState();
  const {
    fetchBoard,
    upsertBoardItems,
  } = useProjectExternalContoursBoard();
  const {
    fetchTargetOptions,
    getTargetOptionsByProjectId,
    updateRequest,
    updateRequestIssue,
  } = useProjectExternalContours();
  const [isUpdating, setIsUpdating] = useState(false);
  const [isSourceOptionsLoading, setIsSourceOptionsLoading] = useState(false);

  const issue = request.issue;
  const selectedInboxIssueId = searchParams.get("inboxIssueId");
  const isActive = selectedInboxIssueId === request.id;
  const requester = request.requested_by?.display_name || request.requested_by_name || issue.created_by_detail?.display_name || "NODE.DC";
  const requesterAvatar = issue.created_by_detail?.avatar_url || "";
  const counterpartContourName =
    direction === "outgoing"
      ? request.target_project?.name || request.target_project_name || issue.project_detail?.name
      : request.source_project?.name || request.source_project_name;
  const targetProjectId = issue.project_id || request.target_project?.id || request.target_project_id || null;
  const projectRole = targetProjectId
    ? getProjectRoleByWorkspaceSlugAndProjectId(workspaceSlug, targetProjectId)
    : undefined;
  const canEditTargetIssue =
    direction === "incoming" && !!targetProjectId && projectRole !== undefined && projectRole !== EUserPermissions.GUEST;
  const canEditSourceRequest = direction === "outgoing" && !!request.capabilities?.can_edit_request && !!targetProjectId;
  const canEditCard = canEditTargetIssue || canEditSourceRequest;
  const requestLink = `/${workspaceSlug}/projects/${projectId}/external-contours?inboxIssueId=${request.id}`;
  const targetOptions = getTargetOptionsByProjectId(targetProjectId);
  const sourceStateMap = useMemo(
    () => buildSourceStateMap(targetOptions?.states, targetProjectId),
    [targetOptions?.states, targetProjectId]
  );
  const sourceStateIds = useMemo(() => targetOptions?.states?.map((state) => state.id) ?? [], [targetOptions?.states]);
  const selectedState = canEditTargetIssue ? getStateById(issue.state_id) : sourceStateMap[issue.state_id ?? ""];
  const projectStateIds = issue.project_id ? getProjectStateIds(issue.project_id) : [];
  const foregroundClasses = isActive ? "text-[#111111]" : "text-white";
  const subtleTextClasses = isActive ? "text-[#2F4721]" : "text-[#B3B3B8]";
  const pillBackgroundClasses =
    isActive ? "bg-black/10 text-[#111111]" : "bg-[rgb(var(--nodedc-card-passive-rgb))] text-white";
  const iconBubbleClasses = isActive ? "bg-black text-[rgb(var(--nodedc-card-active-rgb))]" : "bg-[#111214] text-white";
  const statusIconColor = selectedState?.color ?? (isActive ? "#111111" : "var(--text-color-primary)");
  const dueDateLabel = issue.target_date ? renderFormattedDate(issue.target_date, "d MMM, yyyy") : t("common.none");

  if (!issue) return null;

  const stopCardPropagation = (event: React.MouseEvent) => {
    event.stopPropagation();
  };

  const openDetail = () => {
    if (isActive) return;
    router.push(requestLink);
  };

  const syncBoardAfterMutation = async () => {
    await fetchBoard(workspaceSlug, projectId);
  };

  const ensureSourceOptions = async () => {
    if (!canEditSourceRequest || !targetProjectId) return;

    const tasks: Promise<unknown>[] = [];
    if (!targetOptions) {
      setIsSourceOptionsLoading(true);
      tasks.push(fetchTargetOptions(workspaceSlug, projectId, targetProjectId));
    }
    if (!workspace.workspaceMemberIds) {
      tasks.push(workspace.fetchWorkspaceMembers(workspaceSlug));
    }

    if (tasks.length === 0) return;

    try {
      await Promise.all(tasks);
    } finally {
      setIsSourceOptionsLoading(false);
    }
  };

  const handleTargetIssueUpdate = async (data: Partial<TIssue>) => {
    if (!targetProjectId || !issue.id || isUpdating) return;

    setIsUpdating(true);
    try {
      const updatedIssue = await issueService.patchIssue(workspaceSlug, targetProjectId, issue.id, data);
      const nextIssue = { ...issue, ...updatedIssue };
      const nextRequest = {
        ...request,
        issue: nextIssue,
        status: resolveRequestStatus(nextIssue, request.status),
      };

      updateRequestIssue(request.id, nextIssue);
      upsertBoardItems([nextRequest]);
      await syncBoardAfterMutation();
    } catch {
      setToast({ title: t("error"), type: TOAST_TYPE.ERROR, message: t("issue_could_not_be_updated") });
    } finally {
      setIsUpdating(false);
    }
  };

  const handleSourceRequestUpdate = async (data: Partial<TIssue>) => {
    if (!canEditSourceRequest || isUpdating) return;

    setIsUpdating(true);
    try {
      const updatedRequest = await updateRequest(workspaceSlug, projectId, request.id, data);
      if (updatedRequest) {
        upsertBoardItems([updatedRequest]);
      }
      await syncBoardAfterMutation();
    } catch {
      setToast({ title: t("error"), type: TOAST_TYPE.ERROR, message: t("issue_could_not_be_updated") });
    } finally {
      setIsUpdating(false);
    }
  };

  const handleCardUpdate = async (data: Partial<TIssue>) => {
    if (canEditTargetIssue) {
      await handleTargetIssueUpdate(data);
      return;
    }

    await handleSourceRequestUpdate(data);
  };

  return (
    <div className="group/kanban-block relative mb-2">
      <div
        data-active={isActive}
        className="nodedc-external-card relative flex min-h-[220px] w-full cursor-pointer flex-col p-4 transition-all hover:bg-white/5"
        role="button"
        tabIndex={0}
        onClick={openDetail}
        onKeyDown={(event) => {
          if (event.key === "Enter" || event.key === " ") {
            event.preventDefault();
            openDetail();
          }
        }}
      >
        <div className={cn("relative flex min-h-[220px] flex-col px-1", foregroundClasses)}>
          <div className="space-y-0.5">
            <div className="flex items-center justify-between gap-3">
              <div className="flex min-w-0 flex-1 items-center gap-3">
                <div className="shrink-0">
                  <Avatar src={requesterAvatar} name={requester} size="md" />
                </div>
                <div className={cn("truncate text-body-sm-medium leading-5", foregroundClasses)}>{requester}</div>
              </div>

              <div className="flex shrink-0 items-center gap-2" onClick={stopCardPropagation}>
                {request.has_unread_updates && (
                  <span
                    className={cn("size-2 rounded-full", isActive ? "bg-black/70" : "bg-accent-primary")}
                    title={t("external_contours_page.list.unread_updates")}
                  />
                )}

                <PriorityDropdown
                  value={issue.priority}
                  onChange={(priority) => void handleCardUpdate({ priority })}
                  disabled={!canEditCard || isUpdating}
                  buttonVariant="transparent-without-text"
                  button={
                    <div
                      className={cn(
                        "flex h-8 w-8 items-center justify-center rounded-full border-0 shadow-none outline-none",
                        iconBubbleClasses
                      )}
                    >
                      <PriorityIcon priority={issue.priority} className="h-3.5 w-3.5" />
                    </div>
                  }
                />

                {canEditTargetIssue ? (
                  <StateDropdown
                    projectId={issue.project_id ?? undefined}
                    stateIds={projectStateIds ?? []}
                    value={issue.state_id}
                    onChange={(stateId) => void handleCardUpdate({ state_id: stateId })}
                    disabled={!canEditCard || isUpdating}
                    buttonVariant="transparent-without-text"
                    button={
                      <div className={cn("flex h-8 w-8 items-center justify-center rounded-full", iconBubbleClasses)}>
                        <StateGroupIcon
                          stateGroup={selectedState?.group ?? "backlog"}
                          color={statusIconColor}
                          className="h-3.5 w-3.5"
                          percentage={selectedState?.order}
                        />
                      </div>
                    }
                  />
                ) : (
                  <WorkItemStateDropdownBase
                    projectId={targetProjectId ?? undefined}
                    value={issue.state_id}
                    stateIds={sourceStateIds}
                    getStateById={(stateId) => (stateId ? sourceStateMap[stateId] : undefined)}
                    onChange={(stateId) => void handleCardUpdate({ state_id: stateId })}
                    disabled={!canEditCard || isUpdating || !targetProjectId}
                    isInitializing={isSourceOptionsLoading}
                    onDropdownOpen={() => {
                      void ensureSourceOptions();
                    }}
                    buttonVariant="transparent-without-text"
                    button={
                      <div className={cn("flex h-8 w-8 items-center justify-center rounded-full", iconBubbleClasses)}>
                        <StateGroupIcon
                          stateGroup={selectedState?.group ?? "backlog"}
                          color={statusIconColor}
                          className="h-3.5 w-3.5"
                          percentage={selectedState?.order}
                        />
                      </div>
                    }
                  />
                )}
              </div>
            </div>

            <div className={cn("truncate -mt-0.5 pl-8 text-[11px] font-medium leading-4", subtleTextClasses)}>
              {counterpartContourName || t("common.none")}
            </div>
          </div>

          <div className="flex flex-1 items-center justify-center px-5 py-4 text-center">
            <div className="line-clamp-4 max-w-full text-lg font-semibold leading-6">{issue.name}</div>
          </div>

          <div className="flex items-center justify-between gap-3" onClick={stopCardPropagation}>
            {canEditTargetIssue ? (
              <MemberDropdown
                multiple
                projectId={issue.project_id ?? undefined}
                value={issue.assignee_ids ?? []}
                onChange={(assigneeIds) => void handleCardUpdate({ assignee_ids: assigneeIds })}
                disabled={!canEditCard || isUpdating}
                buttonVariant="transparent-without-text"
                button={
                  <div className={cn(basePillClasses, pillBackgroundClasses, "pl-1 pr-2")}>
                    <ButtonAvatars showTooltip={false} userIds={issue.assignee_ids ?? []} size="sm" />
                  </div>
                }
              />
            ) : (
              <MemberDropdownBase
                multiple
                getUserDetails={getUserDetails}
                memberIds={targetOptions?.member_ids ?? []}
                value={issue.assignee_ids ?? []}
                onChange={(assigneeIds) => void handleCardUpdate({ assignee_ids: assigneeIds })}
                disabled={!canEditCard || isUpdating || !targetProjectId}
                onDropdownOpen={() => {
                  void ensureSourceOptions();
                }}
                buttonVariant="transparent-without-text"
                button={
                  <div className={cn(basePillClasses, pillBackgroundClasses, "pl-1 pr-2")}>
                    <ButtonAvatars showTooltip={false} userIds={issue.assignee_ids ?? []} size="sm" />
                  </div>
                }
              />
            )}

            <DateDropdown
              value={issue.target_date}
              onChange={(targetDate) =>
                void handleCardUpdate({
                  target_date: targetDate ? renderFormattedPayloadDate(targetDate) : null,
                })
              }
              disabled={!canEditCard || isUpdating}
              buttonVariant="transparent-without-text"
              button={
                <div className={cn(basePillClasses, pillBackgroundClasses)}>
                  <CalendarDays className="h-3.5 w-3.5" />
                  <span className="truncate">{dueDateLabel}</span>
                </div>
              }
            />
          </div>
        </div>
      </div>
    </div>
  );
});
