/**
 * Certificate Service
 *
 * Generates cryptographically signed certificates for blockchain-registered contracts.
 * These certificates can be downloaded and verified by third parties, proving that
 * a contract was registered on-chain with specific metadata at a specific timestamp.
 *
 * Uses ECDSA (Elliptic Curve Digital Signature Algorithm) via ethers.js Wallet.
 */

import { ethers } from "ethers";
import { createHash } from "crypto";
import PDFDocument from "pdfkit";
import QRCode from "qrcode";
import type { CryptographicCertificate } from "./blockchain.types";
import { BlockchainService } from "./blockchain.service";

export class CertificateService {
  /**
   * Generate a digitally-signed certificate for a blockchain-registered contract.
   *
   * @param contractId - Contract ID
   * @param contractTitle - Contract title
   * @param contractFileName - Original file name
   * @param documentHash - SHA-256 hash of the document
   * @param txHash - Blockchain transaction hash
   * @param blockNumber - Block number
   * @param blockTimestamp - Block timestamp (ISO string)
   * @param network - Blockchain network ('hardhat' | 'sepolia')
   * @param contractAddress - Smart contract address
   * @returns Digitally-signed certificate
   */
  static async generateCertificate(
    contractId: string,
    contractTitle: string | null,
    contractFileName: string,
    documentHash: string,
    txHash: string,
    blockNumber: number,
    blockTimestamp: string,
    network: string,
    contractAddress: string,
  ): Promise<CryptographicCertificate> {
    if (!BlockchainService.isReadConfigured()) {
      throw new Error("Blockchain not configured for certificate generation");
    }

    // Generate unique certificate ID
    const certificateId = this.generateCertificateId(contractId);

    // Get issuer wallet address
    let issuerAddress = "";
    try {
      // Try to get the wallet address if blockchain write is configured
      if (BlockchainService.isConfigured()) {
        // We can't directly access the wallet from here, so we'll use the private key env var
        const privateKey = process.env.BLOCKCHAIN_PRIVATE_KEY;
        if (privateKey) {
          const wallet = new ethers.Wallet(privateKey);
          issuerAddress = wallet.address;
        }
      }
    } catch {
      // If wallet creation fails, use a placeholder
      issuerAddress = "0x0000000000000000000000000000000000000000";
    }

    const issuedAt = new Date().toISOString();

    // Prepare the data to sign
    const certificateData = {
      contractId,
      contractTitle,
      contractFileName,
      documentHash,
      txHash,
      blockNumber,
      blockTimestamp,
      network,
      contractAddress,
      certificateId,
      issuedAt,
      issuer: issuerAddress,
      version: "1.0",
    };

    // Create a message to sign (hash of the certificate data)
    const messageToSign = this.createSignableMessage(certificateData);
    const messageHash = ethers.hashMessage(messageToSign);

    let signature = "";
    try {
      // Sign the message with the server wallet
      if (BlockchainService.isConfigured()) {
        const privateKey = process.env.BLOCKCHAIN_PRIVATE_KEY;
        if (privateKey) {
          const wallet = new ethers.Wallet(privateKey);
          signature = await wallet.signMessage(messageToSign);
        }
      }
    } catch (error) {
      console.warn("Failed to sign certificate:", error);
      signature = "0x"; // Return empty signature if signing fails
    }

    const certificate: CryptographicCertificate = {
      contractId,
      contractTitle,
      contractFileName,
      documentHash,
      txHash,
      blockNumber,
      blockTimestamp,
      network,
      contractAddress,
      certificateId,
      issuedAt,
      issuer: issuerAddress,
      signature,
      version: "1.0",
    };

    return certificate;
  }

  /**
   * Generate a unique certificate ID based on contract ID and timestamp.
   */
  private static generateCertificateId(contractId: string): string {
    const timestamp = Date.now();
    const combined = `${contractId}:${timestamp}`;
    const hash = createHash("sha256").update(combined).digest("hex");
    return `cert_${hash.slice(0, 16)}`;
  }

