var crypto = require('crypto'),
    moment = require('moment'),
    dat = require('./dat'),
    createDateAsUTC,
    validate,
    generate,
    signKey,
    parseSubmittedKeyForm,
    msgGen;



/////////////////////////////////////////////////////
// MSGGEN  
// Generate key code message from key
//
msgGen = function (li, license, callback) {
    //Format return message with key embedded in it:
    //END OF LINE CHARACTER    
    var eol = '\n';
    var requestGreeting = '';

    //get the key messages from db:
    dat.read(
        'license_templates',
        {}, //no find map for this route 
        {},
        function (license_templates) {
            var templ = license_templates[0];
            if (templ.error) {
                callback(templ);
            } else {

                //format and return license message

                var sMessage = '';
                if (li.licenseType == "new") {
                    if (li.isLite) {
                        sMessage = templ.liteNew;
                    } else {
                        sMessage = templ.fullNew;
                    }
                } else if (li.licenseType == "addon") {
                    if (li.isLite) {
                        sMessage = templ.liteAddOn;
                    } else {
                        sMessage = templ.fullAddOn;
                    }
                } else if (li.licenseType == "licensedTrial") {//Licensed trial both types ("licensedTrial","webRequestedTrial") same message currently
                    if (li.isLite) {
                        sMessage = templ.liteTrial;
                    } else {
                        sMessage = templ.fullTrial;
                    }
                } else if (li.licenseType == "webRequestedTrial") {//Licensed trial both types ("licensedTrial","webRequestedTrial") same message currently
                    if (li.isLite) {
                        sMessage = templ.liteTrial;
                        requestGreeting = templ.liteTrialGreeting.replace('[FirstName]', li.greetingName);
                        requestGreeting += '\r\n\r\n====================\r\nREQUEST DETAILS\r\n---------------\r\n' + li.requestMessage + '\r\n\r\n====================\r\n';
                    } else {
                        sMessage = templ.fullTrial;
                        requestGreeting = templ.fullTrialGreeting.replace('[FirstName]', li.greetingName);
                        requestGreeting += '\r\n\r\n====================\r\nREQUEST DETAILS\r\n---------------\r\n' + li.requestMessage + '\r\n\r\n====================\r\n';
                    }
                }



                //*********************************************
                //TOKEN SUBSTITUTIONS
                var installByDate = moment().add(1, 'M').format("dddd, MMMM Do YYYY");
                //supportExpiresMoment = new moment(li[keyType].Expires);
                //note UTC is required to ensure it displays the correct date for this one only
                var supportExpiresDate = (new moment(li.parsed[li.keyType].Expires)).utc().format("dddd, MMMM Do YYYY");



                //LICENSE ENTRY EXPIRY DATE
                sMessage = sMessage.replace("[LicenseExpiryDate]", installByDate);

                //LICENSE DESCRIPTION
                var licenseDescription = 'LICENSE DETAILS';
                licenseDescription += eol;
                licenseDescription += 'This key must be installed before: ';
                licenseDescription += installByDate;
                licenseDescription += eol;

                if (li.licenseType == "webRequestedTrial") {
                    licenseDescription += '*** This temporary license key has been provided for limited evaluation purposes only ***';
                    licenseDescription += eol;
                    licenseDescription += 'This license will expire and AyaNova usage will be restricted after: ';
                    licenseDescription += supportExpiresDate;
                    licenseDescription += eol;
                    licenseDescription += eol;

                }

                if (li.keyWillLockout) {
                    licenseDescription += '*** This temporary license key is provided for evaluation use only pending payment ***';
                    licenseDescription += eol;
                    licenseDescription += 'This license will expire and AyaNova usage will be restricted after: ';
                    licenseDescription += supportExpiresDate;
                    licenseDescription += eol;
                    licenseDescription += eol;
                    licenseDescription += 'A permanent license key will be sent to you when payment';
                    licenseDescription += eol;
                    licenseDescription += 'has been received and processed.  There will be no extensions or ';
                    licenseDescription += eol;
                    licenseDescription += 'exceptions.  Please send in payment early enough to allow for';
                    licenseDescription += eol;
                    licenseDescription += 'mail and processing time to ensure uninterrupted use of AyaNova';
                    licenseDescription += eol;
                    licenseDescription += eol;
                }

                licenseDescription += 'Registered to: ';
                licenseDescription += li.parsed[li.keyType].registeredTo;
                licenseDescription += eol;

                licenseDescription += 'Scheduleable resources: ';
                switch (li.parsed[li.keyType].TotalScheduleableUsers) {
                    case '1':
                        licenseDescription += '1';
                        break;
                    case '5':
                        licenseDescription += 'Up to 5';
                        break;
                    case '10':
                        licenseDescription += 'Up to 10';
                        break;
                    case '20':
                        licenseDescription += 'Up to 20';
                        break;
                    case '50':
                        licenseDescription += 'Up to 50';
                        break;
                    case '999':
                        licenseDescription += 'Up to 999';
                        break;
                }
                licenseDescription += eol;

                licenseDescription += 'Support and updates until:  ';
                licenseDescription += supportExpiresDate;
                licenseDescription += eol;


                var pluginsLength = li.parsed[li.keyType].Plugins.Plugin.length;
                if (pluginsLength > 0) {
                    licenseDescription += eol;
                    licenseDescription += 'Plugins:';
                    licenseDescription += eol;
                    for (var i = 0; i < pluginsLength; i++) {
                        licenseDescription += '\t';
                        licenseDescription += li.parsed[li.keyType].Plugins.Plugin[i].Item;
                        licenseDescription += ' support and updates until: ';
                        var pluginExpiresMoment = new moment(li.parsed[li.keyType].Plugins.Plugin[i].SubscriptionExpires);
                        licenseDescription += pluginExpiresMoment.utc().format("dddd, MMMM Do YYYY");
                        licenseDescription += eol;

                    }
                }


                sMessage = sMessage.replace('[LicenseDescription]', licenseDescription);

                //KEY ITSELF
                sMessage = sMessage.replace('[LicenseKey]', license);
                //******************************************************************



                //return the result
                callback(
                    {
                        li: li,
                        keyMessage: sMessage,
                        requestGreeting: requestGreeting
                    }
                );
            }

        }
    );
}


