const { sign } = require('pdf-signer');
const fs = require('fs');
const forge = require('node-forge');
const { PDFDocument } = require('pdf-lib'); // Importar rgb para colores
const path = require('path');
const { randomText } = require('./util');

const PASSWORD =  randomText(8);
const COMPANY = "Codemakers LLC";
const COUNTRY = 'US';
const STATE = 'NY';
const CITY = 'New York'
const LOCATION = 'New York, USA'
const EMAIL = 'info@code-makers.com'
const LOGO = path.join(__dirname, "../public/img/codeman.jpg");
const DEFAULT_USER = "Sistema";

// Generar el buffer PKCS#12 (P12)
function generateP12Buffer(fullName) {
    const pki = forge.pki;
    const keys = pki.rsa.generateKeyPair(2048);

    const cert = pki.createCertificate();
    cert.publicKey = keys.publicKey;
    cert.serialNumber = generateUId(8);
    cert.validity.notBefore = new Date();
    cert.validity.notAfter = new Date();
    cert.validity.notAfter.setFullYear(cert.validity.notBefore.getFullYear() + 1); // Válido por 1 año

    const attrs = [
        { name: 'commonName', value: fullName },
        { name: 'countryName', value: COUNTRY },
        { shortName: 'ST', value: STATE },
        { name: 'localityName', value: CITY },
        { name: 'organizationName', value: COMPANY },
        { shortName: 'OU', value: COMPANY }
    ];
    cert.setSubject(attrs);
    cert.setIssuer(attrs);

    cert.sign(keys.privateKey, forge.md.sha256.create());

    // Crear el archivo PKCS#12
    const p12Asn1 = forge.pkcs12.toPkcs12Asn1(
        keys.privateKey,
        [cert],
        PASSWORD, // Puedes establecer una contraseña o dejarlo vacío
        { algorithm: '3des' } // Algoritmo de cifrado para la clave privada
    );

    // Convertir a formato DER (binario)
    const p12Der = forge.asn1.toDer(p12Asn1).getBytes();

    // Retornar el buffer PKCS#12
    return Buffer.from(p12Der, 'binary');
}



// Generar el buffer PKCS#12 (P12)
function generateP12BufferExtended(fullName, company, city, state, country) {
    const pki = forge.pki;
    const keys = pki.rsa.generateKeyPair(2048);

    const cert = pki.createCertificate();
    cert.publicKey = keys.publicKey;
    cert.serialNumber = generateUId(8);
    cert.validity.notBefore = new Date();
    cert.validity.notAfter = new Date();
    cert.validity.notAfter.setFullYear(cert.validity.notBefore.getFullYear() + 1); // Válido por 1 año

    const attrs = [
        { name: 'commonName', value: fullName },
        { name: 'countryName', value: country },
        { shortName: 'ST', value: state },
        { name: 'localityName', value: city },
        { name: 'organizationName', value: company },
        { shortName: 'OU', value: company }
    ];
    cert.setSubject(attrs);
    cert.setIssuer(attrs);

    cert.sign(keys.privateKey, forge.md.sha256.create());

    // Crear el archivo PKCS#12
    const p12Asn1 = forge.pkcs12.toPkcs12Asn1(
        keys.privateKey,
        [cert],
        PASSWORD, // Puedes establecer una contraseña o dejarlo vacío
        { algorithm: '3des' } // Algoritmo de cifrado para la clave privada
    );

    // Convertir a formato DER (binario)
    const p12Der = forge.asn1.toDer(p12Asn1).getBytes();

    // Retornar el buffer PKCS#12
    return Buffer.from(p12Der, 'binary');
}




// Función para obtener la fecha en el formato requerido (sin zona horaria)
function formatDate() {
    const date = new Date();

    // Formatear la fecha como YYYY.MM.DD
    const datePart = date.toLocaleDateString('en-CA').replace(/-/g, '.');

    // Formatear la hora como HH:MM:SS
    const timePart = date.toLocaleTimeString('en-GB', { hour12: false });
    return `${datePart} ${timePart}`;
}



async function FirmarPdf(pdfBuffer, fullName = DEFAULT_USER, xPos = 10, yPos = 10) {
    return FirmarDocumento(pdfBuffer, fullName, xPos, yPos);
}


async function FirmarPdfExtended(pdfBuffer, fullName, email, company, city, state, country, xPos, yPos, logoPath) {
    return FirmarDocumentoExtended(pdfBuffer, fullName, email, company, city, state, country, xPos, yPos, logoPath);
}


async function FirmarDocumento(pdfBuffer, fullName, xPos, yPos, logoPath = LOGO) {
    try {

        validateString(fullName);
        validateIntegerPositive(xPos, 'xPos');
        validateIntegerPositive(yPos, 'yPos');
        validateJpgFile(logoPath); //Leer la imagen de la firma en formato PNG

        const p12Buffer = generateP12Buffer(fullName); // Generar el P12 buffer
        const lastPage = await getLastPage(pdfBuffer); // ultima pagina del pdf

        const signedPdf = await sign(pdfBuffer, p12Buffer, PASSWORD, {
            reason: 'Signed',
            email: EMAIL,
            location: LOCATION,
            signerName: COMPANY,
            annotationOnPages: [lastPage],
            annotationAppearanceOptions: {
                signatureCoordinates: {
                    left: xPos,
                    bottom: yPos,
                    right: xPos + 160,
                    top: yPos + 60
                },
                imageDetails: {
                    imagePath: LOGO,
                    transformOptions: { rotate: 0, space: 60, stretch: 40, tilt: 0, xPos: 0, yPos: 15 },
                },
                signatureDetails: [
                    {
                        value: 'Signed by ' + fullName,
                        fontSize: 7,
                        transformOptions: { rotate: 0, space: 1, tilt: 0, xPos: 70, yPos: 50 },
                    },
                    {
                        value: 'Reason: Signed',
                        fontSize: 7,
                        transformOptions: { rotate: 0, space: 1, tilt: 0, xPos: 70, yPos: 40 },
                    },
                    {
                        value: `Date: ${formatDate()}`,
                        fontSize: 7,
                        transformOptions: { rotate: 0, space: 1, tilt: 0, xPos: 70, yPos: 30 },
                    },
                    {
                        value: 'Location: ' + LOCATION,
                        fontSize: 7,
                        transformOptions: { rotate: 0, space: 1, tilt: 0, xPos: 70, yPos: 20 },
                    },
                ],
            },
        });

        console.log('PDF firmado exitosamente');
        return signedPdf;

    } catch (err) {
        let error = { 'Error al firmar el PDF:': err.message };
        console.log(error);
        return error;
    }
}