  /**
   * Create a human-readable and machine-verifiable message to sign.
   */
  private static createSignableMessage(data: any): string {
    const lines = [
      "═══════════════════════════════════════════",
      "LexiChain Cryptographic Certificate",
      "═══════════════════════════════════════════",
      "",
      `Certificate ID: ${data.certificateId}`,
      `Contract: ${data.contractTitle || data.contractFileName}`,
      `Document Hash: ${data.documentHash}`,
      "",
      "Blockchain Proof:",
      `  Transaction Hash: ${data.txHash}`,
      `  Block Number: ${data.blockNumber}`,
      `  Timestamp: ${data.blockTimestamp}`,
      `  Network: ${data.network}`,
      `  Contract Address: ${data.contractAddress}`,
      "",
      `Issued At: ${data.issuedAt}`,
      `Issued By: ${data.issuer}`,
      "",
      "This certificate cryptographically proves that the above",
      "document was registered on the blockchain at the specified",
      "block and timestamp. The signature on this message can be",
      "verified using the issuer's public key.",
      "═══════════════════════════════════════════",
    ];

    return lines.join("\n");
  }

  /**
   * Verify a certificate's signature (useful for third parties).
   * Returns true if the signature is valid and matches the issuer.
   */
  static verifyCertificateSignature(
    certificate: CryptographicCertificate,
  ): boolean {
    try {
      const messageToSign = this.createSignableMessage(certificate);
      const recoveredAddress = ethers.verifyMessage(
        messageToSign,
        certificate.signature,
      );
      return (
        recoveredAddress.toLowerCase() === certificate.issuer.toLowerCase()
      );
    } catch {
      return false;
    }
  }

