var React = require('react')
var ReactDOM = require('react-dom')
var Router = require('react-router').Router
var Route = require('react-router').Route
var IndexRoute = require('react-router').IndexRoute
var Redirect = require('react-router').Redirect
var Link = require('react-router').Link

var MarkdownLib = require('react-markdown')
var moment = require('moment')
var sortedIndex = require('lodash.sortedindex')
var BoardsAPI = require('../lib/boards-api.js')

var opt, s = localStorage.getItem('ipfs-boards-settings')
try {
  opt = JSON.parse(s)
} catch(e){
  // Do nothing
}
if(opt === null || opt === undefined) opt = { addr: 'localhost', port: 5001 }
var ipfs = require('ipfs-api')(opt.addr || 'localhost',opt.port || 5001)
var boards = new BoardsAPI(ipfs)

// Components

var Markdown = React.createClass({
  renderIfApplicable: function(){
    if(this.props.source)
      return <MarkdownLib source={this.props.source} skipHtml={true} />
    return <p>...</p>
  },
  render: function(){
    return this.renderIfApplicable()
  }
})

var Icon = React.createClass({
  class: function(){
    return 'fa fa-'+this.props.name+' '+this.props.className
  },
  render: function(){
    return ( <i className={this.class()}></i> )
  }
})

var Container = React.createClass({
  render: function(){
    return ( <div className="container app">{this.props.children}</div> )
  }
})

var App = React.createClass({
  render: function(){
    return ( <div><Navbar /><Container>{this.props.children}</Container></div> )
  }
})

var Navbar = React.createClass({
  render: function(){
    return (
      <div className="navbar">
        <div className="container">
          {this.props.children || <h4><Link to="/"><Icon name="comments" className="light"/> Boards</Link></h4>}
          <div className="u-pull-right iconbar">
            <Link className="nounderline" to="/@me"><Icon name="user" className="fa-2x light"/></Link>
            <Link className="nounderline" to="/users"><Icon name="globe" className="fa-2x light"/></Link>
            <Link className="nounderline" to="/settings"><Icon name="cog" className="fa-2x light"/></Link>
            <a className="nounderline" href="https://github.com/fazo96/ipfs-boards"><Icon name="github" className="fa-2x light"/></a>
          </div>
        </div>
      </div>)
  }
})

var Post = React.createClass({
  getDate: function(){
    if(this.props.post.date){
      return moment.unix(this.props.post.date).fromNow()
    } else {
      return 'Unknown Date'
    }
  },
  render: function(){
    return <div key={this.props.post.title} className="post">
      <div className="content">
        <h5>{this.props.post.title}</h5><hr/>
        <Markdown source={this.props.post.text} skipHtml={true} /><hr/>
        <div className="icons">
          <UserID id={this.props.post.op}></UserID>
          <Icon name="clock-o" className="not-first"/> {this.getDate()}
          <Icon name="comments" className="not-first" /> Comments
        </div>
      </div>
    </div>
  }
})

var PostList = React.createClass({
  getInitialState: function(){
    return { posts: [] }
  },
  sortFn: function(a,b){
    return (b.date || 0) - (a.date || 0)
  },
  componentDidMount: function(){
    console.log('Initial POSTS',this.state.posts.length)
    boards.getPostsInBoard(this.props.admin,this.props.board)
    .on('post in '+this.props.board+'@'+this.props.admin,(post,hash) => {
      if(!this.isMounted()) return true
      var now = moment().unix()
      var posts = this.state.posts
      if(post.date === undefined || post.date <= 0){
        posts.push(post)
      } else if(post.date <= now){
        var i = sortedIndex(posts,post,(p) => now-p.date || now)
        posts.splice(i,0,post)
      } else {
        console.log('Post discarded cause date in the future:',post)
      }
      this.setState({ posts })
    })
  },
  render: function(){
    return (
      <div className="postList">
        {this.state.posts.map(post => {
          return <Post key={post.title+post.text} post={post} />
        })}
      </div>
    )
  }
})