async function FirmarDocumentoExtended(pdfBuffer, fullName, email, company, city, state, country, xPos, yPos, logoPath) {
    try {

        let location = city + ", " + country;
        validateString(fullName);
        validateIntegerPositive(xPos, 'xPos');
        validateIntegerPositive(yPos, 'yPos');
        validateJpgFile(logoPath); //Leer la imagen de la firma en formato PNG

        const p12Buffer = generateP12BufferExtended(fullName, company, city, state, country); // Generar el P12 buffer
        const lastPage = await getLastPage(pdfBuffer); // ultima pagina del pdf

        const signedPdf = await sign(pdfBuffer, p12Buffer, PASSWORD, {
            reason: 'Signed',
            email: email,
            location: location,
            signerName: company,
            annotationOnPages: [lastPage],
            annotationAppearanceOptions: {
                signatureCoordinates: {
                    left: xPos,
                    bottom: yPos,
                    right: xPos + 160,
                    top: yPos + 60
                },
                imageDetails: {
                    imagePath: logoPath,
                    transformOptions: { rotate: 0, space: 60, stretch: 40, tilt: 0, xPos: 0, yPos: 15 },
                },
                signatureDetails: [
                    {
                        value: 'Signed by ' + fullName,
                        fontSize: 7,
                        transformOptions: { rotate: 0, space: 1, tilt: 0, xPos: 70, yPos: 50 },
                    },
                    {
                        value: 'Reason: Signed',
                        fontSize: 7,
                        transformOptions: { rotate: 0, space: 1, tilt: 0, xPos: 70, yPos: 40 },
                    },
                    {
                        value: `Date: ${formatDate()}`,
                        fontSize: 7,
                        transformOptions: { rotate: 0, space: 1, tilt: 0, xPos: 70, yPos: 30 },
                    },
                    {
                        value: 'Location: ' + location,
                        fontSize: 7,
                        transformOptions: { rotate: 0, space: 1, tilt: 0, xPos: 70, yPos: 20 },
                    },
                ],
            },
            incremental: true
        });

        console.log('PDF firmado exitosamente');
        return signedPdf;

    } catch (err) {
        let error = { 'Error al firmar el PDF:': err.message };
        console.log(error);
        return error;
    }
}


function generateUId(x) {
    const caracteres = 'ABCDEFGHIJKLMNOPQRSTUUVWXYZ123456789';
    let codigo = '';

    for (let i = 0; i < x; i++) {
        const indice = Math.floor(Math.random() * caracteres.length);
        codigo += caracteres.charAt(indice);
    }

    return codigo;
}


async function getLastPage(bytes) {
    try {
        const pdfDoc = await PDFDocument.load(bytes);
        const pages = pdfDoc.getPages().length;
        return pages - 1;
    }
    catch (err) {
        throw new Error("Only valid pdf files are acepted for signature")
    }
}


function validateJpgFile(filePath) {

    if (!fs.existsSync(filePath))
        throw new Error("It doesn't exist file in path: " + filePath)

    const extname = path.extname(filePath).toLowerCase();

    if (extname != '.jpg' && extname != '.jpeg')
        throw new Error("Only .jpg or .jpeg files are acepted to signature logo")
}



function validateIntegerPositive(numero, z) {

    if (!typeof numero === 'number' && Number.isInteger(numero) && numero < 0)
        throw new Error("Only positive integers are accepted for the coordinate in: " + z)
}


function validateString(input) {

    if (typeof input === 'string' && input.trim().length > 0)
        return;
    else
        throw new Error("Only valid strings are accepted in fullName")
}


//métodos publicos
module.exports = {
    FirmarPdf,
    FirmarPdfExtended,
};



/*
//descomentar solo para pruebas de firma
async function TestFirma() {

    try {
        let ruta = path.join(__dirname, "./logos/codeman.jpg");
        let ruta2 = path.join(__dirname, "./logos/8k-signature.jpg");
        let pdfBuffer = await fs.readFileSync('c:/tmp/financial.pdf');
        //let signedNewPdf = await FirmarPdf(pdfBuffer22, 'Hola Mundo', 10, 10, ruta);
        //let signedNewPdf = await FirmarPdf(pdfBuffer, "Juan Perez");
        //let signedNewPdf2 = await FirmarPdf(signedNewPdf, "Ana Rivas", 450, 10);
        
        //let signedNewPdf2 = await FirmarPdfExtended(pdfBuffer, "Otto Morales", "omorales@gmail.com", "Argus LLc", "Houston", "TX", "USA", 450, 10, ruta2);

        if (signedNewPdf2.error) return;

        let rutaSalida = 'c:/tmp/bbb20.pdf';
        await fs.writeFileSync(rutaSalida, signedNewPdf2);

        console.log("signed generado en: " + rutaSalida);
    }
    catch (err) {
        console.log("Error al firmar pdf: " + err.message);
    }
}
*/
