const express = require('express');
const OSS = require('ali-oss');
const { STS } = require('ali-oss');
const { getCredential } = require('ali-oss/lib/common/signUtils');
const { getStandardRegion } = require('ali-oss/lib/common/utils/getStandardRegion');
const { policy2Str } = require('ali-oss/lib/common/utils/policy2Str');

const app = express();
const PORT = process.env.PORT || 8000; // Server request port number

// Set static file directory
app.use(express.static('templates'));

const GenerateSignature = async () => {
    // Initialize STS client
    let sts = new STS({
        accessKeyId: process.env.OSS_ACCESS_KEY_ID,  // Get RAM user's AccessKey ID from environment variables
        accessKeySecret: process.env.OSS_ACCESS_KEY_SECRET // Get RAM user's AccessKey Secret from environment variables
    });

    // Call assumeRole interface to get STS temporary access credentials
    const result = await sts.assumeRole(process.env.OSS_STS_ROLE_ARN, '', '3600', 'yourRoleSessionName'); // Get RAM role ARN from environment variables, set temporary access credentials validity to 3600 seconds, and role session name to yourRoleSessionName (can be customized)

    // Extract AccessKeyId, AccessKeySecret, and SecurityToken from temporary access credentials
    const accessKeyId = result.credentials.AccessKeyId;
    const accessKeySecret = result.credentials.AccessKeySecret;
    const securityToken = result.credentials.SecurityToken;

    // Initialize OSS Client
    const client = new OSS({
        bucket: 'examplebucket', // Replace with target Bucket name
        region: 'cn-hangzhou', // Replace with Bucket's region
        accessKeyId,
        accessKeySecret,
        stsToken: securityToken,
        refreshSTSTokenInterval: 0,
        refreshSTSToken: async () => {
            const { accessKeyId, accessKeySecret, securityToken } = await client.getCredential();
            return { accessKeyId, accessKeySecret, stsToken: securityToken };
        },
    });

    // Create form data Map
    const formData = new Map();

    // Set signature expiration time to 10 minutes from now
    const date = new Date();
    const expirationDate = new Date(date);
    expirationDate.setMinutes(date.getMinutes() + 10);

    // Format date to ISO 8601 standard UTC time string format
    function padTo2Digits(num) {
        return num.toString().padStart(2, '0');
    }
    function formatDateToUTC(date) {
        return (
            date.getUTCFullYear() +
            padTo2Digits(date.getUTCMonth() + 1) +
            padTo2Digits(date.getUTCDate()) +
            'T' +
            padTo2Digits(date.getUTCHours()) +
            padTo2Digits(date.getUTCMinutes()) +
            padTo2Digits(date.getUTCSeconds()) +
            'Z'
        );
    }
    const formattedDate = formatDateToUTC(expirationDate);
    // Set upload callback URL, which is the address of the callback server for communication between the application server and OSS. OSS will send the file upload information to the application server through this callback URL after the file upload is complete. For example, callbackUrl is set to https://oss-demo.aliyuncs.com:23450.

    // Generate x-oss-credential and set form data
    const credential = getCredential(formattedDate.split('T')[0], getStandardRegion(client.options.region), client.options.accessKeyId);
    formData.set('x_oss_date', formattedDate);
    formData.set('x_oss_credential', credential);
    formData.set('x_oss_signature_version', 'OSS4-HMAC-SHA256');
  
    // Create policy
    // Example policy form fields only list required fields
    const policy = {
        expiration: expirationDate.toISOString(),
        conditions: [
            { 'bucket': 'examplebucket' }, // Replace with target Bucket name
            { 'x-oss-credential': credential },
            { 'x-oss-signature-version': 'OSS4-HMAC-SHA256' },
            { 'x-oss-date': formattedDate },
        ],
    };

    // If STS Token exists, add it to the policy and form data
    if (client.options.stsToken) {
        policy.conditions.push({ 'x-oss-security-token': client.options.stsToken });
        formData.set('security_token', client.options.stsToken);
    }

    // Generate signature and set form data
    const signature = client.signPostObjectPolicyV4(policy, date);
    formData.set('policy', Buffer.from(policy2Str(policy), 'utf8').toString('base64'));
    formData.set('signature', signature);
    const callback = {
      callbackUrl: 'http://oss-demo.aliyuncs.com:23450/callback',// Set the server address for callback requests, for example http://oss-demo.aliyuncs.com:23450/callback.
      callbackBody:// Set the content of the callback, for example file ETag, resource type mimeType, etc.
      "filename=${object}&size=${size}&mimeType=${mimeType}&height=${imageInfo.height}&width=${imageInfo.width}",
      callbackBodyType: "application/x-www-form-urlencoded",// Set the content type of the callback.    
    };
    // Return form data
    return {
        host: `http://${client.options.bucket}.oss-${client.options.region}.aliyuncs.com`, 
        policy: Buffer.from(policy2Str(policy), 'utf8').toString('base64'),
        x_oss_signature_version: 'OSS4-HMAC-SHA256',
        x_oss_credential: credential,
        x_oss_date: formattedDate,
        signature: signature,
        dir: 'user-dir', // Specify the file prefix for uploading to OSS
        callback: Buffer.from(JSON.stringify(callback)).toString("base64"),// Use Buffer.from to Base64 encode JSON.
        security_token: client.options.stsToken
    };
};

app.get('/get_post_signature_for_oss_upload', async (req, res) => {
    try {
        const result = await GenerateSignature();
        res.json(result); // Return generated signature data
    } catch (error) {
        console.error('Error generating signature:', error);
        res.status(500).send('Error generating signature');
    }
});

app.listen(PORT, () => {
    console.log(`Server is running on http://127.0.0.1:${PORT}`);
});