import React from "react";
import {
  Typography,
  IconButton,
  Card,
  CardHeader,
  Avatar,
  CardContent,
  CardActions,
  withStyles,
  Menu,
  MenuItem,
  Chip,
  Divider,
  CardMedia,
  CardActionArea,
  ExpansionPanel,
  ExpansionPanelSummary,
  ExpansionPanelDetails,
  Zoom,
  Tooltip,
  RadioGroup,
  Radio,
  FormControlLabel,
  Button,
  Dialog,
  DialogTitle,
  DialogContent,
  DialogContentText,
  DialogActions
} from "@material-ui/core";
import MoreVertIcon from "@material-ui/icons/MoreVert";
import ReplyIcon from "@material-ui/icons/Reply";
import FavoriteIcon from "@material-ui/icons/Favorite";
import AutorenewIcon from "@material-ui/icons/Autorenew";
import OpenInNewIcon from "@material-ui/icons/OpenInNew";
import PublicIcon from "@material-ui/icons/Public";
import VisibilityOffIcon from "@material-ui/icons/VisibilityOff";
import WarningIcon from "@material-ui/icons/Warning";
import ExpandMoreIcon from "@material-ui/icons/ExpandMore";
import GroupIcon from "@material-ui/icons/Group";
import ForumIcon from "@material-ui/icons/Forum";
import AlternateEmailIcon from "@material-ui/icons/AlternateEmail";
import LocalOfferIcon from "@material-ui/icons/LocalOffer";
import { styles } from "./Post.styles";
import { Status } from "../../types/Status";
import { Tag } from "../../types/Tag";
import { Mention } from "../../types/Mention";
import { Visibility } from "../../types/Visibility";
import moment from "moment";
import AttachmentComponent from "../Attachment";
import Mastodon from "megalodon";
import {
  LinkableChip,
  LinkableMenuItem,
  LinkableIconButton,
  LinkableAvatar
} from "../../interfaces/overrides";
import { withSnackbar } from "notistack";
import ShareMenu from "./PostShareMenu";
import { emojifyString } from "../../utilities/emojis";
import { PollOption } from "../../types/Poll";

interface IPostProps {
  post: Status;
  classes: any;
  client: Mastodon;
}

interface IPostState {
  post: Status;
  media_slides?: number;
  menuIsOpen: boolean;
  myVote?: [number];
  deletePostDialog: boolean;
}

export class Post extends React.Component<any, IPostState> {
  client: Mastodon;

  constructor(props: any) {
    super(props);

    this.state = {
      post: this.props.post,
      media_slides:
        this.props.post.media_attachments.length > 0
          ? this.props.post.media_attachments.length
          : 0,
      menuIsOpen: false,
      deletePostDialog: false
    };

    this.client = this.props.client;
  }

  togglePostMenu() {
    this.setState({ menuIsOpen: !this.state.menuIsOpen });
  }

  togglePostDeleteDialog() {
    this.setState({ deletePostDialog: !this.state.deletePostDialog });
  }

  deletePost() {
    this.client
      .del("/statuses/" + this.state.post.id)
      .then((resp: any) => {
        this.props.enqueueSnackbar("Post deleted. Refresh to see changes.");
      })
      .catch((err: Error) => {
        this.props.enqueueSnackbar("Couldn't delete post: " + err.name);
        console.log(err.message);
      });
  }

  findBiggestVote() {
    let poll = this.state.post.poll;
    let votes: number[] = [];
    let bv = "";
    if (poll) {
      poll.options.forEach((option: PollOption) => {
        votes.push(option.votes_count ? option.votes_count : 0);
      });
      let biggestVote = Math.max.apply(null, votes);
      poll.options.forEach((option: PollOption) => {
        if (option.votes_count === biggestVote) {
          bv = option.title;
        }
      });
      return bv;
    } else {
      return "No poll option was the best.";
    }
  }

  captureVote(option: any) {
    let poll = this.state.post.poll;
    let pollIndex: number = 0;
    if (poll) {
      poll.options.forEach((pollOption: PollOption, index: number) => {
        if (pollOption.title === option) {
          pollIndex = index;
        }
      });
    }
    this.setState({ myVote: [pollIndex] });
  }

