import * as path from 'path'
import * as fetch from 'node-fetch'
import {parseLDAT} from '../utils/ldat'
import * as rsa from '../crypto/rsa'
import * as crypto from 'crypto'
import * as meme from '../utils/meme'
import * as FormData from 'form-data'   
import { models } from '../models'
import * as RNCryptor from 'jscryptor'
import {sendMessage} from './send'
import { Op } from 'sequelize'

const constants = require(path.join(__dirname,'../../config/constants.json'))
const msgtypes = constants.message_types

export async function modifyPayloadAndSaveMediaKey(payload, chat, sender) {
  if(payload.type!==msgtypes.attachment) return payload
  try{
    const ret = await downloadAndUploadAndSaveReturningTermsAndKey(payload,chat,sender)
    return fillmsg(payload, ret) // key is re-encrypted later
  } catch(e) {
    console.log("[modify] error", e)
    return payload
  }
}

// "purchase" type
export async function purchaseFromOriginalSender(payload, chat, purchaser){
  if(payload.type!==msgtypes.purchase) return

  const mt = payload.message && payload.message.mediaToken
  const amount = payload.message.amount
  const muid = mt && mt.split('.').length && mt.split('.')[1]
  if(!muid) return

  const mediaKey = await models.MediaKey.findOne({where:{originalMuid:muid}})

  const terms = parseLDAT(mt)
  let price = terms.meta && terms.meta.amt
  if(amount<price) return // not enough sats

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

  if(mediaKey) { // ALREADY BEEN PURHCASED! simply send
    console.log("MEDIA KEY EXISTS ALREADY",mediaKey)
    // send back the new mediaToken and key
    const mediaTerms: {[k:string]:any} = {
      muid, ttl:31536000, host:'',
      meta:{...amount && {amt:amount}},
    }
    // send full new key and token
    const msg = {mediaTerms, mediaKey:mediaKey.key}
    console.log("SEND PURCHASE ACCEPT FROM STORED KEY")
    sendMessage({
      chat: {...chat.dataValues, contactIds:[purchaser.id]},
      sender: owner,
      type: constants.message_types.purchase_accept,
      message: msg,
      success: ()=>{},
      failure: ()=>{}
    })
  } else {
    console.log("NO MEDIA KEY EXISTS YET")
    const ogmsg = await models.Message.findOne({where:{chatId:chat.id,mediaToken:mt}})
    // purchase it from creator (send "purchase")
    const msg={amount, mediaToken:mt}
    console.log("GO AHEARD AND BUY!!! from:",ogmsg.sender,{
      chat: {...chat.dataValues, contactIds:[ogmsg.sender]},
      sender: {
        ...owner.dataValues,
        ...purchaser&&purchaser.alias && {alias:purchaser.alias}
      },
      type: constants.message_types.purchase,
      message: msg,
      amount: amount,
      success: ()=>{},
      failure: ()=>{}
    })
    sendMessage({
      chat: {...chat.dataValues, contactIds:[ogmsg.sender]},
      sender: {
        ...owner.dataValues,
        ...purchaser&&purchaser.alias && {alias:purchaser.alias}
      },
      type: constants.message_types.purchase,
      message: msg,
      amount: amount,
      success: ()=>{},
      failure: ()=>{}
    })
  }
}