/////////////////////////////////////////////////////
// GENERATE  
// called by Generate route in routs.js when user submits a key form
//
generate = function (req, callback) {

    //Parse the keygen form request
    var li = parseSubmittedKeyForm(req);

    //SIGN the license
    var license = signKey(li.parsed);

    //generate the message and callback with it
    msgGen(li, license, function (ret) {
        return callback(ret.keyMessage);
    });
}

////////////////////////
// PARSE KEY GEN FORM REQUEST
parseSubmittedKeyForm = function (req) {

    var r = req.body;
    dat.booleanizeObject(r);
    dat.fixStringToDates(r);


    var keyType = 'AyaNovaLicenseKey';
    if (r.isLite == true) {
        keyType = 'AyaNovaLiteLicenseKey';
    }


    var li = {};
    li[keyType] = {};
    li[keyType].SchemaVersion = '7';


    //Hex encode the source to obscure it from users
    var buffer = new Buffer('Rockfish - IP: ' + req.ip + ' USER: ' + req.apitoken.id);
    li[keyType].Source = buffer.toString('hex');

    //c# version of id
    //Math.Truncate((DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1))).TotalSeconds);
    var rightNow = new Date();
    li[keyType].Id = Math.floor(Date.now() / 1000).toString();
    li[keyType].Created = createDateAsUTC(rightNow).toISOString();// new Date(Date.UTC(rightNow.getUTCFullYear(), rightNow.getUTCMonth(), rightNow.getUTCDate(),0,0,0)).toISOString();
    li[keyType].Sub = 'true';
    li[keyType].RegisteredTo = r.registeredTo;
    li[keyType].InstallableUntil = createDateAsUTC(moment().add(1, 'M').toDate()).toISOString();
    li[keyType].TotalScheduleableUsers = r.users;
    li[keyType].Expires = r.supportExpiresDate;
    if (r.keyWillLockout) {
        li[keyType].LockDate = r.lockoutDate;
    }


    //Only web requested trial licenses are RequestedTrial=true
    if (r.licenseType == "webRequestedTrial") {
        li[keyType].RequestedTrial = 'true';
    } else {
        li[keyType].RequestedTrial = 'false';
    }



    li[keyType].Plugins = {
        Plugin: []
    };

    //PLUGINS
    if (r.mbi) {
        li[keyType].Plugins.Plugin.push({
            Item: 'MBI - Minimal browser interface',
            SubscriptionExpires: r.mbiSupportExpiresDate
        });
    }

    if (r.wbi) {
        li[keyType].Plugins.Plugin.push({
            Item: 'WBI - Web browser interface',
            SubscriptionExpires: r.wbiSupportExpiresDate
        });
    }

    if (r.qbi) {
        li[keyType].Plugins.Plugin.push({
            Item: 'QBI - QuickBooks interface',
            SubscriptionExpires: r.qbiSupportExpiresDate
        });
    }

    if (r.pti) {
        li[keyType].Plugins.Plugin.push({
            Item: 'PTI - US Sage 50/Peachtree interface',
            SubscriptionExpires: r.ptiSupportExpiresDate
        });
    }

    if (r.quickNotification) {
        li[keyType].Plugins.Plugin.push({
            Item: 'QuickNotification',
            SubscriptionExpires: r.quickNotificationSupportExpiresDate
        });
    }

    if (r.exportToXls) {
        li[keyType].Plugins.Plugin.push({
            Item: 'ExportToXls',
            SubscriptionExpires: r.exportToXlsSupportExpiresDate
        });
    }

    if (r.outlookSchedule) {
        li[keyType].Plugins.Plugin.push({
            Item: 'OutlookSchedule',
            SubscriptionExpires: r.outlookScheduleSupportExpiresDate
        });
    }

    if (r.oli) {
        li[keyType].Plugins.Plugin.push({
            Item: 'AyaNovaOLI',
            SubscriptionExpires: r.oliSupportExpiresDate
        });
    }

    if (r.importExportCSVDuplicate) {
        li[keyType].Plugins.Plugin.push({
            Item: 'ImportExportCSVDuplicate',
            SubscriptionExpires: r.importExportCSVDuplicateSupportExpiresDate
        });
    }

    if (r.ri) {
        li[keyType].Plugins.Plugin.push({
            Item: 'RI - Responsive Interface',
            SubscriptionExpires: r.riSupportExpiresDate
        });
    }

    return {
        keyType: keyType,
        licenseType: r.licenseType,
        isLite: r.isLite,
        keyWillLockout: r.keyWillLockout,
        parsed: li
    };

}


