"""Self-contained browser handoff for air-gapped license activation."""

from __future__ import annotations

import secrets

from fastapi.responses import HTMLResponse

from licensing_shared.constants import MAX_SIGNED_DOCUMENT_BYTES


OFFLINE_ACTIVATION_PATH = "/offline-activation"
CONTENT_SECURITY_NONCE_PLACEHOLDER = "__APOLON_CSP_NONCE__"
MAXIMUM_FILE_BYTES_PLACEHOLDER = "__APOLON_MAXIMUM_FILE_BYTES__"

OFFLINE_ACTIVATION_HTML = """<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width,initial-scale=1">
  <title>Apolon offline licensing</title>
  <style nonce="__APOLON_CSP_NONCE__">
    :root
    {
      color-scheme: light dark;
      font-family: system-ui, -apple-system, "Segoe UI", sans-serif;
    }
    body
    {
      margin: 0;
      background: #111820;
      color: #eef4f8;
    }
    main
    {
      box-sizing: border-box;
      max-width: 760px;
      min-height: 100vh;
      margin: 0 auto;
      padding: clamp(24px, 6vw, 64px) 24px;
    }
    .card
    {
      padding: clamp(24px, 5vw, 44px);
      border: 1px solid #344554;
      border-radius: 16px;
      background: #1b2731;
      box-shadow: 0 20px 60px rgb(0 0 0 / 25%);
    }
    h1
    {
      margin: 0 0 12px;
      font-size: clamp(1.8rem, 5vw, 2.6rem);
    }
    p
    {
      line-height: 1.55;
    }
    label
    {
      display: block;
      margin-top: 22px;
      margin-bottom: 8px;
      font-weight: 650;
    }
    input, button
    {
      box-sizing: border-box;
      width: 100%;
      min-height: 48px;
      border-radius: 8px;
      font: inherit;
    }
    input
    {
      padding: 10px 12px;
      border: 1px solid #668091;
      background: #101820;
      color: #fff;
    }
    button
    {
      margin-top: 26px;
      border: 0;
      background: #de3445;
      color: #fff;
      font-weight: 700;
      cursor: pointer;
    }
    button:disabled
    {
      cursor: wait;
      opacity: .65;
    }
    #status
    {
      min-height: 1.6em;
      margin-top: 18px;
      padding: 10px 12px;
      border-radius: 8px;
      background: #121d25;
    }
    .privacy
    {
      color: #b7c7d1;
      font-size: .94rem;
    }
  </style>
</head>
<body>
  <main>
    <section class="card" aria-labelledby="page-title">
      <h1 id="page-title">Offline activation, trial, renewal, and release</h1>
      <p>On the offline computer, create a request in <strong>License Center</strong>.
      Transfer that request file here. Activation requests also need the serial
      from the purchase; release requests securely free the existing device slot.
      Trial requests are reviewed without requiring a card. Renewal requests use
      account sign-in in License Center on a connected computer.</p>

      <label for="request-file">Activation request file</label>
      <input id="request-file" type="file" accept="application/json,.json" required>

      <div id="serial-fields">
        <label for="serial">Purchase serial</label>
        <input id="serial" type="text" inputmode="text" autocomplete="off"
               autocapitalize="characters" spellcheck="false" placeholder="MSA1-...">
      </div>

      <button id="activate" type="button">Create signed license file</button>
      <div id="status" role="status" aria-live="polite">Ready.</div>
      <p class="privacy">The browser does not retain the serial or request file.
      The licensing authority stores only the serial's keyed digest. Trial request
      files contain a public device key and privacy-bounded evidence digests, never
      raw hardware identifiers. Release requires a valid enrolled-device signature.</p>
    </section>
  </main>
  <script nonce="__APOLON_CSP_NONCE__">
    "use strict";
    const maximumFileBytes = __APOLON_MAXIMUM_FILE_BYTES__;
    const fileInput = document.getElementById("request-file");
    const serialInput = document.getElementById("serial");
    const serialFields = document.getElementById("serial-fields");
    const activateButton = document.getElementById("activate");
    const statusOutput = document.getElementById("status");

    function setStatus(message)
    {
      statusOutput.textContent = message;
    }

    async function postJson(endpoint, body)
    {
      const response = await fetch(endpoint,
      {
        method: "POST",
        credentials: "omit",
        cache: "no-store",
        headers:
        {
          "Accept": "application/json",
          "Content-Type": "application/json"
        },
        body: JSON.stringify(body)
      });
      return {response, result: await response.json()};
    }

    fileInput.addEventListener("change", async () =>
    {
      const file = fileInput.files[0];
      if (!file || file.size < 1 || file.size > maximumFileBytes)
      {
        serialFields.hidden = false;
        activateButton.textContent = "Create signed license file";
        return;
      }
      try
      {
        const request = JSON.parse(await file.text());
        const requestType = request?.payload?.requestType;
        const releasing = requestType === "release";
        const renewing = requestType === "renew";
        const trialing = requestType === "trial";
        serialFields.hidden = releasing || renewing || trialing;
        activateButton.textContent = renewing
          ? "Use signed-in License Center"
          : releasing
            ? "Release licensed device"
            : trialing
              ? "Submit or check trial request"
              : "Create signed license file";
      }
      catch (_error)
      {
        serialFields.hidden = false;
        activateButton.textContent = "Create signed license file";
      }
    });

    activateButton.addEventListener("click", async () =>
    {
      const file = fileInput.files[0];
      if (!file)
      {
        setStatus("Choose the activation request file from the offline computer.");
        fileInput.focus();
        return;
      }
      if (file.size < 1 || file.size > maximumFileBytes)
      {
        setStatus("The activation request file is empty or too large.");
        return;
      }
      activateButton.disabled = true;
      try
      {
        const offlineRequest = JSON.parse(await file.text());
        const requestId = offlineRequest?.payload?.requestId;
        const requestType = offlineRequest?.payload?.requestType;
        if (typeof requestId !== "string" || !requestId)
        {
          throw new Error("The selected file is not an Apolon offline request.");
        }
        if (
          requestType !== "activate"
          && requestType !== "trial"
          && requestType !== "renew"
          && requestType !== "release"
        )
        {
          throw new Error("The selected file has an unsupported request type.");
        }
        if (requestType === "renew")
        {
          throw new Error(
            "For renewal, open License Center on a connected computer, sign in, "
            + "and choose Process renewal request. No new serial is needed."
          );
        }
        const releasing = requestType === "release";
        const trialing = requestType === "trial";
        const serial = serialInput.value.trim().toUpperCase();
        if (!releasing && !trialing && !serial)
        {
          throw new Error("Enter the serial from your purchase.");
        }
        setStatus(
          trialing
            ? "Checking whether the signed offline trial request is awaiting review..."
            : releasing
            ? "Verifying the device signature and releasing its license slot..."
            : "Verifying the request and creating the signed license..."
        );
        let result;
        if (trialing)
        {
          const nonce = offlineRequest?.payload?.nonce;
          if (typeof nonce !== "string" || !nonce)
          {
            throw new Error("The offline trial request has no retrieval proof.");
          }
          let exchange = await postJson(
            "/v1/offline/trial-requests/status",
            {requestId, nonce}
          );
          if (exchange.response.status === 404)
          {
            exchange = await postJson(
              "/v1/offline/trial-requests",
              {offlineRequest}
            );
          }
          if (!exchange.response.ok)
          {
            const detail = typeof exchange.result?.message === "string"
              ? exchange.result.message
              : "The licensing authority rejected the trial request.";
            throw new Error(detail);
          }
          if (exchange.result?.status === "pending")
          {
            setStatus(
              "The trial request is awaiting review. Keep this request file and "
              + "return here later to download the signed license."
            );
            return;
          }
          if (exchange.result?.status === "rejected")
          {
            throw new Error(
              "The trial request was not approved ("
              + (exchange.result?.reasonCode ?? "policy decision")
              + "). Contact support if you believe this is an error."
            );
          }
          if (exchange.result?.status === "expired")
          {
            throw new Error("The trial request expired. Create a new request in License Center.");
          }
          if (exchange.result?.status !== "approved")
          {
            throw new Error("The licensing authority returned an unknown trial status.");
          }
          result = exchange.result;
        }
        else
        {
          const endpoint = releasing
            ? "/v1/activations/release-file"
            : "/v1/offline/requests";
          const requestBody = releasing
            ? {offlineRequest}
            :
            {
              offlineRequest,
              serial,
              idempotencyKey: requestId,
              correlationId: requestId,
              targetLicenseId: offlineRequest?.payload?.targetLicenseId ?? null
            };
          const exchange = await postJson(endpoint, requestBody);
          if (!exchange.response.ok)
          {
            const detail = typeof exchange.result?.message === "string"
              ? exchange.result.message
              : "The licensing authority rejected the request.";
            throw new Error(detail);
          }
          result = exchange.result;
        }
        if (releasing)
        {
          fileInput.value = "";
          serialInput.value = "";
          serialFields.hidden = false;
          activateButton.textContent = "Create signed license file";
          setStatus("The licensed device was released successfully. Its activation slot is now available.");
          return;
        }
        const safeRequestId = requestId.replace(/[^a-zA-Z0-9._-]/g, "-");
        const body = JSON.stringify(result, null, 2) + "\r\n";
        const downloadUrl = URL.createObjectURL(
          new Blob([body], {type: "application/json"})
        );
        const download = document.createElement("a");
        download.href = downloadUrl;
        download.download = `apolon-${safeRequestId}-license.json`;
        document.body.appendChild(download);
        download.click();
        download.remove();
        URL.revokeObjectURL(downloadUrl);
        serialInput.value = "";
        setStatus("Signed license created. Transfer the downloaded file to the offline computer and import it in License Center.");
      }
      catch (error)
      {
        setStatus(error instanceof Error ? error.message : "Offline activation failed.");
      }
      finally
      {
        activateButton.disabled = false;
      }
    });
  </script>
</body>
</html>
"""


def offline_activation_response() -> HTMLResponse:
    nonce = secrets.token_urlsafe(24)
    body = OFFLINE_ACTIVATION_HTML.replace(
        CONTENT_SECURITY_NONCE_PLACEHOLDER,
        nonce,
    ).replace(
        MAXIMUM_FILE_BYTES_PLACEHOLDER,
        str(MAX_SIGNED_DOCUMENT_BYTES),
    )
    return HTMLResponse(
        body,
        headers={
            "Cache-Control": "no-store",
            "Content-Security-Policy": (
                "default-src 'none'; "
                f"script-src 'nonce-{nonce}'; style-src 'nonce-{nonce}'; "
                "connect-src 'self'; base-uri 'none'; form-action 'none'; "
                "frame-ancestors 'none'"
            ),
            "Cross-Origin-Opener-Policy": "same-origin",
            "Permissions-Policy": "camera=(), microphone=(), geolocation=()",
            "Referrer-Policy": "no-referrer",
            "X-Content-Type-Options": "nosniff",
            "X-Frame-Options": "DENY",
        },
    )


__all__ = ["OFFLINE_ACTIVATION_PATH", "offline_activation_response"]