export async function sendFinalMemeIfFirstPurchaser(payload, chat, sender){
  if(payload.type!==msgtypes.purchase_accept) return

  console.log("PURCHASE ACCEPT!!!!!")

  const mt = payload.message && payload.message.mediaToken
  const typ = payload.message && payload.message.mediaType
  if(!mt) return
  const muid = mt && mt.split('.').length && mt.split('.')[1]
  if(!muid) return

  const existingMediaKey = await models.MediaKey.findOne({where:{muid}})
  if(existingMediaKey) return // no need, its already been sent

  console.log("DOWNLOAD AND REIP:OAD",mt)
  const termsAndKey = await downloadAndUploadAndSaveReturningTermsAndKey(payload,chat,sender)

  const host = mt.split('.')[0]
  const ogPurchaseMessage = await models.Message.findOne({where:{
    mediaToken: {[Op.like]: `${host}.${muid}%`}
  }})

  console.log('ogPurchaseMessage',ogPurchaseMessage.dataValues)

  // send it to the purchaser
  const owner = await models.Contact.findOne({where: {isOwner:true}})
  console.log("SEND firST PURHCASE ACCEPT MSG!")
  sendMessage({
		sender: {
			...owner.dataValues,
			...sender&&sender.alias && {alias:sender.alias}
		},
    chat:{
      ...chat.dataValues,
      contactIds:[ogPurchaseMessage.sender],
    },
    type:msgtypes.purchase_accept, 
    message:{
      ...termsAndKey,
      mediaType: typ,
    },
		success: ()=>{},
		receive: ()=>{}
	})
}

function fillmsg(full, props){
	return {
		...full, message: {
			...full.message,
			...props,
		}
	}
}

async function sleep(ms) {
	return new Promise(resolve => setTimeout(resolve, ms))
}

export async function downloadAndUploadAndSaveReturningTermsAndKey(payload, chat, sender){
  const mt = payload.message && payload.message.mediaToken
  const key = payload.message && payload.message.mediaKey
  const typ = payload.message && payload.message.mediaType
  if(!mt || !key) return payload // save anyway??????????

  const ogmuid = mt && mt.split('.').length && mt.split('.')[1]

  const terms = parseLDAT(mt)
  if(!terms.host) return payload

  try {
    const r = await fetch(`https://${terms.host}/file/${mt}`, {
      headers: {'Authorization': `Bearer ${meme.mediaToken}`}
    })
    const buf = await r.buffer()

    const decMediaKey = rsa.decrypt(chat.groupPrivateKey, key)
  
    const imgBuf = RNCryptor.Decrypt(buf.toString('base64'), decMediaKey)

    const newKey = crypto.randomBytes(20).toString('hex')

    const encImgBase64 = RNCryptor.Encrypt(imgBuf, newKey)

    var encImgBuffer = Buffer.from(encImgBase64,'base64');

    const form = new FormData()
    form.append('file', encImgBuffer, {
      contentType: typ||'image/jpg',
      filename: 'Image.jpg',
      knownLength:encImgBuffer.length,
    })
    const formHeaders = form.getHeaders()
    const resp = await fetch(`https://${terms.host}/file`, {
      method: 'POST',
      headers: {
        ...formHeaders, // THIS IS REQUIRED!!!
        'Authorization': `Bearer ${meme.mediaToken}`,
      },
      body:form
    })

    let json = await resp.json()
    if(!json.muid) throw new Error('no muid')

    // PUT NEW TERMS, to finish in personalizeMessage
    const amt = terms.meta&&terms.meta.amt
    const ttl = terms.meta&&terms.meta.ttl
    const mediaTerms: {[k:string]:any} = {
      muid:json.muid, ttl:ttl||31536000, host:'',
      meta:{...amt && {amt}},
      skipSigning: amt ? true : false // only sign if its free
    }

    const encKey = rsa.encrypt(chat.groupKey, newKey.slice())
    var date = new Date()

    date.setMilliseconds(0)
    console.log('[modify] save media key!',{
      muid:json.muid,
      chatId:chat.id,
      key: encKey,
      messageId: (payload.message&&payload.message.id)||0,
      receiver: 0,
      sender: sender.id, // the og sender
      createdAt: date,
    })
    await sleep(1)
    await models.MediaKey.create({
      muid:json.muid,
      chatId:chat.id,
      key:encKey,
      messageId: (payload.message&&payload.message.id)||0,
      receiver: 0,
      sender: sender.id, // the og sender
      createdAt: date,
      originalMuid: ogmuid,
    })
    return {mediaTerms,mediaKey:encKey}
  } catch(e) {
    throw e
  }
}