  /**
   * Generate a professional PDF certificate for the given certificate data.
   */
  static async generateCertificatePdf(
    certificate: CryptographicCertificate,
  ): Promise<Buffer> {
    const qrPayload = JSON.stringify({
      certificateId: certificate.certificateId,
      contractId: certificate.contractId,
      documentHash: certificate.documentHash,
      txHash: certificate.txHash,
      blockNumber: certificate.blockNumber,
      blockTimestamp: certificate.blockTimestamp,
      network: certificate.network,
      contractAddress: certificate.contractAddress,
      issuer: certificate.issuer,
      issuedAt: certificate.issuedAt,
      version: certificate.version,
    });

    const qrPng = await QRCode.toBuffer(qrPayload, {
      width: 160,
      margin: 1,
      errorCorrectionLevel: "M",
    });

    return new Promise((resolve) => {
      const doc = new PDFDocument({ size: "A4", margin: 56 });
      const chunks: Buffer[] = [];

      doc.on("data", (chunk) => chunks.push(chunk as Buffer));
      doc.on("end", () => resolve(Buffer.concat(chunks)));

      // Header
      doc
        .font("Helvetica-Bold")
        .fontSize(20)
        .fillColor("#111827")
        .text("LexiChain Contract Authenticity Certificate", {
          align: "center",
        })
        .moveDown(0.2);

      doc
        .font("Helvetica")
        .fontSize(10)
        .fillColor("#6B7280")
        .text("Digitally signed proof of blockchain registration", {
          align: "center",
        })
        .moveDown(1.2);

      // Divider
      doc
        .moveTo(doc.page.margins.left, doc.y)
        .lineTo(doc.page.width - doc.page.margins.right, doc.y)
        .lineWidth(1)
        .strokeColor("#E5E7EB")
        .stroke()
        .moveDown(0.8);

      const pageLeft = doc.page.margins.left;
      const pageRight = doc.page.width - doc.page.margins.right;
      const gap = 24;
      const qrSize = 120;
      const rightX = pageRight - qrSize;
      const leftWidth = pageRight - pageLeft - qrSize - gap;
      const sectionTitle = (title: string) => {
        doc
          .font("Helvetica-Bold")
          .fontSize(12)
          .fillColor("#111827")
          .text(title, pageLeft, doc.y)
          .moveDown(0.4);
      };

      const writeFieldAt = (
        x: number,
        width: number,
        label: string,
        value: string,
      ) => {
        doc
          .font("Helvetica-Bold")
          .fontSize(10)
          .fillColor("#111827")
          .text(`${label} `, x, doc.y, { continued: true, width });
        doc.font("Helvetica").fillColor("#111827").text(value, { width });
        doc.moveDown(0.3);
      };

      // Summary + QR
      const summaryStartY = doc.y + 6;
      doc.image(qrPng, rightX, summaryStartY, {
        width: qrSize,
        height: qrSize,
      });
      doc
        .font("Helvetica")
        .fontSize(8)
        .fillColor("#6B7280")
        .text("Verification QR", rightX, summaryStartY + qrSize + 4, {
          width: qrSize,
          align: "center",
        });

      doc.y = summaryStartY;
      doc
        .font("Helvetica-Bold")
        .fontSize(12)
        .fillColor("#111827")
        .text("Certificate Summary", pageLeft, doc.y, { width: leftWidth })
        .moveDown(0.4);

      writeFieldAt(
        pageLeft,
        leftWidth,
        "Certificate ID:",
        certificate.certificateId,
      );
      writeFieldAt(
        pageLeft,
        leftWidth,
        "Contract:",
        certificate.contractTitle || certificate.contractFileName,
      );
      writeFieldAt(pageLeft, leftWidth, "Issued At:", certificate.issuedAt);
      writeFieldAt(pageLeft, leftWidth, "Network:", certificate.network);

      doc.y = Math.max(doc.y, summaryStartY + qrSize + 24);
      doc.moveDown(0.4);

      // Document Proof
      sectionTitle("Document Proof");
      doc.font("Helvetica-Bold").fontSize(10).text("Document Hash:");
      doc
        .font("Courier")
        .fontSize(9)
        .fillColor("#111827")
        .text(certificate.documentHash, { lineGap: 2 })
        .moveDown(0.6);

      // Blockchain Proof
      sectionTitle("Blockchain Proof");
      doc.font("Helvetica-Bold").fontSize(10).text("Transaction Hash:");
      doc
        .font("Courier")
        .fontSize(9)
        .fillColor("#111827")
        .text(certificate.txHash, { lineGap: 2 })
        .moveDown(0.4);

      writeFieldAt(
        pageLeft,
        pageRight - pageLeft,
        "Block Number:",
        String(certificate.blockNumber),
      );
      writeFieldAt(
        pageLeft,
        pageRight - pageLeft,
        "Block Timestamp:",
        certificate.blockTimestamp,
      );
      writeFieldAt(
        pageLeft,
        pageRight - pageLeft,
        "Contract Address:",
        certificate.contractAddress,
      );

      // Issuer and Signature
      doc.moveDown(0.2);
      sectionTitle("Issuer and Signature");
      writeFieldAt(
        pageLeft,
        pageRight - pageLeft,
        "Issued By:",
        certificate.issuer,
      );

      doc.font("Helvetica-Bold").fontSize(10).text("Digital Signature:");
      doc
        .font("Courier")
        .fontSize(8.5)
        .fillColor("#111827")
        .text(certificate.signature || "Signature not available", {
          lineGap: 2,
        })
        .moveDown(0.6);

      doc
        .font("Helvetica")
        .fontSize(9)
        .fillColor("#374151")
        .text(
          "This certificate verifies that the document referenced above was registered on the blockchain and can be independently verified using the issuer address and signature.",
          { align: "left" },
        )
        .moveDown(0.4);

      doc
        .font("Helvetica")
        .fontSize(8)
        .fillColor("#6B7280")
        .text("LexiChain - Confidential and tamper-evident certification.", {
          align: "center",
        });

      doc.end();
    });
  }
}