  submitVote() {
    let poll = this.state.post.poll;
    if (poll) {
      this.client
        .post(`/polls/${poll.id}/votes`, { choices: this.state.myVote })
        .then((resp: any) => {
          let post = this.state.post;
          post.poll = resp.data;
          this.setState({ post });
          this.props.enqueueSnackbar("Vote submitted.");
        })
        .catch((err: Error) => {
          this.props.enqueueSnackbar("Couldn't vote: " + err.name);
          console.error(err.message);
        });
    }
  }

  materializeContent(status: Status) {
    const { classes } = this.props;

    const oldContent = document.createElement("div");
    oldContent.innerHTML = status.content;

    let anchors = oldContent.getElementsByTagName("a");

    Array.prototype.forEach.call(anchors, (link: HTMLAnchorElement) => {
      if (
        link.className.includes("mention") ||
        link.className.includes("hashtag")
      ) {
        link.removeAttribute("href");
      }
    });

    oldContent.innerHTML = emojifyString(
      oldContent.innerHTML,
      status.emojis,
      classes.postEmoji
    );

    return (
      <CardContent className={classes.postContent}>
        <div className={classes.mediaContainer}>
          <Typography
            paragraph
            dangerouslySetInnerHTML={{ __html: oldContent.innerHTML }}
          />
          {status.card ? (
            <div className={classes.postCard}>
              <Divider />
              <CardActionArea
                href={status.card.url}
                target="_blank"
                rel="noreferrer"
              >
                <CardContent>
                  <Typography gutterBottom variant="h6" component="h2">
                    {status.card.title}
                  </Typography>
                  <Typography>
                    {status.card.description.slice(0, 500) +
                      (status.card.description.length > 500 ? "..." : "") ||
                      "No description provided. Click with caution."}
                  </Typography>
                </CardContent>
                {status.card.image && status.media_attachments.length <= 0 ? (
                  <CardMedia
                    className={classes.postMedia}
                    image={status.card.image}
                  />
                ) : (
                  <span />
                )}
                <CardContent>
                  <Typography>
                    {status.card.provider_url ||
                      status.card.author_url ||
                      status.card.author_url}
                  </Typography>
                </CardContent>
              </CardActionArea>
              <Divider />
            </div>
          ) : (
            <span />
          )}
          {status.media_attachments.length > 0 ? (
            <AttachmentComponent media={status.media_attachments} />
          ) : (
            <span />
          )}
          {status.poll ? (
            status.poll.voted || status.poll.expired ? (
              <div>
                <Typography variant="caption">
                  You can't vote on this poll. Below are the results of the
                  poll.
                </Typography>
                <RadioGroup value={this.findBiggestVote()}>
                  {status.poll.options.map((pollOption: PollOption) => {
                    let x = (
                      <FormControlLabel
                        disabled
                        value={pollOption.title}
                        control={<Radio />}
                        label={`${pollOption.title} (${pollOption.votes_count} votes)`}
                        key={pollOption.title + pollOption.votes_count}
                      />
                    );
                    return x;
                  })}
                </RadioGroup>
                {status.poll && status.poll.expired ? (
                  <Typography variant="caption">
                    This poll has expired.
                  </Typography>
                ) : (
                  <Typography variant="caption">
                    This poll will expire on{" "}
                    {moment(
                      status.poll.expires_at ? status.poll.expires_at : ""
                    ).format("MMMM Do YYYY, [at] h:mm A")}
                    .
                  </Typography>
                )}
              </div>
            ) : (
              <div>
                <RadioGroup
                  onChange={(event: any, option: any) =>
                    this.captureVote(option)
                  }
                >
                  {status.poll.options.map((pollOption: PollOption) => {
                    let x = (
                      <FormControlLabel
                        value={pollOption.title}
                        control={<Radio />}
                        label={pollOption.title}
                        key={pollOption.title + pollOption.votes_count}
                      />
                    );
                    return x;
                  })}
                </RadioGroup>
                <Button
                  color="primary"
                  onClick={(event: any) => this.submitVote()}
                >
                  Vote
                </Button>
              </div>
            )
          ) : null}
        </div>
      </CardContent>
    );
  }