var UserID = React.createClass({
  getInitialState: function(){
    return { }
  },
  componentDidMount: function(){
    if(this.props.id) boards.getProfile(this.props.id, (err,res) => {
      if(!this.isMounted()) return true
      if(!err) {
        this.setState({ name: res.name.trim() })
      }
    })
  },
  getContent: function(){
    if(this.state.name){
      return (<Icon name="user" />)
    } else {
      return '@'
    }
  },
  render: function(){
    if(this.props.id)
      return (<div className="user-id">
        <Link className="light nounderline" to={'/@'+this.props.id}>
          {this.getContent()}{this.state.name || this.props.id}
        </Link>
      </div>)
    else return <div className="user-id">
        <Icon name="ban" /> Unknown User
      </div>
  }
})

// Static pages

var Homepage = React.createClass({
  render: function(){
    return (
      <div>
        <h3>Welcome to the IPFS Boards Prototype</h3>
        <p>Not much is implemented...</p>
        <p>You can try <Link to="@QmXnfA41SXMX3tqFD4kjED7ehyvgTsuAho86TkEoTbZdpw">Opening my Profile</Link> though :)</p>
        <p>More information about the project on <a href="https://github.com/fazo96/ipfs-board">GitHub</a></p>
      </div>
    )
  }
})

var GetIPFS = React.createClass({
  render: function(){
    return (
      <div className="">
        <h1><Icon name="ban"/> Missing IPFS Node</h1>
        <p>You don't have an IPFS node running at <code>{opt.addr}:{opt.port}</code> or it is not reachable</p>
        <p>The IPFS Boards prototype requires a full IPFS node running at localhost.
        Please start one by following the
        <a href="https://github.com/ipfs/go-ipfs"><code>go-ipfs</code> documentation.</a></p>
        <h5>Do you have a running node but the app won't work?</h5>
        <p>It's probably one of these issues:</p>
        <ul>
          <li>Your IPFS node doesn't allow requests from the domain you're running the app from (CORS issue). See <a href="https://github.com/fazo96/ipfs-board/blob/master/ipfs_daemon_set_cors.sh">here</a> for the fix.</li>
          <li>Your IPFS node is not listening for API requests at <code>{opt.addr}:{opt.port}</code>. Go to the <Link to="/settings">Settings page</Link>, provide the correct address for the node, then save and reload the page.</li>
        <li>Some other networking issue is preventing the App from talking to your node.</li>
        </ul>
        <p>Still can't fix it? <a href="https://github.com/fazo96/ipfs-board/issues">File a issue on GitHub</a>, we'll be happy to help!</p>
      </div>
    )
  }
})

var NotFound = React.createClass({
  render: function(){
    return (<div className="text-center">
      <h1><Icon name="ban"/></h1>
      <p>Sorry, there's nothing here!</p>
    </div>)
  }
})

var NotImplemented = React.createClass({
  render: function(){
    return ( <div className="text-center">
      <h1>Not yet implemented</h1>
      <h1><Icon name="cog" className="fa-spin"/></h1>
      <p>Sorry, working on it!</p>
    </div> )
  }
})

// Dynamic pages

var Profile = React.createClass({
  getInitialState: function(){
    return { name: '...', boards: [] }
  },
  componentDidMount: function(){
    console.log('About to ask for profile for',this.props.params.userid)
    var ee = boards.getEventEmitter()
    ee.on('boards for '+this.props.params.userid,l => {
      if(!this.isMounted()) return true
      this.setState({ boards: l })
    })
    boards.getProfile(this.props.params.userid,(err,res) => {
      if(!this.isMounted()) return true
      if(err){
        this.setState({
          name: <Icon name="ban" />,
          description: err
        })
      } else {
        this.setState({ name: res.name, description: res.description })
      }
    })
  },
  linkToEditor: function(){
    if(this.props.params.userid === boards.id){
      return <div>
        <h6>This is your profile</h6>
        <hr/>
      </div>
    }
    return ''
  },
  render: function(){
    return (<div className="profile">
      {this.linkToEditor()}
      <h1>{this.state.name}</h1>
      <Markdown source={this.state.description} skipHtml={true} />
      <hr/>
      <h5 className="light">@{this.props.params.userid}</h5>
      {this.state.boards.map(n => {
        return <h6 className="light" key={this.props.params.userid+'/'+n.name}>
          <Link to={'/@'+this.props.params.userid+'/'+n.name}># {n.name}</Link>
        </h6>
      })}
    </div>)
  }
})

