import * as React from "react";
import { useTranslation } from "react-i18next";

import { UiLogicError } from "@/common";

import {
  getHttpTimelineClient,
  HttpTimelineInfo,
  HttpTimelinePostInfo,
  HttpTimelinePostPostRequestData,
} from "@/http/timeline";

import { pushAlert } from "@/services/alert";

import base64 from "@/utilities/base64";

import BlobImage from "@/views/common/BlobImage";
import LoadingButton from "@/views/common/button/LoadingButton";
import PopupMenu from "@/views/common/menu/PopupMenu";
import MarkdownPostEdit from "./MarkdownPostEdit";
import TimelinePostEditCard from "./TimelinePostEditCard";
import IconButton from "@/views/common/button/IconButton";

import "./TimelinePostEdit.css";

interface TimelinePostEditTextProps {
  text: string;
  disabled: boolean;
  onChange: (text: string) => void;
  className?: string;
  style?: React.CSSProperties;
}

const TimelinePostEditText: React.FC<TimelinePostEditTextProps> = (props) => {
  const { text, disabled, onChange, className, style } = props;

  return (
    <textarea
      value={text}
      disabled={disabled}
      onChange={(event) => {
        onChange(event.target.value);
      }}
      className={className}
      style={style}
    />
  );
};

interface TimelinePostEditImageProps {
  onSelect: (file: File | null) => void;
  disabled: boolean;
}

const TimelinePostEditImage: React.FC<TimelinePostEditImageProps> = (props) => {
  const { onSelect, disabled } = props;

  const { t } = useTranslation();

  const [file, setFile] = React.useState<File | null>(null);
  const [error, setError] = React.useState<boolean>(false);

  const onInputChange: React.ChangeEventHandler<HTMLInputElement> = (e) => {
    setError(false);
    const files = e.target.files;
    if (files == null || files.length === 0) {
      setFile(null);
      onSelect(null);
    } else {
      setFile(files[0]);
    }
  };

  React.useEffect(() => {
    return () => {
      onSelect(null);
    };
  }, [onSelect]);

  return (
    <>
      <input
        type="file"
        onChange={onInputChange}
        accept="image/*"
        disabled={disabled}
        className="mx-3 my-1"
      />
      {file != null && !error && (
        <BlobImage
          blob={file}
          className="timeline-post-edit-image"
          onLoad={() => onSelect(file)}
          onError={() => {
            onSelect(null);
            setError(true);
          }}
        />
      )}
      {error ? <div className="text-danger">{t("loadImageError")}</div> : null}
    </>
  );
};

type PostKind = "text" | "markdown" | "image";

const postKindIconMap: Record<PostKind, string> = {
  text: "fonts",
  markdown: "markdown",
  image: "image",
};

export interface TimelinePostEditProps {
  className?: string;
  style?: React.CSSProperties;
  timeline: HttpTimelineInfo;
  onPosted: (newPost: HttpTimelinePostInfo) => void;
}

const TimelinePostEdit: React.FC<TimelinePostEditProps> = (props) => {
  const { timeline, style, className, onPosted } = props;

  const { t } = useTranslation();

  const [process, setProcess] = React.useState<boolean>(false);

  const [kind, setKind] = React.useState<Exclude<PostKind, "markdown">>("text");
  const [showMarkdown, setShowMarkdown] = React.useState<boolean>(false);

  const [text, setText] = React.useState<string>("");
  const [image, setImage] = React.useState<File | null>(null);

  const draftTextLocalStorageKey = `timeline.${timeline.owner.username}.${timeline.nameV2}.postDraft.text`;

  React.useEffect(() => {
    setText(window.localStorage.getItem(draftTextLocalStorageKey) ?? "");
  }, [draftTextLocalStorageKey]);

  const canSend =
    (kind === "text" && text.length !== 0) ||
    (kind === "image" && image != null);

  const onPostError = (): void => {
    pushAlert({
      type: "danger",
      message: "timeline.sendPostFailed",
    });
  };

  const onSend = async (): Promise<void> => {
    setProcess(true);

    let requestData: HttpTimelinePostPostRequestData;
    switch (kind) {
      case "text":
        requestData = {
          contentType: "text/plain",
          data: await base64(text),
        };
        break;
      case "image":
        if (image == null) {
          throw new UiLogicError(
            "Content type is image but image blob is null.",
          );
        }
        requestData = {
          contentType: image.type,
          data: await base64(image),
        };
        break;
      default:
        throw new UiLogicError("Unknown content type.");
    }

    getHttpTimelineClient()
      .postPost(timeline.owner.username, timeline.nameV2, {
        dataList: [requestData],
      })
      .then(
        (data) => {
          if (kind === "text") {
            setText("");
            window.localStorage.removeItem(draftTextLocalStorageKey);
          }
          setProcess(false);
          setKind("text");
          onPosted(data);
        },
        () => {
          setProcess(false);
          onPostError();
        },
      );
  };

  return (
    <TimelinePostEditCard className={className} style={style}>
      {showMarkdown ? (
        <MarkdownPostEdit
          className="cru-fill-parent"
          onClose={() => setShowMarkdown(false)}
          owner={timeline.owner.username}
          timeline={timeline.nameV2}
          onPosted={onPosted}
          onPostError={onPostError}
        />
      ) : (
        <div className="row">
          <div className="col px-1 py-1">
            {(() => {
              if (kind === "text") {
                return (
                  <TimelinePostEditText
                    className="cru-fill-parent timeline-post-edit"
                    text={text}
                    disabled={process}
                    onChange={(t) => {
                      setText(t);
                      window.localStorage.setItem(draftTextLocalStorageKey, t);
                    }}
                  />
                );
              } else if (kind === "image") {
                return (
                  <TimelinePostEditImage
                    onSelect={setImage}
                    disabled={process}
                  />
                );
              }
            })()}
          </div>
          <div className="col col-auto align-self-end m-1">
            <div className="d-block cru-text-center mt-1 mb-2">
              <PopupMenu
                items={(["text", "image", "markdown"] as const).map((kind) => ({
                  type: "button",
                  text: `timeline.post.type.${kind}`,
                  iconClassName: postKindIconMap[kind],
                  onClick: () => {
                    if (kind === "markdown") {
                      setShowMarkdown(true);
                    } else {
                      setKind(kind);
                    }
                  },
                }))}
              >
                <IconButton large icon={postKindIconMap[kind]} />
              </PopupMenu>
            </div>
            <LoadingButton
              onClick={() => void onSend()}
              disabled={!canSend}
              loading={process}
            >
              {t("timeline.send")}
            </LoadingButton>
          </div>
        </div>
      )}
    </TimelinePostEditCard>
  );
};

export default TimelinePostEdit;
