import LabelsElement from "Common/UI/Components/Label/Labels";
import OnCallDutyPoliciesView from "../../../Components/OnCallPolicy/OnCallPolicies";
import IncidentEpisodeFeedElement from "../../../Components/IncidentEpisode/IncidentEpisodeFeed";
import IncidentEpisodeMemberRoleAssignment from "../../../Components/IncidentEpisode/IncidentEpisodeMemberRoleAssignment";
import PageComponentProps from "../../PageComponentProps";
import SortOrder from "Common/Types/BaseDatabase/SortOrder";
import { Black } from "Common/Types/BrandColors";
import { LIMIT_PER_PROJECT } from "Common/Types/Database/LimitMax";
import OneUptimeDate from "Common/Types/Date";
import BadDataException from "Common/Types/Exception/BadDataException";
import { PromiseVoidFunction } from "Common/Types/FunctionTypes";
import ObjectID from "Common/Types/ObjectID";
import ErrorMessage from "Common/UI/Components/ErrorMessage/ErrorMessage";
import FormFieldSchemaType from "Common/UI/Components/Forms/Types/FormFieldSchemaType";
import InfoCard from "Common/UI/Components/InfoCard/InfoCard";
import PageLoader from "Common/UI/Components/Loader/PageLoader";
import CardModelDetail from "Common/UI/Components/ModelDetail/CardModelDetail";
import Pill from "Common/UI/Components/Pill/Pill";
import FieldType from "Common/UI/Components/Types/FieldType";
import BaseAPI from "Common/UI/Utils/API/API";
import ModelAPI, { ListResult } from "Common/UI/Utils/ModelAPI/ModelAPI";
import Navigation from "Common/UI/Utils/Navigation";
import IncidentEpisode from "Common/Models/DatabaseModels/IncidentEpisode";
import IncidentSeverity from "Common/Models/DatabaseModels/IncidentSeverity";
import IncidentState from "Common/Models/DatabaseModels/IncidentState";
import IncidentEpisodeStateTimeline from "Common/Models/DatabaseModels/IncidentEpisodeStateTimeline";
import Label from "Common/Models/DatabaseModels/Label";
import React, {
  Fragment,
  FunctionComponent,
  ReactElement,
  useEffect,
  useState,
} from "react";
import UserElement from "../../../Components/User/User";
import ChangeEpisodeState from "../../../Components/IncidentEpisode/ChangeState";

const IncidentEpisodeView: FunctionComponent<
  PageComponentProps
