import { render, Component, linkEvent } from 'inferno';
import * as moment from 'moment';

import { endpoint } from './env';
import { SearchParams, Results, State } from './interfaces';
import { convertCsvToJson, humanFileSize, magnetLink } from './utils';

import './Main.css';

const container = document.getElementById('app');

class TorrentSearchComponent extends Component<any, State> {

  state: State = {
    results: {
      torrents: []
    },
    searchParams: {
      q: "",
      page: 1
    },
    searching: false
  };

  constructor(props, context) {
    super(props, context);

  }

  search(i: TorrentSearchComponent, event) {
    event.preventDefault();

    if (!!i.state.searchParams.q) {
      i.setState({ searching: true, results: { torrents: [] } });
      i.fetchData(i.state.searchParams)
        .then(results => {
          if (!!results) {
            i.setState({
              results: results
            });
          }
        }).catch(error => {
          console.error('request failed', error);
        }).then(() => i.setState({ searching: false }));
    } else {
      i.setState({ results: { torrents: [] } });
    }
  }

  fetchData(searchParams: SearchParams): Promise<Results> {
    let q = encodeURI(searchParams.q);
    return fetch(`${endpoint}/service/search?q=${q}&page=${searchParams.page}`)
      .then(data => data.text())
      .then(csv => convertCsvToJson(csv));
  }

  render() {
    return (
      <div>
        {this.navbar()}
        <div className={this.state.results.torrents[0] ? "container-fluid" : "container"}>
          <div class="row mt-2">
            <div class="col-12">
              {
                this.state.searching ?
                  this.spinner()
                  :
                  this.state.results.torrents[0] ? this.table() : this.onboard()
              }
            </div>
          </div>
        </div>
      </div>
    );
  }

  table() {
    return (
      <div class="table-responsive-sm">
        <table class="table table-hover">
          <thead>
            <tr>
              <th>Name</th>
              <th class="text-right">Size</th>
              <th class="text-right">Seeds</th>
              <th class="text-right">Leeches</th>
              <th>Created</th>
              <th>Scraped</th>
              <th></th>
            </tr>
          </thead>
          <tbody>
            {this.state.results.torrents.map(torrent => (
              <tr>
                <td>{torrent.name}</td>
                <td class="text-right">{humanFileSize(torrent.size_bytes, true)}</td>
                <td class="text-right">{torrent.seeders}</td>
                <td class="text-right">{torrent.leechers}</td>
                <td>{moment(torrent.created_unix * 1000).fromNow()}</td>
                <td>{moment(torrent.scraped_date * 1000).fromNow()}</td>
                <td class="text-right">
                  <a href={magnetLink(torrent.infohash)}>
                    <i class="fas fa-magnet"></i>
                  </a>
                </td>
              </tr>
            ))}
          </tbody>
        </table>
        {this.paginator()}
      </div>
    );
  }

  navbar() {
    return (
      <nav class="navbar navbar-light bg-light">
        <a class="navbar-brand" href="#">
          <i class="fas fa-fw fa-database mr-1"></i>Torrents.csv
				</a>
        <div class="col-12 col-sm-6">
          {this.searchForm()}
        </div>
      </nav>
    );
  }

  // TODO
  // https://www.codeply.com/go/xBVaM3q5X4/bootstrap-4-navbar-search-full-width
  searchForm() {
    return (
      <form class="my-2 my-lg-0 d-inline w-100" onSubmit={linkEvent(this, this.search)}>
        <div class="input-group">
          <input value={this.state.searchParams.q} onInput={linkEvent(this, this.searchChange)} type="text" class="form-control border" placeholder="Search..." />
          <span class="input-group-append"></span>
          <button type="submit" class="btn btn-outline-secondary border border-left-0">
            <i className={(this.state.searching) ? "fas fa-spinner fa-spin" : "fas fa-fw fa-search"}></i>
          </button>
        </div>
      </form>
    );
  }

  spinner() {
    return (
      <div class="text-center">
        <i class="fas fa-spinner fa-spin fa-5x"></i>
      </div>
    );
  }

  onboard() {
    let site: string = "https://gitlab.com/dessalines/torrents.csv";
    return (
      <div>
        <a href={site}>Torrents.csv</a> is a collaborative, <b>vetted</b> git repository of torrents, consisting of a single, searchable <code>torrents.csv</code> file. Its initially populated with a January 2017 backup of the pirate bay, and new torrents are periodically added from various torrents sites via a rust script.<br></br><br></br>
        <a href={site}>Torrents.csv</a> will only store torrents with at least one seeder to keep the file small, and will be periodically purged of non-seeded torrents, and sorted by seeders descending.<br></br><br></br>
        To request more torrents, or add your own to the file, go <a href={site}>here</a>.<br></br><br></br>
        Made with <a href="https://www.rust-lang.org">Rust</a>, <a href="https://github.com/BurntSushi/ripgrep">ripgrep</a>, <a href="https://actix.rs/">Actix</a>, <a href="https://www.infernojs.org">Inferno</a>, and <a href="https://www.typescriptlang.org/">Typescript</a>.
			</div>
    );
  }

  paginator() {
    return (
      <nav>
        <ul class="pagination">
          <li className={(this.state.searchParams.page == 1) ? "page-item disabled" : "page-item"}>
            <button class="page-link"
              onClick={linkEvent({ i: this, nextPage: false }, this.switchPage)}
            >
              Previous</button>
          </li>
          <li class="page-item">
            <button class="page-link"
              onClick={linkEvent({ i: this, nextPage: true }, this.switchPage)}>
              Next
							</button>
          </li>
        </ul>
      </nav>
    );
  }

  searchChange(i, event) {
    let searchParams: SearchParams = {
      q: event.target.value,
      page: 1
    }
    i.setState({ searchParams: searchParams });
  }


  switchPage(a: { i: TorrentSearchComponent, nextPage: boolean }, event) {

    let newSearch = a.i.state.searchParams;
    newSearch.page += (a.nextPage) ? 1 : -1;
    a.i.setState({
      searchParams: newSearch
    });

    a.i.search(a.i, event);
  }

}

render(<TorrentSearchComponent />, container);