  spoilerContainsFlags(text: string): boolean {
    let unsafeFlags = ["NSFW", "nsfw", "lewd", "sex"];
    let result: boolean = false;
    unsafeFlags.forEach((flag: string) => {
      if (text.includes(flag)) {
        result = true;
      }
    });
    return result;
  }

  getSensitiveContent(spoiler_text: string, content: Status) {
    const { classes } = this.props;
    const warningText = spoiler_text || "Unmarked content";
    let icon;
    if (
      this.spoilerContainsFlags(spoiler_text) ||
      spoiler_text.includes("Spoiler") ||
      warningText === "Unmarked content"
    ) {
      icon = <WarningIcon className={classes.postWarningIcon} />;
    }
    return (
      <ExpansionPanel
        className={
          this.spoilerContainsFlags(spoiler_text)
            ? classes.nsfwCard
            : classes.spoilerCard
        }
        color="inherit"
      >
        <ExpansionPanelSummary expandIcon={<ExpandMoreIcon />} color="inherit">
          {icon}
          <Typography>{warningText}</Typography>
        </ExpansionPanelSummary>
        <ExpansionPanelDetails className={classes.postContent} color="inherit">
          {this.materializeContent(content)}
        </ExpansionPanelDetails>
      </ExpansionPanel>
    );
  }

  getReblogOfPost(of: Status | null) {
    const { classes } = this.props;
    if (of !== null) {
      return of.sensitive
        ? this.getSensitiveContent(of.spoiler_text, of)
        : this.materializeContent(of);
    } else {
      return null;
    }
  }

  getReblogAuthors(post: Status) {
    const { classes } = this.props;
    if (post.reblog) {
      let author = post.reblog.account;
      let origString = `<span>${author.display_name || author.username} (@${
        author.acct
      }) 🔄 ${post.account.display_name || post.account.username}</span>`;
      let emojis = author.emojis;
      emojis.concat(post.account.emojis);
      return emojifyString(origString, emojis, classes.postAuthorEmoji);
    } else {
      let author = post.account;
      let origString = `<span>${author.display_name || author.username} (@${
        author.acct
      })</span>`;
      return emojifyString(origString, author.emojis, classes.postAuthorEmoji);
    }
  }

  getMentions(mention: [Mention]) {
    const { classes } = this.props;
    if (mention.length > 0) {
      return (
        <CardContent className={classes.postTags}>
          <Typography variant="caption">Mentions</Typography>
          {mention.map((person: Mention) => {
            return (
              <LinkableChip
                avatar={
                  <Avatar>
                    <AlternateEmailIcon />
                  </Avatar>
                }
                label={person.username}
                key={this.state.post.id + "_mention_" + person.id}
                to={`/profile/${person.id}`}
                className={classes.postMention}
                clickable
              />
            );
          })}
        </CardContent>
      );
    } else {
      return null;
    }
  }

  getTags(tags: [Tag]) {
    const { classes } = this.props;
    if (tags.length > 0) {
      return (
        <CardContent className={classes.postTags}>
          <Typography variant="caption">Tags</Typography>
          {tags.map((tag: Tag) => {
            return (
              <LinkableChip
                avatar={
                  <Avatar>
                    <LocalOfferIcon />
                  </Avatar>
                }
                label={tag.name}
                key={this.state.post.id + "_tag_" + tag.name}
                to={`/search?query=${tag.name}&type=tag`}
                className={classes.postMention}
                clickable
              />
            );
          })}
        </CardContent>
      );
    } else {
      return null;
    }
  }

  showVisibilityIcon(visibility: Visibility) {
    const { classes } = this.props;
    switch (visibility) {
      case "public":
        return (
          <Tooltip title="Public">
            <PublicIcon className={classes.postTypeIcon} />
          </Tooltip>
        );
      case "private":
        return (
          <Tooltip title="Followers only">
            <GroupIcon className={classes.postTypeIcon} />
          </Tooltip>
        );
      case "unlisted":
        return (
          <Tooltip title="Unlisted (invisible from public timeline)">
            <VisibilityOffIcon className={classes.postTypeIcon} />
          </Tooltip>
        );
    }
  }