> = (): ReactElement => {
  const modelId: ObjectID = Navigation.getLastParamAsObjectID();

  const [episodeStateTimeline, setEpisodeStateTimeline] = useState<
    IncidentEpisodeStateTimeline[]
  >([]);
  const [incidentStates, setIncidentStates] = useState<IncidentState[]>([]);

  const [error, setError] = useState<string>("");
  const [isLoading, setIsLoading] = useState<boolean>(false);

  const fetchData: PromiseVoidFunction = async (): Promise<void> => {
    try {
      setIsLoading(true);

      const episodeTimelines: ListResult<IncidentEpisodeStateTimeline> =
        await ModelAPI.getList({
          modelType: IncidentEpisodeStateTimeline,
          query: {
            incidentEpisodeId: modelId,
          },
          limit: LIMIT_PER_PROJECT,
          skip: 0,
          select: {
            _id: true,
            startsAt: true,
            createdByUser: {
              name: true,
              email: true,
              profilePictureId: true,
            },
            incidentStateId: true,
          },
          sort: {
            startsAt: SortOrder.Ascending,
          },
        });

      const incidentStates: ListResult<IncidentState> = await ModelAPI.getList({
        modelType: IncidentState,
        query: {},
        limit: LIMIT_PER_PROJECT,
        skip: 0,
        select: {
          _id: true,
          name: true,
          isAcknowledgedState: true,
          isResolvedState: true,
        },
        sort: {},
      });

      setIncidentStates(incidentStates.data as IncidentState[]);
      setEpisodeStateTimeline(
        episodeTimelines.data as IncidentEpisodeStateTimeline[],
      );
      setError("");
    } catch (err) {
      setError(BaseAPI.getFriendlyMessage(err));
    }

    setIsLoading(false);
  };

  useEffect(() => {
    fetchData().catch((err: Error) => {
      setError(BaseAPI.getFriendlyMessage(err));
    });
  }, []);

  if (isLoading) {
    return <PageLoader isVisible={true} />;
  }

  if (error) {
    return <ErrorMessage message={error} />;
  }

  type GetIncidentStateFunction = () => IncidentState | undefined;

  const getAcknowledgeState: GetIncidentStateFunction = ():
    | IncidentState
    | undefined => {
    return incidentStates.find((state: IncidentState) => {
      return state.isAcknowledgedState;
    });
  };

  const getResolvedState: GetIncidentStateFunction = ():
    | IncidentState
    | undefined => {
    return incidentStates.find((state: IncidentState) => {
      return state.isResolvedState;
    });
  };

  type getTimeFunction = () => string;

  const getTimeToAcknowledge: getTimeFunction = (): string => {
    const episodeStartTime: Date =
      episodeStateTimeline[0]?.startsAt || new Date();

    const acknowledgeTime: Date | undefined = episodeStateTimeline.find(
      (timeline: IncidentEpisodeStateTimeline) => {
        return (
          timeline.incidentStateId?.toString() ===
          getAcknowledgeState()?._id?.toString()
        );
      },
    )?.startsAt;

    const resolveTime: Date | undefined = episodeStateTimeline.find(
      (timeline: IncidentEpisodeStateTimeline) => {
        return (
          timeline.incidentStateId?.toString() ===
          getResolvedState()?._id?.toString()
        );
      },
    )?.startsAt;

    if (!acknowledgeTime && !resolveTime) {
      return (
        "Not yet " +
        (getAcknowledgeState()?.name?.toLowerCase() || "acknowledged")
      );
    }

    if (!acknowledgeTime && resolveTime) {
      return OneUptimeDate.convertMinutesToDaysHoursAndMinutes(
        OneUptimeDate.getDifferenceInMinutes(resolveTime, episodeStartTime),
      );
    }

    return OneUptimeDate.convertMinutesToDaysHoursAndMinutes(
      OneUptimeDate.getDifferenceInMinutes(acknowledgeTime!, episodeStartTime),
    );
  };

  const getTimeToResolve: getTimeFunction = (): string => {
    const episodeStartTime: Date =
      episodeStateTimeline[0]?.startsAt || new Date();

    const resolveTime: Date | undefined = episodeStateTimeline.find(
      (timeline: IncidentEpisodeStateTimeline) => {
        return (
          timeline.incidentStateId?.toString() ===
          getResolvedState()?._id?.toString()
        );
      },
    )?.startsAt;

    if (!resolveTime) {
      return (
        "Not yet " + (getResolvedState()?.name?.toLowerCase() || "resolved")
      );
    }

    return OneUptimeDate.convertMinutesToDaysHoursAndMinutes(
      OneUptimeDate.getDifferenceInMinutes(resolveTime, episodeStartTime),
    );
  };

  type GetInfoCardFunction = (value: string) => ReactElement;

  const getInfoCardValue: GetInfoCardFunction = (
    value: string,
  ): ReactElement => {
    return <div className="font-medium text-gray-900 text-lg">{value}</div>;
  };

  return (
    <Fragment>
      <CardModelDetail<IncidentEpisode>
        name="Episode Details"
        cardProps={{
          title: "Episode Details",
          description: "Here are more details for this episode.",
        }}
        isEditable={true}
        formSteps={[
          {
            title: "Episode Details",
            id: "episode-details",
          },
          {
            title: "Labels",
            id: "labels",
          },
        ]}
        formFields={[
          {
            field: {
              title: true,
            },
            title: "Episode Title",
            stepId: "episode-details",
            fieldType: FormFieldSchemaType.Text,
            required: true,
            placeholder: "Episode Title",
            validation: {
              minLength: 2,
            },
          },
          {
            field: {
              incidentSeverity: true,
            },
            title: "Episode Severity",
            description: "What is the severity of this episode?",
            fieldType: FormFieldSchemaType.Dropdown,
            stepId: "episode-details",
            dropdownModal: {
              type: IncidentSeverity,
              labelField: "name",
              valueField: "_id",
            },
            required: true,
            placeholder: "Episode Severity",
          },
          {
            field: {
              labels: true,
            },
            title: "Labels ",
            stepId: "labels",
            description:
              "Team members with access to these labels will only be able to access this resource. This is optional and an advanced feature.",
            fieldType: FormFieldSchemaType.MultiSelectDropdown,
            dropdownModal: {
              type: Label,
              labelField: "name",
              valueField: "_id",
            },
            required: false,
            placeholder: "Labels",
          },
        ]}
        modelDetailProps={{
          selectMoreFields: {
            episodeNumberWithPrefix: true,
            createdByUser: {
              _id: true,
              name: true,
              email: true,
              profilePictureId: true,
            },
          },
          showDetailsInNumberOfColumns: 2,
          modelType: IncidentEpisode,
          id: "model-detail-episodes",
          fields: [
            {
              field: {
                episodeNumber: true,
                episodeNumberWithPrefix: true,
              },
              title: "Episode Number",
              fieldType: FieldType.Element,
              getElement: (item: IncidentEpisode): ReactElement => {
                if (!item.episodeNumber) {
                  return <>-</>;
                }

                return (
                  <div className="inline-flex items-center gap-2 px-3 py-2 rounded-lg bg-gray-50 border border-gray-200">
                    <div className="flex items-center justify-center w-6 h-6 rounded-md bg-gray-100">
                      <svg
                        className="w-3.5 h-3.5 text-gray-500"
                        fill="none"
                        viewBox="0 0 24 24"
                        stroke="currentColor"
                        strokeWidth={2}
                      >
                        <path
                          strokeLinecap="round"
                          strokeLinejoin="round"
                          d="M19 11H5m14 0a2 2 0 012 2v6a2 2 0 01-2 2H5a2 2 0 01-2-2v-6a2 2 0 012-2m14 0V9a2 2 0 00-2-2M5 11V9a2 2 0 012-2m0 0V5a2 2 0 012-2h6a2 2 0 012 2v2M7 7h10"
                        />
                      </svg>
                    </div>
                    <span className="text-lg font-semibold text-gray-700">
                      {item.episodeNumberWithPrefix || `#${item.episodeNumber}`}
                    </span>
                  </div>
                );
              },
            },
            {
              field: {
                _id: true,
              },
              title: "Episode ID",
              fieldType: FieldType.ObjectID,
            },
            {
              field: {
                title: true,
              },
              title: "Episode Title",
              fieldType: FieldType.Text,
            },
            {
              field: {
                currentIncidentState: {
                  color: true,
                  name: true,
                },
              },
              title: "Current State",
              fieldType: FieldType.Entity,
              getElement: (item: IncidentEpisode): ReactElement => {
                if (!item["currentIncidentState"]) {
                  throw new BadDataException("Episode State not found");
                }

                return (
                  <Pill
                    color={item.currentIncidentState.color || Black}
                    text={item.currentIncidentState.name || "Unknown"}
                  />
                );
              },
            },
            {
              field: {
                incidentSeverity: {
                  color: true,
                  name: true,
                },
              },
              title: "Episode Severity",
              fieldType: FieldType.Entity,
              getElement: (item: IncidentEpisode): ReactElement => {
                if (!item["incidentSeverity"]) {
                  throw new BadDataException("Episode Severity not found");
                }

                return (
                  <Pill
                    color={item.incidentSeverity.color || Black}
                    text={item.incidentSeverity.name || "Unknown"}
                  />
                );
              },
            },
            {
              field: {
                incidentCount: true,
              },
              title: "Incident Count",
              fieldType: FieldType.Number,
            },
            {
              field: {
                onCallDutyPolicies: {
                  name: true,
                  _id: true,
                },
              },
              title: "On-Call Duty Policies",
              fieldType: FieldType.Element,
              getElement: (item: IncidentEpisode): ReactElement => {
                return (
                  <OnCallDutyPoliciesView
                    onCallPolicies={item.onCallDutyPolicies || []}
                  />
                );
              },
            },
            {
              field: {
                createdAt: true,
              },
              title: "Created At",
              fieldType: FieldType.DateTime,
            },
            {
              field: {
                lastIncidentAddedAt: true,
              },
              title: "Last Incident Added At",
              fieldType: FieldType.DateTime,
            },
            {
              field: {
                createdByUser: {
                  name: true,
                  email: true,
                  profilePictureId: true,
                },
              },
              title: "Created By",
              fieldType: FieldType.Element,
              getElement: (item: IncidentEpisode): ReactElement => {
                if (item.createdByUser) {
                  return <UserElement user={item.createdByUser} />;
                }

                return <p>System</p>;
              },
            },
            {
              field: {
                labels: {
                  name: true,
                  color: true,
                },
              },
              title: "Labels",
              fieldType: FieldType.Element,
              getElement: (item: IncidentEpisode): ReactElement => {
                return <LabelsElement labels={item["labels"] || []} />;
              },
            },
          ],
          modelId: modelId,
        }}
      />

      <ChangeEpisodeState
        episodeId={modelId}
        onActionComplete={async () => {
          await fetchData();
        }}
      />

      <IncidentEpisodeMemberRoleAssignment
        incidentEpisodeId={modelId}
        onMemberChange={async () => {
          await fetchData();
        }}
      />

      <div className="flex space-x-5 mt-5 mb-5 w-full justify-between">
        <InfoCard
          title={`${getAcknowledgeState()?.name || "Acknowledged"} in`}
          value={getInfoCardValue(getTimeToAcknowledge())}
          className="w-1/2"
        />
        <InfoCard
          title={`${getResolvedState()?.name || "Resolved"} in`}
          value={getInfoCardValue(getTimeToResolve())}
          className="w-1/2"
        />
      </div>

      <IncidentEpisodeFeedElement incidentEpisodeId={modelId} />
    </Fragment>
  );
};

export default IncidentEpisodeView;