///////////////////////////////////////////////////////
// RETURN A DATE AS UTC WITH SAME d/m/y as passed date
// strips out time
//
createDateAsUTC = function (date) {
    return new Date(Date.UTC(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0));
}


/////////////////////////////////////////////////////
// SIGN KEY
//
signKey = function (li) {
    //**** Note this is our real 2016 private key
    var eol = '\n';
    var privatePEM = '-----BEGIN RSA PRIVATE KEY-----\n' +
        'MIIEpAIBAAKCAQEAz7wrvLDcKVMZ31HFGBnLWL08IodYIV5VJkKy1Z0n2snprhSi' +
        'u3izxTyz+SLpftvKHJpky027ii7l/pL9Bo3JcjU5rKrxXavnE7TuYPjXn16dNLd0' +
        'K/ERSU+pXLmUaVN0nUWuGuUMoGJMEXoulS6pJiG11yu3BM9fL2Nbj0C6a+UwzEHF' +
        'mns3J/daZOb4gAzMUdJfh9OJ0+wRGzR8ZxyC99Na2gDmqYglUkSMjwLTL/HbgwF4' +
        'OwmoQYJBcET0Wa6Gfb17SaF8XRBV5ZtpCsbStkthGeoXZkFriB9c1eFQLKpBYQo2' +
        'DW3H1MPG2nAlQZLbkJj5cSh7/t1bRF08m6P+EQIDAQABAoIBAQCGvTpxLRXgB/Kk' +
        'EtmQBEsMx9EVZEwZeKIqKuDsBP8wvf4/10ql5mhT6kehtK9WhSDW5J2z8DtQKZMs' +
        'SBKuCZE77qH2CPp9E17SPWzQoRbaW/gDlWpYhgf8URs89XH5zxO4XtXKw/4omRlV' +
        'zLYiNR2pifv0EHqpOAg5KGzewdEo4VgXgtRWpHZLMpH2Q0/5ZIKMhstI6vFHP1p7' +
        'jmU4YI6uxiu7rVrZDmIUsAGoTdMabNqK/N8hKaoBiIto0Jn1ck26g+emLg8m160y' +
        'Xciu5yFUU+PP1SJMUs+k1UnAWf4p46X9jRLQCBRue9o0Ntiq/75aljRoDvgdwDsR' +
        'mg4ZANqxAoGBAPBoM5KoMZ4sv8ZFv8V+V8hgL5xiLgGoiwQl91mRsHRM/NQU5A/w' +
        'tH8nmwUrJOrksV7kX9228smKmoliTptyGGyi1NPmSkA7cN9YYnENoOEBHCVNK9vh' +
        'P+bkbMYUDNMW4fgOj09oXtQtMl5E2B3OTGoNwZ2w13YQJ8RIniLPsX7nAoGBAN01' +
        'eQNcUzQk9YrFGTznOs8udDLBfigDxaNnawvPueulJdBy6ZXDDrKmkQQA7xxl8YPr' +
        'dNtBq2lOgnb6+smC15TaAfV/fb8BLmkSwdn4Fy0FApIXIEOnLq+wjkte98nuezl8' +
        '9KXDzaqNI9hPuk2i36tJuLLMH8hzldveWbWjSlRHAoGBAKRPE7CQtBjfjNL+qOta' +
        'RrT0yJWhpMANabYUHNJi+K8ET2jEPnuGkFa3wwPtUPYaCABLJhprB9Unnid3wTIM' +
        '8RSO1ddd9jGgbqy3w9Bw+BvQnmQAMpG9iedNB+r5mSpM4XSgvuIO+4EYwuwbMXpt' +
        'nVx+um4Eh75xnDxTRYGVYkLRAoGAaZVpUlpR+HSfooHbPv+bSWKB4ewLPCw4vHrT' +
        'VErtEfW8q9b9eRcmP81TMFcFykc6VN4g47pfh58KlKHM7DwAjDLWdohIy89TiKGE' +
        'V3acEUfv5y0UoFX+6ara8Ey+9upWdKUY3Lotw3ckoc3EPeQ84DQK7YSSswnAgLaL' +
        'mS/8fWcCgYBjRefVbEep161d2DGruk4X7eNI9TFJ278h6ydW5kK9aTJuxkrtKIp4' +
        'CYf6emoB4mLXFPvAmnsalkhN2iB29hUZCXXSUjpKZrpijL54Wdu2S6ynm7aT97NF' +
        'oArP0E2Vbow3JMxq/oeXmHbrLMLQfYyXwFmciLFigOtkd45bfHdrbA==\n' +
        '-----END RSA PRIVATE KEY-----';

    //***** NOTE: this is our real 2016 public key 
    //                 KEEP A COPY HERE
    // var publicPem = '-----BEGIN PUBLIC KEY-----\n' +
    //     'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAz7wrvLDcKVMZ31HFGBnL' +
    //     'WL08IodYIV5VJkKy1Z0n2snprhSiu3izxTyz+SLpftvKHJpky027ii7l/pL9Bo3J' +
    //     'cjU5rKrxXavnE7TuYPjXn16dNLd0K/ERSU+pXLmUaVN0nUWuGuUMoGJMEXoulS6p' +
    //     'JiG11yu3BM9fL2Nbj0C6a+UwzEHFmns3J/daZOb4gAzMUdJfh9OJ0+wRGzR8ZxyC' +
    //     '99Na2gDmqYglUkSMjwLTL/HbgwF4OwmoQYJBcET0Wa6Gfb17SaF8XRBV5ZtpCsbS' +
    //     'tkthGeoXZkFriB9c1eFQLKpBYQo2DW3H1MPG2nAlQZLbkJj5cSh7/t1bRF08m6P+' +
    //     'EQIDAQAB\n' +
    //     '-----END PUBLIC KEY-----';

    var sign = crypto.createSign('RSA-SHA256');
    var licenseStringNoWS = JSON.stringify(li);

    sign.update(licenseStringNoWS, 'utf8'); //<<===== CRITICAL TO MATCH C# that utf8 parameter is specified even though it's the default supposedly
    var sig = sign.sign(privatePEM, 'base64');
    var licenseStringReadable = JSON.stringify(li, null, 4);

    var license =
        '[KEY' +
        eol +
        licenseStringReadable +
        eol +
        'KEY]' +
        eol +
        '[SIGNATURE' +
        eol +
        sig +
        eol +
        'SIGNATURE]' +
        eol;

    return license;
}

