import React, { Component } from "react";
import {
  withStyles,
  CircularProgress,
  Typography,
  Paper,
  Button,
  Chip,
  Avatar,
  Slide
} from "@material-ui/core";
import { styles } from "./PageLayout.styles";
import Post from "../components/Post";
import { Status } from "../types/Status";
import Mastodon, { StreamListener } from "megalodon";
import { withSnackbar } from "notistack";
import ArrowUpwardIcon from "@material-ui/icons/ArrowUpward";

interface IPublicPageState {
  posts?: [Status];
  backlogPosts?: [Status] | null;
  viewIsLoading: boolean;
  viewDidLoad?: boolean;
  viewDidError?: boolean;
  viewDidErrorCode?: any;
}

class PublicPage extends Component<any, IPublicPageState> {
  client: Mastodon;
  streamListener: StreamListener;

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

    this.state = {
      viewIsLoading: true,
      backlogPosts: null
    };

    this.client = new Mastodon(
      localStorage.getItem("access_token") as string,
      (localStorage.getItem("baseurl") as string) + "/api/v1"
    );
    this.streamListener = this.client.stream("/streaming/public");
  }

  componentWillMount() {
    this.streamListener.on("connect", () => {
      this.client
        .get("/timelines/public", { limit: 40 })
        .then((resp: any) => {
          let statuses: [Status] = resp.data;
          this.setState({
            posts: statuses,
            viewIsLoading: false,
            viewDidLoad: true,
            viewDidError: false
          });
        })
        .catch((resp: any) => {
          this.setState({
            viewIsLoading: false,
            viewDidLoad: true,
            viewDidError: true,
            viewDidErrorCode: String(resp)
          });
          this.props.enqueueSnackbar("Failed to get posts.", {
            variant: "error"
          });
        });
    });

    this.streamListener.on("update", (status: Status) => {
      let queue = this.state.backlogPosts;
      if (queue !== null && queue !== undefined) {
        queue.unshift(status);
      } else {
        queue = [status];
      }
      this.setState({ backlogPosts: queue });
    });

    this.streamListener.on("delete", (id: number) => {
      let posts = this.state.posts;
      if (posts) {
        posts.forEach((post: Status) => {
          if (posts && parseInt(post.id) === id) {
            posts.splice(posts.indexOf(post), 1);
          }
        });
        this.setState({ posts });
      }
    });

    this.streamListener.on("error", (err: Error) => {
      this.setState({
        viewDidError: true,
        viewDidErrorCode: err.message
      });
      this.props.enqueueSnackbar("An error occured.", {
        variant: "error"
      });
    });

    this.streamListener.on("heartbeat", () => {});
  }

  componentWillUnmount() {
    this.streamListener.stop();
  }

  insertBacklog() {
    window.scrollTo(0, 0);
    let posts = this.state.posts;
    let backlog = this.state.backlogPosts;
    if (posts && backlog && backlog.length > 0) {
      let push = backlog.concat(posts);
      this.setState({ posts: push as [Status], backlogPosts: null });
    }
  }

  loadMoreTimelinePieces() {
    this.setState({ viewDidLoad: false, viewIsLoading: true });
    if (this.state.posts) {
      this.client
        .get("/timelines/public", {
          max_id: this.state.posts[this.state.posts.length - 1].id,
          limit: 20
        })
        .then((resp: any) => {
          let newPosts: [Status] = resp.data;
          let posts = this.state.posts as [Status];
          newPosts.forEach((post: Status) => {
            posts.push(post);
          });
          this.setState({
            viewIsLoading: false,
            viewDidLoad: true,
            posts
          });
        })
        .catch((err: Error) => {
          this.setState({
            viewIsLoading: false,
            viewDidError: true,
            viewDidErrorCode: err.message
          });
          this.props.enqueueSnackbar("Failed to get posts", {
            variant: "error"
          });
        });
    }
  }

  render() {
    const { classes } = this.props;

    return (
      <div className={classes.pageLayoutMaxConstraints}>
        {this.state.backlogPosts ? (
          <div className={classes.pageTopChipContainer}>
            <div className={classes.pageTopChips}>
              <Slide direction="down" in={true}>
                <Chip
                  avatar={
                    <Avatar>
                      <ArrowUpwardIcon />
                    </Avatar>
                  }
                  label={`View ${this.state.backlogPosts.length} new post${
                    this.state.backlogPosts.length > 1 ? "s" : ""
                  }`}
                  color="primary"
                  className={classes.pageTopChip}
                  onClick={() => this.insertBacklog()}
                  clickable
                />
              </Slide>
            </div>
          </div>
        ) : null}
        {this.state.posts ? (
          <div>
            {this.state.posts.map((post: Status) => {
              return <Post key={post.id} post={post} client={this.client} />;
            })}
            <br />
            {this.state.viewDidLoad && !this.state.viewDidError ? (
              <div
                style={{ textAlign: "center" }}
                onClick={() => this.loadMoreTimelinePieces()}
              >
                <Button variant="contained">Load more</Button>
              </div>
            ) : null}
          </div>
        ) : (
          <span />
        )}
        {this.state.viewDidError ? (
          <Paper className={classes.errorCard}>
            <Typography variant="h4">Bummer.</Typography>
            <Typography variant="h6">
              Something went wrong when loading this timeline.
            </Typography>
            <Typography>
              {this.state.viewDidErrorCode ? this.state.viewDidErrorCode : ""}
            </Typography>
          </Paper>
        ) : (
          <span />
        )}
        {this.state.viewIsLoading ? (
          <div style={{ textAlign: "center" }}>
            <CircularProgress className={classes.progress} color="primary" />
          </div>
        ) : (
          <span />
        )}
      </div>
    );
  }
}

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