  getMastodonUrl(post: Status) {
    let url = "";
    if (post.reblog) {
      url = post.reblog.uri;
    } else {
      url = post.uri;
    }
    return url;
  }

  toggleFavorited(post: Status) {
    let _this = this;
    if (post.favourited) {
      this.client
        .post(`/statuses/${post.id}/unfavourite`)
        .then((resp: any) => {
          let post: Status = resp.data;
          this.setState({ post });
        })
        .catch((err: Error) => {
          _this.props.enqueueSnackbar(`Couldn't unfavorite post: ${err.name}`, {
            variant: "error"
          });
          console.log(err.message);
        });
    } else {
      this.client
        .post(`/statuses/${post.id}/favourite`)
        .then((resp: any) => {
          let post: Status = resp.data;
          this.setState({ post });
        })
        .catch((err: Error) => {
          _this.props.enqueueSnackbar(`Couldn't favorite post: ${err.name}`, {
            variant: "error"
          });
          console.log(err.message);
        });
    }
  }

  toggleReblogged(post: Status) {
    if (post.reblogged) {
      this.client
        .post(`/statuses/${post.id}/unreblog`)
        .then((resp: any) => {
          let post: Status = resp.data;
          this.setState({ post });
        })
        .catch((err: Error) => {
          this.props.enqueueSnackbar(`Couldn't unboost post: ${err.name}`, {
            variant: "error"
          });
          console.log(err.message);
        });
    } else {
      this.client
        .post(`/statuses/${post.id}/reblog`)
        .then((resp: any) => {
          let post: Status = resp.data;
          this.setState({ post });
        })
        .catch((err: Error) => {
          this.props.enqueueSnackbar(`Couldn't boost post: ${err.name}`, {
            variant: "error"
          });
          console.log(err.message);
        });
    }
  }

  showDeleteDialog() {
    return (
      <Dialog
        open={this.state.deletePostDialog}
        onClose={() => this.togglePostDeleteDialog()}
      >
        <DialogTitle id="alert-dialog-title">Delete this post?</DialogTitle>
        <DialogContent>
          <DialogContentText id="alert-dialog-description">
            Are you sure you want to delete this post? This action cannot be
            undone.
          </DialogContentText>
        </DialogContent>
        <DialogActions>
          <Button
            onClick={() => this.togglePostDeleteDialog()}
            color="primary"
            autoFocus
          >
            Cancel
          </Button>
          <Button
            onClick={() => {
              this.deletePost();
              this.togglePostDeleteDialog();
            }}
            color="primary"
          >
            Delete
          </Button>
        </DialogActions>
      </Dialog>
    );
  }

