import { englishPage } from "./language"
const SECRET_KEY = "my secret symmetric key"

async function handleRequest(request) {
    if (request.method == "POST") {
        return signRequest(request)
    } else {
        return verifyRequest(request)
    } 
}

async function verifyRequest(request) {
    const url = new URL(request.url)

    // Make sure we have the minimum necessary query parameters.
    if (!url.searchParams.has("mac") || !url.searchParams.has("expiry")) {
        return failPage(request, "Missing query parameter")
    }

    const encoder = new TextEncoder()
    const secretKeyData = encoder.encode(SECRET_KEY)

    const key = await crypto.subtle.importKey(
        "raw",
        secretKeyData,
        { name: "HMAC", hash: "SHA-256" },
        false,
        ["verify"],
    )

    // Extract the query parameters we need and run the HMAC algorithm on the
    // parts of the request we are authenticating: the path and the expiration
    // timestamp.
    const expiry = Number(url.searchParams.get("expiry"))
    const dataToAuthenticate = url.pathname + expiry

    // The received MAC is Base64-encoded, so we have to go to some trouble to
    // get it into a buffer type that crypto.subtle.verify() can read.
    const receivedMacBase64 = url.searchParams.get("mac")
    const receivedMac = byteStringToUint8Array(atob(receivedMacBase64))

    // Use crypto.subtle.verify() to guard against timing attacks. Since HMACs use
    // symmetric keys, we could implement this by calling crypto.subtle.sign() and
    // then doing a string comparison -- this is insecure, as string comparisons
    // bail out on the first mismatch, which leaks information to potential
    // attackers.
    const verified = await crypto.subtle.verify(
        "HMAC",
        key,
        receivedMac,
        encoder.encode(dataToAuthenticate),
    )

    if (!verified) {
        return failPage(request, "Invalid MAC")
    }

    if (Date.now() > expiry) {
        return failPage(request, `URL expired at ${new Date(expiry)}`)
    }

    // We have verified the MAC and expiration time; we are good to pass the request
    // through.
    return fetch(request)
}

// Convert a ByteString (a string whose code units are all in the range
// [0, 255]), to a Uint8Array. If you pass in a string with code units larger
// than 255, their values will overflow!
function byteStringToUint8Array(byteString) {
    const ui = new Uint8Array(byteString.length)
    for (let i = 0; i < byteString.length; ++i) {
        ui[i] = byteString.charCodeAt(i)
    }
    return ui
}

async function signRequest(reqeust) {
    const url = new URL(reqeust.url)
    return generateSignedUrl(url)
}

async function generateSignedUrl(url) {
    // We"ll need some super-secret data to use as a symmetric key.
    const encoder = new TextEncoder()
    const secretKeyData = encoder.encode(SECRET_KEY)
    const key = await crypto.subtle.importKey(
      "raw",
      secretKeyData,
      { name: "HMAC", hash: "SHA-256" },
      false,
      ["sign"],
    )
  
    // Signed requests expire after one minute. Note that you could choose
    // expiration durations dynamically, depending on, e.g. the path or a query
    // parameter.
    const expirationMs = 18000
    const expiry = Date.now() + expirationMs
    const dataToAuthenticate = url.pathname + expiry
  
    const mac = await crypto.subtle.sign("HMAC", key, encoder.encode(dataToAuthenticate))
  
    // `mac` is an ArrayBuffer, so we need to jump through a couple of hoops to get
    // it into a ByteString, and then a Base64-encoded string.
    const base64Mac = btoa(String.fromCharCode(...new Uint8Array(mac)))
  
    url.searchParams.set("mac", base64Mac)
    url.searchParams.set("expiry", expiry)
  
    return new Response(url)
  }


function failPage(request, msg) {
    let page = englishPage(request) ? FAILPAGE_EN : FAILPAGE
    page = page.replace('{{error-msg}}', msg)
    return new Response(page, {headers: {"Content-Type": "text/html; charset=utf-8"}})
}




const FAILPAGE_EN = `
<!DOCTYPE html>
<html>
<head>
<title>Error Notification Page</title>
<style>
body {
    font-family: Arial, sans-serif;
}
h1 {
    text-align: center;
}
.container {
    max-width: 500px;
    margin: auto;
    border: 1px solid #ccc;
    padding: 20px;
    border-radius: 5px;
}
.url-section {
    margin-top: 20px;
}
.url-wrapper {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 10px;
    background-color: #f5f5f5;
    border: 1px solid #ccc;
    border-radius: 5px;
}
.button-wrapper {
    margin-top: 20px;
    margin-bottom: 20px;
    display: flex;
    justify-content: center;
}
.url-button {
    background-color: #4CAF50;
    color: white;
    border: none;
    padding: 10px 20px;
    text-align: center;
    text-decoration: none;
    display: inline-block;
    font-size: 16px;
    border-radius: 5px;
    cursor: pointer;
}
.url-display {
    display: inline-block;
    max-width: 460px; /* Set maximum width */
    word-wrap: break-word; /* Automatically wrap words that exceed the width */
}
</style>
</head>
<body>
<div class="container">
<h1>Error Notification</h1>
<p>The current URL has not been properly signed: {{error-msg}}</p>
<div class="url-section">
    <div class="url-wrapper">
        <p><span class="url-display" id="current-url" style="display: block;"></span></p>
    </div>
    <div class="button-wrapper">
        <button class="url-button" type="button" onclick="getSignedUrl()">Sign URL</button>
    </div>
    <div class="url-wrapper" id="signed-url-section">
        <p><strong>The signed URL will expire in 18 seconds</strong></p>
        <p><span class="url-display" id="signed-url"></span></p>
    </div>
</div>
</div>

<script>
var currentUrl = window.location.href;
var currentUrlElement = document.getElementById("current-url");
currentUrlElement.innerText = currentUrl;

function showResult(result) {
    var signedUrlElement = document.getElementById("signed-url");
    signedUrlElement.innerText = result;
  
    var signedUrlSection = document.getElementById("signed-url-section");
    signedUrlSection.style.display = "block";
}

function getSignedUrl() {
    fetch("/ECA-test/pet-shop-website-template/about.html", {
        method: "POST"
    })
    .then(response => response.text())
    .then(data => {
        showResult(data);
    })
    .catch(error => {
        showResult('Error occurred while requesting signed URL: ' + error.toString());
    });
}
</script>
</body>
</html>
`
                    
addEventListener("fetch", event => {
    return event.respondWith(handleRequest(event.request))
})