import {models} from '../models'
import * as crypto from 'crypto'
import * as socket from '../utils/socket'
import * as helpers from '../helpers'
import * as jsonUtils from '../utils/json'
import {success, failure} from '../utils/res'

const constants = require(__dirname + '/../../config/constants.json')

const getContacts = async (req, res) => {
	const contacts = await models.Contact.findAll({ where:{deleted:false}, raw: true })
	const invites = await models.Invite.findAll({ raw: true })
	const chats = await models.Chat.findAll({ where:{deleted:false}, raw: true })
	const subscriptions = await models.Subscription.findAll({ raw: true })

	const contactsResponse = contacts.map(contact => {
		let contactJson = jsonUtils.contactToJson(contact)
		let invite = invites.find(invite => invite.contactId == contact.id)

		if (invite) {
			contactJson.invite = jsonUtils.inviteToJson(invite)
		}
		
		return contactJson
	});

	const subsResponse = subscriptions.map(s=> jsonUtils.subscriptionToJson(s,null))
  	const chatsResponse = chats.map(chat => jsonUtils.chatToJson(chat))

	success(res, {
		contacts: contactsResponse,
		chats: chatsResponse,
		subscriptions: subsResponse
	})
}

const generateToken = async (req, res) => {
	console.log('=> generateToken called', { body: req.body, params: req.params, query: req.query })

	const owner = await models.Contact.findOne({ where: { isOwner: true, authToken: null }})

	if (owner) {
		const hash = crypto.createHash('sha256').update(req.body['token']).digest('base64');

		console.log("req.params['token']", req.params['token']);
		console.log("hash", hash);

		owner.update({ authToken: hash })

		success(res,{})
	} else {
		failure(res,{})
	}
}

const updateContact = async (req, res) => {
	console.log('=> updateContact called', { body: req.body, params: req.params, query: req.query })

	let attrs = extractAttrs(req.body)

	const contact = await models.Contact.findOne({ where: { id: req.params.id }})
	let shouldUpdateContactKey = (contact.isOwner && contact.contactKey == null && attrs["contact_key"] != null)
	
	const owner = await contact.update(jsonUtils.jsonToContact(attrs))
	success(res, jsonUtils.contactToJson(owner))

	if (!shouldUpdateContactKey) {
		return
	}
	// definitely "owner" now

	const contactIds = await models.Contact.findAll({where:{deleted:false}}).map(c => c.id)
	if (contactIds.length == 0) {
		return
	}

	helpers.sendContactKeys({
		contactIds: contactIds,
		sender: owner,
		type: constants.message_types.contact_key,
	})
}

const exchangeKeys = async (req, res) => {
	console.log('=> exchangeKeys called', { body: req.body, params: req.params, query: req.query })

	const contact = await models.Contact.findOne({ where: { id: req.params.id }})
	const owner = await models.Contact.findOne({ where: { isOwner: true }})

	success(res, jsonUtils.contactToJson(contact))

	helpers.sendContactKeys({
		contactIds: [contact.id],
		sender: owner,
		type: constants.message_types.contact_key,
	})
}

const createContact = async (req, res) => {
	console.log('=> createContact called', { body: req.body, params: req.params, query: req.query })

	let attrs = extractAttrs(req.body)

	const owner = await models.Contact.findOne({ where: { isOwner: true }})

	const createdContact = await models.Contact.create(attrs)
	const contact = await createdContact.update(jsonUtils.jsonToContact(attrs))

	success(res, jsonUtils.contactToJson(contact))

	helpers.sendContactKeys({
		contactIds: [contact.id],
		sender: owner,
		type: constants.message_types.contact_key,
	})
}

const deleteContact = async (req, res) => {
	const id = parseInt(req.params.id||'0')
	if(!id || id===1) {
		failure(res, 'Cannot delete self')
		return
	}

	const contact = await models.Contact.findOne({ where: { id } })
	await contact.update({
		deleted:true,
		publicKey:'',
		photoUrl:'',
		alias:'Unknown',
		contactKey:'',
	})

	// find and destroy chat & messages
	const chats = await models.Chat.findAll({where:{deleted:false}})
	chats.map(async chat => {
		if (chat.type === constants.chat_types.conversation) {
			const contactIds = JSON.parse(chat.contactIds)
			if (contactIds.includes(id)) {
				await chat.update({
					deleted: true, 
					uuid:'', 
					contactIds:'[]',
					name:''
				})
				await models.Message.destroy({ where: { chatId: chat.id } })
			}
		}
	})
	await models.Invite.destroy({ where: { contactId: id } })
	await models.Subscription.destroy({ where: { contactId: id } })

	success(res, {})
}

const receiveConfirmContactKey = async (payload) => {
	console.log('=> confirm contact key', { payload })

	const dat = payload.content || payload
	const sender_pub_key = dat.sender.pub_key
	const sender_contact_key = dat.sender.contact_key
	const sender_alias = dat.sender.alias || 'Unknown'
	const sender_photo_url = dat.sender.photoUrl

	if(sender_photo_url){
		// download and store photo locally
	}

	const sender = await models.Contact.findOne({ where: { publicKey: sender_pub_key, status: constants.contact_statuses.confirmed }})
	if (sender_contact_key && sender) {
		if(!sender.alias || sender.alias==='Unknown') {
			sender.update({ contactKey: sender_contact_key, alias: sender_alias })
		} else {
			sender.update({ contactKey: sender_contact_key })
		}		

		socket.sendJson({
			type: 'contact',
			response: jsonUtils.contactToJson(sender)
		})
	}
}

const receiveContactKey = async (payload) => {
	console.log('=> received contact key', JSON.stringify(payload))

	const dat = payload.content || payload
	const sender_pub_key = dat.sender.pub_key
	const sender_contact_key = dat.sender.contact_key
	const sender_alias = dat.sender.alias || 'Unknown'
	const sender_photo_url = dat.sender.photoUrl

	if(sender_photo_url){
		// download and store photo locally
	}

	const owner = await models.Contact.findOne({ where: { isOwner: true }})
	const sender = await models.Contact.findOne({ where: { publicKey: sender_pub_key, status: constants.contact_statuses.confirmed }})

	if (sender_contact_key && sender) {
		if(!sender.alias || sender.alias==='Unknown') {
			sender.update({ contactKey: sender_contact_key, alias: sender_alias })
		} else {
			sender.update({ contactKey: sender_contact_key })
		}

		socket.sendJson({
			type: 'contact',
			response: jsonUtils.contactToJson(sender)
		})
	}

	helpers.sendContactKeys({
		contactPubKey: sender_pub_key,
		sender: owner,
		type: constants.message_types.contact_key_confirmation,
	})
}

const extractAttrs = body => {
	let fields_to_update = ["public_key", "node_alias", "alias", "photo_url", "device_id", "status", "contact_key"]
	let attrs = {}
	Object.keys(body).forEach(key => {
		if (fields_to_update.includes(key)) {
			attrs[key] = body[key]
		}
	})
	return attrs
}

export {
	generateToken,
	exchangeKeys,
	getContacts,
	updateContact,
	createContact,
	deleteContact,
	receiveContactKey,
	receiveConfirmContactKey
}