"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
Object.defineProperty(exports, "__esModule", { value: true });
const models_1 = require("../models");
const socket = require("../utils/socket");
const jsonUtils = require("../utils/json");
const resUtils = require("../utils/res");
const helpers = require("../helpers");
const hub_1 = require("../hub");
const lightning_1 = require("../utils/lightning");
const rp = require("request-promise");
const lightning_2 = require("../utils/lightning");
const ldat_1 = require("../utils/ldat");
const cron_1 = require("cron");
const zbase32 = require("../utils/zbase32");
const schemas = require("./schemas");
const confirmations_1 = require("./confirmations");
const path = require("path");
const network = require("../network");
const meme = require("../utils/meme");
const short = require("short-uuid");
const env = process.env.NODE_ENV || 'development';
const config = require(path.join(__dirname, '../../config/app.json'))[env];
const constants = require(path.join(__dirname, '../../config/constants.json'));
/*

TODO line 233: parse that from token itself, dont use getMediaInfo at all

"attachment": sends a message to a chat with a signed receipt for a file, which can be accessed from sphinx-meme server
If the attachment has a price, then the media must be purchased to get the receipt

"purchase" sends sats.
if the amount matches the price, the media owner
will respond ("purchase_accept" or "purchase_deny" type)
with the signed token, which can only be used by the buyer

purchase_accept should update the original attachment message with the terms and receipt
(both Relay and client need to do this) or make new???

purchase_deny returns the sats
*/
exports.sendAttachmentMessage = (req, res) => __awaiter(void 0, void 0, void 0, function* () {
    // try {
    //   schemas.attachment.validateSync(req.body)
    // } catch(e) {
    //   return resUtils.failure(res, e.message)
    // }
    const { chat_id, contact_id, muid, text, remote_text, remote_text_map, media_key_map, media_type, amount, file_name, ttl, price, // IF AMOUNT>0 THEN do NOT sign or send receipt
    reply_uuid, } = req.body;
    console.log('[send attachment]', req.body);
    const owner = yield models_1.models.Contact.findOne({ where: { isOwner: true } });
    const chat = yield helpers.findOrCreateChat({
        chat_id,
        owner_id: owner.id,
        recipient_id: contact_id
    });
    let TTL = ttl;
    if (ttl) {
        TTL = parseInt(ttl);
    }
    if (!TTL)
        TTL = 31536000; // default year
    const amt = price || 0;
    // generate media token for self!
    const myMediaToken = yield ldat_1.tokenFromTerms({
        muid, ttl: TTL, host: '',
        pubkey: owner.publicKey,
        meta: Object.assign(Object.assign({}, amt && { amt }), { ttl })
    });
    const date = new Date();
    date.setMilliseconds(0);
    const myMediaKey = (media_key_map && media_key_map[owner.id]) || '';
    const mediaType = media_type || '';
    const remoteMessageContent = remote_text_map ? JSON.stringify(remote_text_map) : remote_text;
    const uuid = short.generate();
    const mm = {
        chatId: chat.id,
        uuid: uuid,
        sender: owner.id,
        type: constants.message_types.attachment,
        status: constants.statuses.pending,
        amount: amount || 0,
        messageContent: text || file_name || '',
        remoteMessageContent,
        mediaToken: myMediaToken,
        mediaKey: myMediaKey,
        mediaType: mediaType,
        date,
        createdAt: date,
        updatedAt: date
    };
    if (reply_uuid)
        mm.replyUuid = reply_uuid;
    const message = yield models_1.models.Message.create(mm);
    console.log('saved attachment msg from me', message.id);
    saveMediaKeys(muid, media_key_map, chat.id, message.id, mediaType);
    const mediaTerms = {
        muid, ttl: TTL,
        meta: Object.assign({}, amt && { amt }),
        skipSigning: amt ? true : false // only sign if its free
    };
    const msg = {
        mediaTerms,
        id: message.id,
        uuid: uuid,
        content: remote_text_map || remote_text || text || file_name || '',
        mediaKey: media_key_map,
        mediaType: mediaType,
    };
    if (reply_uuid)
        msg.replyUuid = reply_uuid;
    network.sendMessage({
        chat: chat,
        sender: owner,
        type: constants.message_types.attachment,
        amount: amount || 0,
        message: msg,
        success: (data) => __awaiter(void 0, void 0, void 0, function* () {
            console.log('attachment sent', { data });
            resUtils.success(res, jsonUtils.messageToJson(message, chat));
        }),
        failure: error => resUtils.failure(res, error.message),
    });
});
function saveMediaKeys(muid, mediaKeyMap, chatId, messageId, mediaType) {
    if (typeof mediaKeyMap !== 'object') {
        console.log('wrong type for mediaKeyMap');
        return;
    }
    var date = new Date();
    date.setMilliseconds(0);
    for (let [contactId, key] of Object.entries(mediaKeyMap)) {
        if (parseInt(contactId) !== 1) {
            const receiverID = parseInt(contactId) || 0; // 0 is for a tribe
            models_1.models.MediaKey.create({
                muid, chatId, key, messageId,
                receiver: receiverID,
                createdAt: date,
                mediaType
            });
        }
    }
}
exports.saveMediaKeys = saveMediaKeys;
exports.purchase = (req, res) => __awaiter(void 0, void 0, void 0, function* () {
    const { chat_id, contact_id, amount, media_token, } = req.body;
    var date = new Date();
    date.setMilliseconds(0);
    try {
        schemas.purchase.validateSync(req.body);
    }
    catch (e) {
        return resUtils.failure(res, e.message);
    }
    const owner = yield models_1.models.Contact.findOne({ where: { isOwner: true } });
    const chat = yield helpers.findOrCreateChat({
        chat_id,
        owner_id: owner.id,
        recipient_id: contact_id
    });
    const message = yield models_1.models.Message.create({
        chatId: chat.id,
        uuid: short.generate(),
        sender: owner.id,
        type: constants.message_types.purchase,
        amount: amount || 0,
        mediaToken: media_token,
        date: date,
        createdAt: date,
        updatedAt: date
    });
    const msg = {
        mediaToken: media_token, id: message.id, uuid: message.uuid,
        purchaser: owner.id,
    };
    network.sendMessage({
        chat: Object.assign(Object.assign({}, chat.dataValues), { contactIds: [contact_id] }),
        sender: owner,
        type: constants.message_types.purchase,
        message: msg,
        amount: amount,
        success: (data) => __awaiter(void 0, void 0, void 0, function* () {
            console.log('purchase sent!');
            resUtils.success(res, jsonUtils.messageToJson(message, chat));
        }),
        failure: error => resUtils.failure(res, error.message),
    });
});
/* RECEIVERS */
exports.receivePurchase = (payload) => __awaiter(void 0, void 0, void 0, function* () {
    console.log('=> received purchase', { payload });
    var date = new Date();
    date.setMilliseconds(0);
    const { owner, sender, chat, amount, mediaToken, msg_uuid, chat_type, skip_payment_processing, purchaser_id } = yield helpers.parseReceiveParams(payload);
    if (!owner || !sender || !chat) {
        return console.log('=> group chat not found!');
    }
    const message = yield models_1.models.Message.create({
        chatId: chat.id,
        uuid: msg_uuid,
        sender: sender.id,
        type: constants.message_types.purchase,
        amount: amount || 0,
        mediaToken: mediaToken,
        date: date,
        createdAt: date,
        updatedAt: date
    });
    socket.sendJson({
        type: 'purchase',
        response: jsonUtils.messageToJson(message, chat, sender)
    });
    const isTribe = chat_type === constants.chat_types.tribe;
    // if sats forwarded from tribe owner, for the >1 time
    // dont need to send back token, because admin already has it
    if (isTribe && skip_payment_processing) {
        return console.log('=> skip payment processing');
    }
    const muid = mediaToken && mediaToken.split('.').length && mediaToken.split('.')[1];
    if (!muid) {
        return console.log('no muid');
    }
    const ogMessage = yield models_1.models.Message.findOne({
        where: { mediaToken }
    });
    if (!ogMessage) {
        return console.log('no original message');
    }
    // find mediaKey for who sent
    const mediaKey = yield models_1.models.MediaKey.findOne({ where: {
            muid, receiver: isTribe ? 0 : sender.id,
        } });
    // console.log('mediaKey found!',mediaKey.dataValues)
    if (!mediaKey)
        return; // this is from another person (admin is forwarding)
    const terms = ldat_1.parseLDAT(mediaToken);
    // get info
    let TTL = terms.meta && terms.meta.ttl;
    let price = terms.meta && terms.meta.amt;
    if (!TTL || !price) {
        const media = yield getMediaInfo(muid);
        console.log("GOT MEDIA", media);
        if (media) {
            TTL = media.ttl && parseInt(media.ttl);
            price = media.price;
        }
        if (!TTL)
            TTL = 31536000;
        if (!price)
            price = 0;
    }
    if (amount < price) { // didnt pay enough
        return network.sendMessage({
            chat: Object.assign(Object.assign({}, chat.dataValues), { contactIds: [sender.id] }),
            sender: owner,
            amount: amount,
            type: constants.message_types.purchase_deny,
            message: { amount, content: 'Payment Denied', mediaToken },
            success: (data) => __awaiter(void 0, void 0, void 0, function* () {
                console.log('purchase_deny sent');
                const denyMsg = yield models_1.models.Message.create({
                    chatId: chat.id,
                    sender: owner.id,
                    type: constants.message_types.purchase_deny,
                    mediaToken: mediaToken,
                    date: date, createdAt: date, updatedAt: date
                });
                socket.sendJson({
                    type: 'purchase_deny',
                    response: jsonUtils.messageToJson(denyMsg, chat, sender)
                });
            }),
            failure: error => console.log('=> couldnt send purcahse deny', error),
        });
    }
    const theMediaToken = yield ldat_1.tokenFromTerms({
        muid, ttl: TTL, host: '',
        meta: { amt: amount },
        pubkey: sender.publicKey,
    });
    const msgToSend = {
        mediaToken: theMediaToken,
        mediaKey: mediaKey.key,
        mediaType: ogMessage.mediaType,
    };
    if (purchaser_id)
        msgToSend.purchaser = purchaser_id;
    network.sendMessage({
        chat: Object.assign(Object.assign({}, chat.dataValues), { contactIds: [sender.id] }),
        sender: owner,
        type: constants.message_types.purchase_accept,
        message: msgToSend,
        success: (data) => __awaiter(void 0, void 0, void 0, function* () {
            console.log('purchase_accept sent!');
            const acceptMsg = yield models_1.models.Message.create({
                chatId: chat.id,
                sender: owner.id,
                type: constants.message_types.purchase_accept,
                mediaToken: theMediaToken,
                date: date, createdAt: date, updatedAt: date
            });
            socket.sendJson({
                type: 'purchase_accept',
                response: jsonUtils.messageToJson(acceptMsg, chat, sender)
            });
        }),
        failure: error => console.log('=> couldnt send purchase accept', error),
    });
});
exports.receivePurchaseAccept = (payload) => __awaiter(void 0, void 0, void 0, function* () {
    console.log('=> receivePurchaseAccept');
    var date = new Date();
    date.setMilliseconds(0);
    const { owner, sender, chat, mediaToken, mediaKey, mediaType, originalMuid } = yield helpers.parseReceiveParams(payload);
    if (!owner || !sender || !chat) {
        return console.log('=> no group chat!');
    }
    const termsArray = mediaToken.split('.');
    // const host = termsArray[0]
    const muid = termsArray[1];
    if (!muid) {
        return console.log('wtf no muid');
    }
    // const attachmentMessage = await models.Message.findOne({where:{
    //   mediaToken: {$like: `${host}.${muid}%`}
    // }})
    // if(attachmentMessage){
    //   console.log('=> updated msg!')
    //   attachmentMessage.update({
    //     mediaToken, mediaKey
    //   })
    // }
    const msg = yield models_1.models.Message.create({
        chatId: chat.id,
        sender: sender.id,
        type: constants.message_types.purchase_accept,
        status: constants.statuses.received,
        mediaToken,
        mediaKey,
        mediaType,
        originalMuid: originalMuid || '',
        date: date,
        createdAt: date,
        updatedAt: date
    });
    socket.sendJson({
        type: 'purchase_accept',
        response: jsonUtils.messageToJson(msg, chat, sender)
    });
});
exports.receivePurchaseDeny = (payload) => __awaiter(void 0, void 0, void 0, function* () {
    console.log('=> receivePurchaseDeny');
    var date = new Date();
    date.setMilliseconds(0);
    const { owner, sender, chat, amount, mediaToken } = yield helpers.parseReceiveParams(payload);
    if (!owner || !sender || !chat) {
        return console.log('=> no group chat!');
    }
    const msg = yield models_1.models.Message.create({
        chatId: chat.id,
        sender: sender.id,
        type: constants.message_types.purchase_deny,
        status: constants.statuses.received,
        messageContent: 'Purchase has been denied and sats returned to you',
        amount: amount,
        amountMsat: parseFloat(amount) * 1000,
        mediaToken,
        date: date,
        createdAt: date,
        updatedAt: date
    });
    socket.sendJson({
        type: 'purchase_deny',
        response: jsonUtils.messageToJson(msg, chat, sender)
    });
});
exports.receiveAttachment = (payload) => __awaiter(void 0, void 0, void 0, function* () {
    // console.log('received attachment', { payload })
    var date = new Date();
    date.setMilliseconds(0);
    const { owner, sender, chat, mediaToken, mediaKey, mediaType, content, msg_id, chat_type, sender_alias, msg_uuid, reply_uuid } = yield helpers.parseReceiveParams(payload);
    if (!owner || !sender || !chat) {
        return console.log('=> no group chat!');
    }
    const msg = {
        chatId: chat.id,
        uuid: msg_uuid,
        type: constants.message_types.attachment,
        sender: sender.id,
        date: date,
        createdAt: date,
        updatedAt: date
    };
    if (content)
        msg.messageContent = content;
    if (mediaToken)
        msg.mediaToken = mediaToken;
    if (mediaKey)
        msg.mediaKey = mediaKey;
    if (mediaType)
        msg.mediaType = mediaType;
    if (reply_uuid)
        msg.replyUuid = reply_uuid;
    const isTribe = chat_type === constants.chat_types.tribe;
    if (isTribe) {
        msg.senderAlias = sender_alias;
    }
    const message = yield models_1.models.Message.create(msg);
    // console.log('saved attachment', message.dataValues)
    socket.sendJson({
        type: 'attachment',
        response: jsonUtils.messageToJson(message, chat, sender)
    });
    hub_1.sendNotification(chat, msg.senderAlias || sender.alias, 'message');
    const theChat = Object.assign(Object.assign({}, chat.dataValues), { contactIds: [sender.id] });
    confirmations_1.sendConfirmation({ chat: theChat, sender: owner, msg_id });
});
function signer(req, res) {
    return __awaiter(this, void 0, void 0, function* () {
        if (!req.params.challenge)
            return resUtils.failure(res, "no challenge");
        try {
            const sig = yield lightning_1.signBuffer(Buffer.from(req.params.challenge, 'base64'));
            const sigBytes = zbase32.decode(sig);
            const sigBase64 = ldat_1.urlBase64FromBytes(sigBytes);
            resUtils.success(res, {
                sig: sigBase64
            });
        }
        catch (e) {
            resUtils.failure(res, e);
        }
    });
}
exports.signer = signer;
function verifier(msg, sig) {
    return __awaiter(this, void 0, void 0, function* () {
        try {
            const res = yield lightning_1.verifyMessage(msg, sig);
            return res;
        }
        catch (e) {
            console.log(e);
        }
    });
}
exports.verifier = verifier;
function getMyPubKey() {
    return __awaiter(this, void 0, void 0, function* () {
        return new Promise((resolve, reject) => {
            const lightning = lightning_2.loadLightning();
            var request = {};
            lightning.getInfo(request, function (err, response) {
                if (err)
                    reject(err);
                if (!response.identity_pubkey)
                    reject('no pub key');
                else
                    resolve(response.identity_pubkey);
            });
        });
    });
}
exports.getMyPubKey = getMyPubKey;
function cycleMediaToken() {
    return __awaiter(this, void 0, void 0, function* () {
        try {
            if (process.env.TEST_LDAT)
                ldat_1.testLDAT();
            const mt = yield getMediaToken(null);
            if (mt) {
                console.log('=> [meme] authed!');
                meme.setMediaToken(mt);
            }
            new cron_1.CronJob('1 * * * *', function () {
                getMediaToken(true);
            });
        }
        catch (e) {
            console.log(e.message);
        }
    });
}
exports.cycleMediaToken = cycleMediaToken;
const mediaURL = 'http://' + config.media_host + '/';
function getMediaToken(force) {
    return __awaiter(this, void 0, void 0, function* () {
        if (!force && meme.mediaToken)
            return meme.mediaToken;
        yield helpers.sleep(3000);
        try {
            const res = yield rp.get(mediaURL + 'ask');
            const r = JSON.parse(res);
            if (!(r && r.challenge && r.id)) {
                throw new Error('no challenge');
            }
            const sig = yield lightning_1.signBuffer(Buffer.from(r.challenge, 'base64'));
            if (!sig)
                throw new Error('no signature');
            const pubkey = yield getMyPubKey();
            if (!pubkey) {
                throw new Error('no pub key!');
            }
            const sigBytes = zbase32.decode(sig);
            const sigBase64 = ldat_1.urlBase64FromBytes(sigBytes);
            const bod = yield rp.post(mediaURL + 'verify', {
                form: { id: r.id, sig: sigBase64, pubkey }
            });
            const body = JSON.parse(bod);
            if (!(body && body.token)) {
                throw new Error('no token');
            }
            meme.setMediaToken(body.token);
            return body.token;
        }
        catch (e) {
            throw e;
        }
    });
}
exports.getMediaToken = getMediaToken;
function getMediaInfo(muid) {
    return __awaiter(this, void 0, void 0, function* () {
        try {
            const token = yield getMediaToken(null);
            const res = yield rp.get(mediaURL + 'mymedia/' + muid, {
                headers: {
                    'Authorization': `Bearer ${token}`,
                    'Content-Type': 'application/json'
                },
                json: true
            });
            return res;
        }
        catch (e) {
            return null;
        }
    });
}
exports.getMediaInfo = getMediaInfo;
//# sourceMappingURL=media.js.map