import { gql, useQuery, useSubscription } from "@apollo/client";
import { LinearProgress, makeStyles, Typography } from "@material-ui/core";
import { format } from "date-fns";
import React, { FC, useState } from "react";
import { ErrorPage } from "../commons/fallbacks/error-page";
import {
  PipelineTask,
  PipelineTaskEvent,
  PipelineTaskLogs,
  TaskStatuses,
} from "../generated/graphql";
import { PIPELINE_TASK_EVENT } from "./subscriptions";
import { clone, find, propEq } from "ramda";
import { PipelineUnits } from '../generated/graphql';

interface Props {
  taskId: string;
}

const PIPELINE_TASK = gql`
  query FindPipelineTask($taskId: String!) {
    pipelineTask(id: $taskId) {
      id
      units
      commit
      status
      startedAt
      endedAt
      logs {
        unit
        logs
        status
        startedAt
        endedAt
      }
    }
  }
`;

const useStyles = makeStyles((theme) => ({
  root: {},
  groupTitle: {
    backgroundColor: "white",
    fontWeight: 500,
    borderTop: "1px solid #eee",
    fontSize: "16px",
    padding: "12px",
    marginLeft: "1px",
  },
  logText: {
    fontFamily: 'Menlo, Monaco, "Courier New", monospace',
    whiteSpace: "pre-wrap",
    border: "none",
    margin: "6px 12px",
    display: "block",
  },
}));

export const PipelineTaskDetail: FC<Props> = ({ taskId }) => {
  const [taskEvents, setTaskEvents] = useState(
    () => new Array<PipelineTaskEvent>()
  );
  const { data, loading, error, client } = useQuery<{
    pipelineTask: PipelineTask;
  }>(PIPELINE_TASK, {
    variables: { taskId },
  });
  const task = data?.pipelineTask;
  useSubscription<{ pipelineTaskEvent: PipelineTaskEvent }>(
    PIPELINE_TASK_EVENT,
    {
      variables: { taskId },
      onSubscriptionData({ subscriptionData: { data } }) {
        const event = data?.pipelineTaskEvent;
        console.log(event);
        if (event && task) {
          setTaskEvents((prev) => [...prev, event]);
          if (event.unit) {
            // event of running scripts
            client.cache.modify({
              id: client.cache.identify(task!),
              fields: {
                logs(before: PipelineTaskLogs[]) {
                  before = clone(before);
                  let l = find(propEq("unit", event.unit), before);
                  if (l) {
                    l.logs += event.message;
                  } else {
                    l = {
                      unit: event.unit!,
                      logs: event.message,
                      status: event.status,
                      startedAt: event.emittedAt,
                      endedAt: null,
                      __typename: "PipelineTaskLogs",
                    };
                    before.push(l);
                  }
                  if (event.status === TaskStatuses.Working) {
                    l.startedAt || (l.startedAt = event.emittedAt);
                  }
                  if (
                    [TaskStatuses.Failed, TaskStatuses.Success].includes(
                      event.status
                    )
                  ) {
                    l.startedAt && (l.startedAt = event.emittedAt);
                    l.endedAt = event.emittedAt;
                  }
                  l.status = event.status;
                  return before;
                },
              },
            });
          } else {
            // event of task status change
            client.cache.modify({
              id: client.cache.identify(task!),
              fields: {
                status() {
                  return event.status;
                },
                startedAt(before) {
                  if (event.status === TaskStatuses.Working) {
                    return event.emittedAt;
                  } else {
                    return before;
                  }
                },
                endedAt(before) {
                  if (
                    [TaskStatuses.Success, TaskStatuses.Failed].includes(
                      event.status
                    )
                  ) {
                    return event.emittedAt;
                  } else {
                    return before;
                  }
                },
              },
            });
          }
        }
      },
    }
  );
  const classes = useStyles();

  if (error) {
    return <ErrorPage>{error.message}</ErrorPage>;
  }

  if (loading) {
    return <LinearProgress color="secondary" />;
  }

  return (
    <div className={classes.root}>
      <Typography variant="h4" component="h2">
        {taskId} detail
      </Typography>
      <Typography variant="h5" component="h3">
        {task?.status}
      </Typography>
      <Typography variant="h5" component="h3">
        {task?.startedAt && format(task?.startedAt, "yyyy-MM-dd HH:mm:ss.SSS")}-
        {task?.endedAt && format(task?.endedAt, "yyyy-MM-dd HH:mm:ss.SSS")}
      </Typography>

      {task?.logs.map((logs) => (
        <LogGroup key={logs.unit} logs={logs} />
      ))}
    </div>
  );
};

const LogGroup: FC<{ logs: PipelineTaskLogs }> = ({ logs }) => {
  const classes = useStyles();
  return (
    <div>
      <div className={classes.groupTitle}>
        {logs.unit}{" "}
        {logs.startedAt && format(logs.startedAt, "yyyy-MM-dd HH:mm:ss")}{" "}
        {logs.endedAt && format(logs.endedAt, "yyyy-MM-dd HH:mm:ss")} {logs.status}
      </div>
      <code className={classes.logText}>{logs.logs}</code>
    </div>
  );
};
