/** @jsx h */

import { h, render, Component, Fragment } from 'preact';
import map from 'lodash/map';
// import memoize from 'lodash/memoize';
import { format } from 'date-fns';
import Sync from 'svg/sync-alt.svg';
import Link from 'svg/link.svg';
import AngleLeft from 'svg/angle-left.svg';
import AngleRight from 'svg/angle-right.svg';
import AngleDoubleLeft from 'svg/angle-double-left.svg';
import AngleDoubleRight from 'svg/angle-double-right.svg';

// const If = ({t,children}) => (!!t && <Fragment>{children}</Fragment>)

const Raw = ({ html }) => <div dangerouslySetInnerHTML={{ __html: html }} />;

const Post = ({ post }) => (
  <article>
    <div class="post-head">
      <div class="post-tags">
        {map(post.tags, (v, k) => <a href={'/tweets/#tag=' + k} class="post-link tag">{v}</a>)}
        {map(post.author, (v) => <a href={'/tweets/#author=' + v} class="post-link author">{v}</a>)}
      </div>
      <a href={post.url} class="post-link" title={format(new Date(post.date), 'MMMM do, yyyy')}><span class="svg-icon"><Link /></span> Permalink</a>
    </div>
    <div class="post-content">{post.content ? <Raw html={post.content} /> : <div class="loading"><Sync /></div>}</div>
  </article>
);

const Pagination = ({ post }) => (
  <div class="pager">
    <div class="prev" >{post.siblings && post.siblings.prev  && <a href={'/tweets/#id=' + post.siblings.prev.replace('/tweets/', '')} class="btn btn-primary left"><span class="svg-icon"><AngleLeft /></span> Back</a>}</div>
    <div class="first">{post.siblings && post.siblings.first && <a href={'/tweets/#id=' + post.siblings.first.replace('/tweets/', '')} class="btn btn-primary left"><span class="svg-icon"><AngleDoubleLeft /></span> Newest</a>}</div>
    <div class="last" >{post.siblings && post.siblings.last  && <a href={'/tweets/#id=' + post.siblings.last.replace('/tweets/', '')} class="btn btn-primary right">Oldest <span class="svg-icon"><AngleDoubleRight /></span></a>}</div>
    <div class="next" >{post.siblings && post.siblings.next  && <a href={'/tweets/#id=' + post.siblings.next.replace('/tweets/', '')} class="btn btn-primary right">Next <span class="svg-icon"><AngleRight /></span></a>}</div>
  </div>
);

class App extends Component {

  constructor (props) {
    super(props);

    const hash = window.location.hash.slice(1);
    const index = this.props.index;
    if (index) {
      this.state = {
        hash,
        loading: false,
        posts: Object.fromEntries(index.posts.map((post) => [ post.id, post ])),
        tags: index.tags,
        authors: index.authors,
        latest: index.latest,
      };
    } else {
      this.state = {
        hash,
        loading: true,
        posts: {},
        tags: {},
        authors: [],
        latest: null,
      };
      this.loadContent().catch(console.error); // eslint-disable-line no-console
    }

    this.loading = new Map();

    this.onChange = this.onChange.bind(this);
    this.hashedPosts = this.hashedPosts.bind(this);
    this.ensurePost = this.ensurePost.bind(this);
  }

  async loadContent () {
    const index = await fetch('/tweets/index.json').then((res) => res.json());
    this.setState({
      loading: false,
      posts: Object.fromEntries(index.posts.map((post) => [ post.id, post ])),
      tags: index.tags,
      authors: index.authors,
      latest: index.latest,
    });
  }

  componentDidMount () {
    window.addEventListener('hashchange', this.onChange);
  }

  componentWillUnmount () {
    window.removeEventListener('hashchange', this.onChange);
  }

  onChange () {
    this.setState({
      hash: window.location.hash.slice(1),
      prevHash: this.state.hash,
    });
  }

  parseHash () {
    return this.state.hash && String(this.state.hash).split('=').filter(Boolean) || [];
  }

  hashedPosts (target, value) {
    const posts = Object.values(this.state.posts);
    if (!target && !value) return [ this.state.latest ];
    return posts.filter((post) => {
      // console.log({ post, target, value })
      if (target === 'tag') return !!post.tags[value];
      if (target === 'author') return post.author.includes(value);
      return false;
    });
  }

  ensurePost ({ id, json }) {
    if (this.loading.has(id)) return this.loading.get(id);
    const p = fetch(json)
      .then((res) => res.json())
      .then((post) => {
        this.setState({
          posts: { ...this.state.posts, [post.id]: post },
        });
      })
      .catch(console.error); // eslint-disable-line no-console

    this.loading.set(id, p);
    return p;
  }

  render () {
    const [ target, value ] = this.parseHash();
    let posts = [ this.state.latest ];
    let paginate = true;
    if (target === 'id' && this.state.posts[value]) {
      posts = [ this.state.posts[value] ];
    } else if (target) {
      posts = this.hashedPosts(target, value);
      paginate = false;
    }

    if (this.state.loading) {
      return <div class="loading"><Sync /></div>;
    }

    posts.forEach(this.ensurePost);

    let caption = null;
    if (target === 'tag')    caption = <h4>Threads about {this.state.tags[value] || value}</h4>;
    if (target === 'author') caption = <h4>Tweets by {value}</h4>;

    return (
      <Fragment>
        {caption}
        {map(posts, (post, i) =>
          <Post post={post} key={i} />,
        )}
        {paginate && <Pagination post={posts[0]} />}
      </Fragment>
    );
  }
}


async function run () {
  const target = document.querySelector('.post-index section');

  let index = null;
  if (window.location.hash.length <= 1) {
    index = await fetch('/tweets/index.json').then((res) => res.json());
  }

  while (target.firstChild) target.removeChild(target.firstChild);
  render(<App index={index} />, target);
}

run().catch(console.error); // eslint-disable-line