/////////////////////////////////////////////////////
// Validate test
//
validate = function () {

    // //***** NOTE: this is our real 2016 public key
    // var publicPem = '-----BEGIN PUBLIC KEY-----\n' +
    //     'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAz7wrvLDcKVMZ31HFGBnL' +
    //     'WL08IodYIV5VJkKy1Z0n2snprhSiu3izxTyz+SLpftvKHJpky027ii7l/pL9Bo3J' +
    //     'cjU5rKrxXavnE7TuYPjXn16dNLd0K/ERSU+pXLmUaVN0nUWuGuUMoGJMEXoulS6p' +
    //     'JiG11yu3BM9fL2Nbj0C6a+UwzEHFmns3J/daZOb4gAzMUdJfh9OJ0+wRGzR8ZxyC' +
    //     '99Na2gDmqYglUkSMjwLTL/HbgwF4OwmoQYJBcET0Wa6Gfb17SaF8XRBV5ZtpCsbS' +
    //     'tkthGeoXZkFriB9c1eFQLKpBYQo2DW3H1MPG2nAlQZLbkJj5cSh7/t1bRF08m6P+' +
    //     'EQIDAQAB\n' +
    //     '-----END PUBLIC KEY-----';

    // var verify = crypto.createVerify('RSA-SHA256');

    // //read in license key, split out sig and key data, strip whitespace from key data and validate
    // //var licenseFileData = fs.readFileSync(global.appRoot + '/lib/NODE_JSAyaLicense.txt').toString('utf8');
    // var licenseFileData = fs.readFileSync(global.appRoot + '/lib/JSAyaLicense.txt').toString('utf8');



    // //signature derived from generate method above
    // var sig = ExtractString(licenseFileData, '[SIGNATURE', 'SIGNATURE]');
    // var rawLicenseText = ExtractString(licenseFileData, '[KEY', 'KEY]');

    // //strip out all whitespace except inside quoted strings
    // var license = rawLicenseText.replace(/([^"]+)|("[^"]+")/g, function ($0, $1, $2) {
    //     if ($1) {
    //         return $1.replace(/\s/g, '');
    //     } else {
    //         return $2;
    //     }
    // });


    // verify.update(license, 'utf8'); //<<=== 'utf8' parameter is CRITICAL to be there even though it's the default supposedly
    // var isValid = verify.verify(publicPem, new Buffer(sig, 'base64'));
    // var retKey = '<pre>' + licenseFileData + '</pre>'
    // return isValid ? "<h1>KEY IS VALID!</h1>" + retKey : "<h1>KEY *NOT* VALID</h1>" + retKey;

    return "<h1>STUB: KG->Validate - to be coded</h1>";
}



/// <summary>
/// Extract string between tokens
/// </summary>
/// <param name="s"></param>
/// <param name="openTag"></param>
/// <param name="closeTag"></param>
/// <returns></returns>
var ExtractString = function (s, openTag, closeTag) {
    var startIndex = s.indexOf(openTag) + openTag.length;
    var endIndex = s.indexOf(closeTag, startIndex);
    //return s.substring(startIndex, endIndex - startIndex);
    return s.substring(startIndex, endIndex);
}


var hexEncode = function (s) {
    var hex, i;

    var result = "";
    for (i = 0; i < s.length; i++) {
        hex = s.charCodeAt(i).toString(16);
        result += ("000" + hex).slice(-4);
    }

    return result
}

var toHex = function (str) {
    var hex;
    try {
        hex = unescape(encodeURIComponent(str))
            .split('').map(function (v) {
                return v.charCodeAt(0).toString(16)
            }).join('')
    } catch (e) {
        hex = str
        console.log('invalid text input: ' + str)
    }
    return hex
}





//==============================================================================================================
//==============================================================================================================
//==============================================================================================================
module.exports = {
    validate: validate,
    generate: generate,
    msgGen: msgGen,
    signKey: signKey
};



// //***********************************************************************************************************