var Board = React.createClass({
  getInitialState: function(){
    return { name: this.props.params.boardname }
  },
  componentDidMount: function(){
    var ee = boards.getBoardSettings(this.props.params.userid,this.props.params.boardname)
    ee.on('settings for '+this.props.params.boardname+'@'+this.props.params.userid, (res) => {
      if(!this.isMounted()) return true
      console.log('Found name:',res.fullname)
      this.setState({ name: res.fullname.trim(), description: res.description })
    })
  },
  render: function(){
    return (<div className="board">
      <h2>{this.state.name}</h2>
      <Markdown source={this.state.description} skipHtml={true} />
      <h5><UserID id={this.props.params.userid} /></h5>
      <PostList board={this.props.params.boardname} admin={this.props.params.userid}/>
    </div>)
  }
})

var Users = React.createClass({
  getInitialState: function(){
    return { users: boards.getUsers() }
  },
  componentDidMount: function(){
    boards.searchUsers().on('user',(id) => {
      if(this.isMounted() && this.state.users.indexOf(id) < 0)
        this.setState({ users: this.state.users.concat(id) })
    })
  },
  render: function(){
    return <div>
      <h1><Icon name="users" /> Users</h1>
      <p>Found <b>{this.state.users.length}</b> users</p>
      <ul>
        {this.state.users.map(user => {
          return <UserID key={user} id={user} />
        })}
      </ul>
    </div>
  }
})

var Settings = React.createClass({
  getDefaults: function(){
    return { addr: 'localhost', port: 5001 }
  },
  getInitialState: function(){
    var s = localStorage.getItem('ipfs-boards-settings')
    var obj = this.getDefaults()
    try {
      obj = JSON.parse(s)
    } catch(e){
      localStorage.removeItem('ipfs-boards-settings')
    }
    return obj || this.getDefaults()
  },
  save: function(){
    if(isNaN(this.state.port) || parseInt(this.state.port) > 65535 || parseInt(this.state.port) < 1){
      alert('Port number invalid')
    } else {
      localStorage.setItem('ipfs-boards-settings',JSON.stringify({
        addr: this.state.addr,
        port: parseInt(this.state.port)
      }))
      alert('Saved')
    }
  },
  setDefaults: function(){
    this.setState(this.getDefaults())
  },
  onChange: function(event){
    if(event.target.id === 'nodeAddress'){
      this.setState({ addr: event.target.value })
    } else {
      this.setState({ port: event.target.value })
    }
  },
  render: function(){
    return (
      <div className="settings">
        <h2><Icon name="cog"/> Settings</h2>
        <h5>This page is still a little rough, but it works. Reload the page after saving to apply changes.</h5>
        <p>Use this page to customize the application's behavior. For now, you can change how it connects to IPFS.</p>
        <p>All settings are saved in your browser.</p>
        <div className="row">
          <div className="six columns">
            <label htmlFor="nodeAddress">IPFS Node</label>
            <input className="u-full-width" type="text" id="nodeAddress" value={this.state.addr} onChange={this.onChange} placeholder="localhost" />
          </div>
          <div className="six columns">
            <label htmlFor="nodePort">API Port</label>
            <input className="u-full-width" type="text" id="nodePort" value={this.state.port} onChange={this.onChange} placeholder="5001" />
          </div>
        </div>
        <div className="buttons">
          <button className="button button-primary" onClick={this.save}>Save</button>
          <button className="button not-first" onClick={this.setDefaults}>Defaults</button>
        </div>
      </div>
    )
  }
})

// Start

boards.init(err => {
  if(err){
    console.log('FATAL: IPFS NODE NOT AVAILABLE')
    ReactDOM.render(
      <Router>
        <Route path="/" component={App}>
          <IndexRoute component={GetIPFS} />
          <Route path="/settings" component={Settings} />
        </Route>
      </Router>
    , document.getElementById('root'))
  } else {
    ReactDOM.render(
      <Router>
        <Route path="/" component={App}>
          <IndexRoute component={Homepage} />
          <Redirect from="/@me" to={'/@'+boards.id} />
          <Route path="/@:userid">
            <IndexRoute component={Profile} />
            <Route path=":boardname" component={Board} />
          </Route>
          <Route path="/users" component={Users} />
          <Route path="/settings" component={Settings} />
          <Route path="*" component={NotFound} />
        </Route>
      </Router>, document.getElementById('root')
    )
  }
})