import express from 'express';
import bodyParser from 'body-parser';
import distanceInWordsToNow from 'date-fns/distance_in_words_to_now';
import parseDate from 'date-fns/format';
import moment from 'moment';

import {STATUS_NORMAL, STATUS_RESERVED, STATUS_SOLD} from '../common/enums';

var hr = require ('date-fns/locale/hr');

var MongoClient = require ('mongodb').MongoClient;
var ObjectID = require ('mongodb').ObjectID;

var url = 'mongodb://localhost:27017/kivi';

require ('babel-polyfill');

const router = express.Router ({mergeParams: true});

const PORT = process.env.PORT || 3001;
const AGENTURA_KEY = process.env.AGENTURA_KEY || '1somethingverysecret';

let db;

router.post ('/contact/:listingId', async (req, res, next) => {
  try {
    const listingId = req.params.listingId;
    const body = req.body;

    const contactRequests = db.collection ('contact_requests');

    if (!body.email) {
      res.status (422);
      res.end ('Email is required');
      return;
    }

    if (!body.name) {
      res.status (422);
      res.end ('Name is required');
      return;
    }

    const result = await contactRequests.insertOne ({
      name: body.name,
      email: body.email,
      listingId,
      message: body.message,
      phone: body.phone,
      alert: body.alert,
    });

    res.status (200);
    res.end ();
  } catch (e) {
    console.log ('error:', e);
    next (e);
  }
});

router.get ('/search/listings/:id', async (req, res, next) => {
  try {
    const id = req.params.id;

    const listings = db.collection ('listings');
    const listing = await listings.findOne ({_id: new ObjectID (id)});
    if (listing) {
      res.json (listing);
    } else {
      res.status (404);
    }

    res.end ();
  } catch (e) {
    console.log ('error:', e);
    next (e);
  }
});

router.get ('/search/listings', async (req, res, next) => {
  try {
    console.log ('Search listings');
    const bounds = req.query.bounds || '';
    const minPrice = req.query.minPrice;
    const maxPrice = req.query.maxPrice;
    const minSize = req.query.minSize;
    const maxSize = req.query.maxSize;
    const rooms = req.query.rooms;
    const adType = req.query.adType;
    const category = req.query.category;
    const sort = req.query.sort;
    const page = req.query.page || 0;
    const pins = req.query.pins || false;

    const properties = db.collection ('listings');
    let query = {};

    //Get only ads with location
    query = Object.assign (query, {
      hasMap: true,
    });

    //AND

    //Do not show sold or reserved properity
    query = Object.assign (query, {
      status: STATUS_NORMAL,
    });

    //AND

    //Show ads that fall inside visible map
    if (bounds) {
      const [lat1, lng1, lat2, lng2] = bounds.split (',').map (parseFloat);
      const box = [[lat1, lng1], [lat2, lng2]];

      query = Object.assign (query, {
        loc: {
          $geoWithin: {
            $box: box,
          },
        },
      });
    }

    //AND

    //Show only selected type of ads (selling or renting)
    if (adType) {
      query = Object.assign (query, {
        adType: parseInt (adType),
      });
    }

    //AND

    //Match price
    if (minPrice || maxPrice) {
      const price = {};
      if (minPrice) {
        price['$gte'] = parseFloat (minPrice);
      }

      if (maxPrice) {
        price['$lte'] = parseFloat (maxPrice);
      }

      query = Object.assign (query, {
        price,
      });
    }

    //AND

    //Match number of rooms
    if (rooms) {
      const roomCount = [];
      let fourPlus = false;

      const allRooms = rooms.split (',');
      allRooms.map (val => {
        if (parseInt (val) !== 4) {
          roomCount.push (parseInt (val));
        } else {
          fourPlus = true;
        }
      });

      if (fourPlus) {
        query = Object.assign (query, {
          rooms: {$gte: 4},
        });
      } else {
        query = Object.assign (query, {
          rooms: {$in: roomCount},
        });
      }
    }

    //AND

    //Match size
    if (minSize || maxSize) {
      const size = {};
      if (minSize) {
        size['$gte'] = parseFloat (minSize);
      }

      if (maxSize) {
        size['$lte'] = parseFloat (maxSize);
      }

      query = Object.assign (query, {
        size,
      });
    }

    //AND

    //Match category
    if (category) {
      const categoryCount = [];

      const allCategories = category.split (',').map (val => {
        categoryCount.push (parseInt (val));
      });

      query = Object.assign (query, {
        category: {$in: categoryCount},
      });
    }

    console.log ('QUERY: ', query);
    const cnt = await properties.find (query).count ();

    res.header ('X-Total-Count', cnt);

    const getSort = () => {
      if (sort === 'price-min') {
        return [['price', 'asc']];
      } else if (sort === 'price-max') {
        return [['price', 'desc']];
      } else if (sort === 'newest') {
        return [['_id', 'desc']];
      } else if (sort === 'relevance') {
        // TODO: figure out what the relevance is
        return [];
      }
    };

    let all = properties.find (query, {
      //"sort": [['field1','asc'], ['field2','desc']]
      sort: getSort (),
    });

    const isPins = pins === 'true';

    if (!isPins) {
      all = await all.skip (20 * page).limit (20).toArray ();
    } else {
      all = await all.toArray ();
    }

    if (all.length > 0) {
      res.header ('X-Last-Record-Id', [...all].pop ()._id);
    }

    if (isPins) {
      res.json (
        all.map (val => {
          return {
            _id: val._id,
            loc: val.loc,
          };
        })
      );
    } else {
      res.json (
        all.map (({_id, address, images, price, rooms, size, time}) => ({
          _id,
          address,
          images: [images[0]],
          price,
          rooms,
          size,
          time: distanceInWordsToNow (moment (time, 'DD.MM.YYYY'), {
            locale: hr,
          }),
          realTime: time,
        }))
      );
    }

    res.end ();
  } catch (e) {
    console.log ('error:', e);
    next (e);
  }
});

const app = express ();
app.use (bodyParser.json ());

app.use (function (req, res, next) {
  res.header ('Access-Control-Allow-Origin', '*');
  res.header (
    'Access-Control-Allow-Headers',
    'Origin, X-Requested-With, Content-Type, Accept, X-Last-Record-Id, X-Total-Count'
  );
  res.header (
    'Access-Control-Expose-Headers',
    'X-Last-Record-Id, X-Total-Count'
  );
  res.header ('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
  res.header ('Access-Control-Allow-Credentials', 'true');
  next ();
});

app.use ('/api', router);

MongoClient.connect (url).then (database => {
  db = database;
  db.collection ('listings').createIndex ({loc: '2d'});
  app.listen (PORT, () =>
    console.log ('Express server running at localhost: ' + PORT)
  );
});
