import { S3Client, GetObjectCommand, PutObjectCommand } from "@aws-sdk/client-s3";
import crypto from "node:crypto";
import { spawn } from "node:child_process";
import { readFile, writeFile } from "node:fs/promises";
import { createWriteStream } from "node:fs";
import { pipeline } from "node:stream/promises";

const s3 = new S3Client({ region: process.env.REGION || process.env.AWS_REGION || "eu-west-3" });
const BUCKET = process.env.BUCKET;
const TSA_URL = process.env.TSA_URL || "https://timestamp.sectigo.com";
const DEFAULT_TSR_PREFIX = process.env.DEFAULT_TSR_PREFIX || "evidence/tsa/";
const DEFAULT_REQ_PREFIX = process.env.DEFAULT_REQ_PREFIX || "evidence/tsq/";

/**
 * Attends un event JSON de la forme :
 * {
 *   "requestRef": "CDDU-2025-0102",
 *   "pdfSha256": "<hex>",          // optionnel si pdfS3Key présent
 *   "pdfS3Key": "source/contrat.pdf", // optionnel si pdfSha256 présent
 *   "tsrS3Key": "evidence/tsa/CDDU-2025-0102.tsr" // optionnel (sinon généré)
 * }
 */
export const handler = async (event = {}) => {
  try {
    const requestRef = event.requestRef || randomRef();

    let pdfSha256 = (event.pdfSha256 || "").trim();
    if (!pdfSha256 && event.pdfS3Key) {
      pdfSha256 = await sha256OfS3Object(BUCKET, event.pdfS3Key);
    }
    if (!pdfSha256 || !/^[0-9a-fA-F]{64}$/.test(pdfSha256)) {
      throw new Error("pdfSha256 manquant ou invalide (attendu : hex 64 chars)");
    }

    // 1) Générer la requête RFC3161 (.tsq) via openssl
    const tsqPath = `/tmp/${requestRef}.tsq`;
    await genTsqWithOpenssl(pdfSha256, tsqPath);

    // 2) Appeler la TSA
    const tsrPath = `/tmp/${requestRef}.tsr`;
    await postTsqToTsa(tsqPath, TSA_URL, tsrPath);

    // 3) Hasher la réponse TSA
    const tsrBuf = await readFile(tsrPath);
    const tsrSha256 = crypto.createHash("sha256").update(tsrBuf).digest("hex");

    // 4) Uploader .tsq et .tsr dans S3
    const tsqKey = event.tsqS3Key || `${DEFAULT_REQ_PREFIX}${requestRef}.tsq`;
    const tsrKey = event.tsrS3Key || `${DEFAULT_TSR_PREFIX}${requestRef}.tsr`;

    await s3.send(new PutObjectCommand({ Bucket: BUCKET, Key: tsqKey, Body: await readFile(tsqPath), ContentType: "application/timestamp-query" }));
    await s3.send(new PutObjectCommand({ Bucket: BUCKET, Key: tsrKey, Body: tsrBuf, ContentType: "application/timestamp-reply" }));

    // 5) Réponse
    return ok({
      requestRef,
      tsa_url: TSA_URL,
      pdf_sha256: pdfSha256.toLowerCase(),
      tsq_s3_key: tsqKey,
      tsr_s3_key: tsrKey,
      tsr_sha256: tsrSha256,
      message: "RFC3161 timestamp acquired"
    });
  } catch (err) {
    console.error(err);
    return errResp(err);
  }
};

// Utilitaires
function randomRef() {
  return `TS-${Date.now()}-${Math.random().toString(16).slice(2, 8)}`;
}

async function sha256OfS3Object(bucket, key) {
  const res = await s3.send(new GetObjectCommand({ Bucket: bucket, Key: key }));
  const hash = crypto.createHash("sha256");
  await pipeline(res.Body, hash);
  return hash.digest("hex");
}

async function genTsqWithOpenssl(hexDigest, outPath) {
  // openssl ts -query -sha256 -digest <hex> -cert -no_nonce -out /tmp/req.tsq
  await exec("openssl", ["ts", "-query", "-sha256", "-digest", hexDigest, "-cert", "-no_nonce", "-out", outPath]);
}

async function postTsqToTsa(tsqPath, url, outPath) {
  // curl -sS -H "Content-Type: application/timestamp-query" --data-binary @file url > out
  await exec("curl", ["-sS", "-H", "Content-Type: application/timestamp-query", "--data-binary", `@${tsqPath}`, url, "-o", outPath]);
}

async function exec(cmd, args) {
  return new Promise((resolve, reject) => {
    const p = spawn(cmd, args);
    let stderr = "";
    p.stderr.on("data", (d) => (stderr += d.toString()));
    p.on("exit", (code) => {
      if (code === 0) resolve(0);
      else reject(new Error(`${cmd} exited with ${code}: ${stderr}`));
    });
  });
}

function ok(payload) {
  return { statusCode: 200, headers: { "content-type": "application/json" }, body: JSON.stringify(payload) };
}
function errResp(err) {
  return { statusCode: 500, headers: { "content-type": "application/json" }, body: JSON.stringify({ error: String(err) }) };
}

