const crypto = require('crypto');

/**
 * Generate a signed license key (Base64 Payload + Base64 Signature)
 * @param {string} moduleCode - 'asset', 'production', 'monitoring'
 * @param {string} type - 'dev', 'sub', 'demo'
 * @param {string} subscriberId - Subscriber ID
 * @param {number} [activationDays] - Optional
 * @param {string} [customExpiryDate] - Optional YYYY-MM-DD
 * @param {string|Buffer} privateKey - PEM formatted private key
 * @returns {string} Signed License Token
 */
function generateLicense(moduleCode, type, subscriberId, activationDays = null, customExpiryDate = null, privateKey) {
    if (!privateKey) {
        throw new Error('Private Key is required to generate license.');
    }

    const payloadData = {
        m: moduleCode,      // Module
        t: type,            // Type
        s: subscriberId,    // Subscriber ID
        d: Date.now()       // Issue Date
    };

    if (customExpiryDate) {
        const date = new Date(customExpiryDate + 'T23:59:59');
        if (!isNaN(date.getTime())) payloadData.e = date.getTime();
    } else {
        if (type === 'demo') payloadData.e = Date.now() + (30 * 24 * 60 * 60 * 1000);
        else if (type === 'sub') payloadData.e = Date.now() + (365 * 24 * 60 * 60 * 1000);
    }

    if (activationDays) {
        payloadData.ad = Date.now() + (activationDays * 24 * 60 * 60 * 1000);
    }

    const payloadStr = JSON.stringify(payloadData);
    const sign = crypto.createSign('SHA256');
    sign.update(payloadStr);
    sign.end();

    const signature = sign.sign(privateKey, 'base64');
    const payloadB64 = Buffer.from(payloadStr).toString('base64');

    return `${payloadB64}.${signature}`;
}

/**
 * Verify a signed license key
 * @param {string} licenseToken - The token string (Payload.Signature)
 * @param {string|Buffer} publicKey - PEM formatted public key
 * @returns {object} { isValid, reason, ...payload }
 */
function verifyLicense(licenseToken, publicKey) {
    if (!publicKey) {
        return { isValid: false, reason: 'Server Configuration Error: Public Key missing' };
    }

    if (!licenseToken || !licenseToken.includes('.')) {
        return { isValid: false, reason: 'Invalid Key Format' };
    }

    const [payloadB64, signature] = licenseToken.split('.');

    try {
        const payloadStr = Buffer.from(payloadB64, 'base64').toString('utf8');

        // Verify Signature
        const verify = crypto.createVerify('SHA256');
        verify.update(payloadStr);
        verify.end();

        const isValidSignature = verify.verify(publicKey, signature, 'base64');
        if (!isValidSignature) {
            return { isValid: false, reason: 'Invalid Signature (Key Modified or Wrong Public Key)' };
        }

        // Logic Checks (Expiry, Activation)
        const payload = JSON.parse(payloadStr);
        const { m, t, d, s, ad, e } = payload;
        const subscriberId = s || null;

        if (ad && Date.now() > ad) {
            return { isValid: false, reason: `Activation Period Expired (${new Date(ad).toLocaleDateString()})`, module: m, type: t, subscriberId };
        }

        let expiryDate = null;
        if (e) {
            expiryDate = new Date(e);
            if (Date.now() > e) {
                return { isValid: false, reason: 'Expired', expiryDate, subscriberId };
            }
        } else {
            // Fallback for old keys? No, this is a breaking change. Old keys fail signature anyway.
            // Just handle 'dev' type (no expiry)
            if (t !== 'dev') {
                // If not dev and no e, treat as permanent or error?
                // Logic above always sets 'e' for sub/demo.
            }
        }

        return { isValid: true, module: m, type: t, subscriberId, expiryDate };

    } catch (e) {
        return { isValid: false, reason: 'Corrupted Key Payload' };
    }
}

module.exports = { generateLicense, verifyLicense };