  render() {
    const { classes } = this.props;
    const post = this.state.post;
    return (
      <Zoom in={true}>
        <Card className={classes.post} id={`post_${post.id}`}>
          <CardHeader
            avatar={
              <LinkableAvatar
                to={`/profile/${
                  post.reblog ? post.reblog.account.id : post.account.id
                }`}
                src={
                  post.reblog
                    ? post.reblog.account.avatar_static
                    : post.account.avatar_static
                }
              />
            }
            action={
              <Tooltip title="More">
                <IconButton
                  key={`${post.id}_submenu`}
                  id={`${post.id}_submenu`}
                  onClick={() => this.togglePostMenu()}
                >
                  <MoreVertIcon />
                </IconButton>
              </Tooltip>
            }
            title={
              <Typography
                dangerouslySetInnerHTML={{
                  __html: this.getReblogAuthors(post)
                }}
              ></Typography>
            }
            subheader={moment(post.created_at).format(
              "MMMM Do YYYY [at] h:mm A"
            )}
          />
          {post.reblog ? this.getReblogOfPost(post.reblog) : null}
          {post.sensitive
            ? this.getSensitiveContent(post.spoiler_text, post)
            : post.reblog
            ? null
            : this.materializeContent(post)}
          {post.reblog && post.reblog.mentions.length > 0
            ? this.getMentions(post.reblog.mentions)
            : this.getMentions(post.mentions)}
          {post.reblog && post.reblog.tags.length > 0
            ? this.getTags(post.reblog.tags)
            : this.getTags(post.tags)}
          <CardActions>
            <Tooltip title="Reply">
              <LinkableIconButton
                to={`/compose?reply=${
                  post.reblog ? post.reblog.id : post.id
                }&visibility=${post.visibility}&acct=${
                  post.reblog ? post.reblog.account.acct : post.account.acct
                }`}
              >
                <ReplyIcon />
              </LinkableIconButton>
            </Tooltip>
            <Typography>
              {post.reblog ? post.reblog.replies_count : post.replies_count}
            </Typography>
            <Tooltip title="Favorite">
              <IconButton onClick={() => this.toggleFavorited(post)}>
                <FavoriteIcon
                  className={
                    post.reblog
                      ? post.reblog.favourited
                        ? classes.postDidAction
                        : ""
                      : post.favourited
                      ? classes.postDidAction
                      : ""
                  }
                />
              </IconButton>
            </Tooltip>
            <Typography>
              {post.reblog
                ? post.reblog.favourites_count
                : post.favourites_count}
            </Typography>
            <Tooltip title="Boost">
              <IconButton onClick={() => this.toggleReblogged(post)}>
                <AutorenewIcon
                  className={
                    post.reblog
                      ? post.reblog.reblogged
                        ? classes.postDidAction
                        : ""
                      : post.reblogged
                      ? classes.postDidAction
                      : ""
                  }
                />
              </IconButton>
            </Tooltip>
            <Typography>
              {post.reblog ? post.reblog.reblogs_count : post.reblogs_count}
            </Typography>
            <Tooltip className={classes.desktopOnly} title="View thread">
              <LinkableIconButton
                to={`/conversation/${post.reblog ? post.reblog.id : post.id}`}
              >
                <ForumIcon />
              </LinkableIconButton>
            </Tooltip>
            <Tooltip className={classes.desktopOnly} title="Open in Web">
              <IconButton
                href={this.getMastodonUrl(post)}
                rel="noreferrer"
                target="_blank"
              >
                <OpenInNewIcon />
              </IconButton>
            </Tooltip>
            <div className={classes.postFlexGrow} />
            <div className={classes.postTypeIconDiv}>
              {this.showVisibilityIcon(post.visibility)}
            </div>
          </CardActions>
          <Menu
            id="postmenu"
            anchorEl={document.getElementById(`${post.id}_submenu`)}
            open={this.state.menuIsOpen}
            onClose={() => this.togglePostMenu()}
          >
            <ShareMenu
              config={{
                params: {
                  title: `@${post.account.username} posted on Mastodon: `,
                  text: post.content,
                  url: this.getMastodonUrl(post)
                },
                onShareSuccess: () =>
                  this.props.enqueueSnackbar("Post shared!", {
                    variant: "success"
                  }),
                onShareError: (error: Error) => {
                  if (error.name != "AbortError")
                    this.props.enqueueSnackbar(
                      `Couldn't share post: ${error.name}`,
                      { variant: "error" }
                    );
                }
              }}
            />
            {post.reblog ? (
              <div>
                <LinkableMenuItem to={`/profile/${post.reblog.account.id}`}>
                  View author profile
                </LinkableMenuItem>
                <LinkableMenuItem to={`/profile/${post.account.id}`}>
                  View reblogger profile
                </LinkableMenuItem>
              </div>
            ) : (
              <LinkableMenuItem to={`/profile/${post.account.id}`}>
                View profile
              </LinkableMenuItem>
            )}
            <div className={classes.mobileOnly}>
              <Divider />
              <LinkableMenuItem
                to={`/conversation/${post.reblog ? post.reblog.id : post.id}`}
              >
                View thread
              </LinkableMenuItem>
              <MenuItem
                component="a"
                href={this.getMastodonUrl(post)}
                rel="noreferrer"
                target="_blank"
              >
                Open in Web
              </MenuItem>
            </div>
            {post.account.id ==
            JSON.parse(localStorage.getItem("account") as string).id ? (
              <div>
                <Divider />
                <MenuItem onClick={() => this.togglePostDeleteDialog()}>
                  Delete
                </MenuItem>
              </div>
            ) : null}
            {this.showDeleteDialog()}
          </Menu>
        </Card>
      </Zoom>
    );
  }
}

export default withStyles(styles)(withSnackbar(Post));
