/**
 * DID Generator Script
 * @author Matthew Raymer
 * 
 * This script generates and registers Decentralized Identifiers (DIDs) with admin authorization.
 * It supports the creation of Ethereum-based DIDs (did:ethr) and handles the complete 
 * registration flow with the endorser.ch API.
 * 
 * Features:
 * - Ethereum keypair generation using ethers.js
 * - JWT creation and signing using did-jwt
 * - DID registration with admin authorization
 * - Detailed error handling and logging
 * - Command-line interface for admin DID input
 * 
 * Dependencies:
 *   did-jwt: For JWT creation and signing
 *   ethers: For Ethereum account operations
 *   node-fetch: For API communication
 *   commander: For CLI argument parsing
 *   dotenv: For environment variable loading
 * 
 * Usage:
 *   npm run generate-did -- [options]
 * 
 * Options:
 *   -a, --admin-did <did>  Override default admin DID
 *   --api-url <url>        Override default API endpoint
 * 
 * Environment:
 *   Default Admin DID: did:ethr:0x0000694B58C2cC69658993A90D3840C560f2F51F
 *   Default API URL: https://test-api.endorser.ch/api/v2/claim
 */

/// <reference types="node" />
// Add at the top of your file to ignore dom types
import * as didJwt from 'did-jwt';
import { ethers } from 'ethers';
import fetch from 'node-fetch';
import { program } from 'commander';
import * as dotenv from 'dotenv';
import { config } from 'dotenv';

// Load environment variables
config();

const admin_keypair = {
  did: process.env.ADMIN_DID || 'did:ethr:0x0000694B58C2cC69658993A90D3840C560f2F51F',
  privateKey: process.env.ADMIN_PRIVATE_KEY || '2b6472c026ec2aa2c4235c994a63868fc9212d18b58f6cbfe861b52e71330f5b'
};

// Default values from environment
const DEFAULT_ADMIN_DID = admin_keypair.did;
const DEFAULT_API_URL = process.env.ENDORSER_API_URL || 'https://test-api.endorser.ch/api/v2/claim';

/**
 * Result interface for DID creation process
 */
interface DIDCreationResult {
  did: string;          // The generated DID
  privateKey: string;   // Private key without 0x prefix
  publicKey: string;    // Public key with 0x prefix
  isValid: boolean;     // Validation status
  jwt: string;         // Signed JWT for registration
}

/**
 * Result interface for DID registration attempt
 */
interface RegistrationResult {
  success: boolean;     // Registration success status
  error?: string;      // Optional error message
  response?: any;      // Optional API response data
}

/**
 * Creates and validates a new DID with admin authorization
 * 
 * Workflow:
 * 1. Generates new Ethereum keypair using ethers.js
 * 2. Creates DID from public key in did:ethr format
 * 3. Constructs registration claim with:
 *    - Admin DID as the agent (authorizer)
 *    - New DID as the participant (being registered)
 * 4. Signs claim as JWT using admin's private key
 * 
 * @param adminDid - Administrator DID for authorization
 * @returns Promise<DIDCreationResult> - Generated DID details and JWT
 */
async function createAndValidateDID(adminDid: string): Promise<DIDCreationResult> {
  console.log('Using admin DID:', adminDid);
  
  // Generate new DID keypair
  console.log('Generating new keypair...');
  const wallet = ethers.Wallet.createRandom();
  const did = `did:ethr:${wallet.address}`;
  const privateKey = wallet.privateKey.slice(2);
  const publicKey = wallet.publicKey;

  // Create registration claim with admin as agent
  const registerClaim = {
    "@context": "https://schema.org",
    "@type": "RegisterAction",
    agent: { did: adminDid },
    participant: { did: did },
    object: "endorser.ch"
  };

  const vcPayload = {
    iat: Math.floor(Date.now() / 1000),
    exp: Math.floor(Date.now() / 1000) + 300,
    sub: "RegisterAction",
    vc: {
      "@context": ["https://www.w3.org/2018/credentials/v1"],
      type: ["VerifiableCredential"],
      credentialSubject: registerClaim
    }
  };

  console.log('\nGenerated DID Details:');
  console.log('----------------------');
  console.log('DID:', did);
  console.log('Admin DID:', adminDid);
  console.log('Address:', wallet.address);
  console.log('Private Key:', wallet.privateKey);
  console.log('Public Key:', wallet.publicKey);

  console.log('\nDebug Details:');
  console.log('-------------');
  console.log('Private Key (hex):', privateKey);  // Should be without 0x
  console.log('Public Key (hex):', publicKey);    // Should be with 0x
  console.log('Header:', {
    typ: "JWT",
    alg: "ES256K"
  });
  console.log('Payload:', vcPayload);

  // Create and sign JWT with admin's key
  console.log('\nCreating JWT...');
  const signer = didJwt.SimpleSigner(admin_keypair.privateKey);  // Use admin's private key
  const jwt = await didJwt.createJWT(vcPayload, {
    issuer: admin_keypair.did,  // Admin DID as issuer
    signer: signer
  });
  
  console.log('\nJWT Parts:');
  const [header, payload, signature] = jwt.split('.');
  console.log('Header (base64):', header);
  console.log('Payload (base64):', payload);
  console.log('Signature (base64):', signature);

  return { did, privateKey, publicKey, isValid: true, jwt };
}

/**
 * Registers a DID with the endorser.ch API
 * 
 * Workflow:
 * 1. Submits JWT to API endpoint
 * 2. Processes response (success/error)
 * 3. Handles error cases:
 *    - Network errors
 *    - API errors (400, 401, etc)
 *    - Malformed responses
 * 
 * @param jwt - Signed JWT for registration
 * @returns Promise<RegistrationResult> - Registration result with response/error
 */
async function registerDID(jwt: string): Promise<RegistrationResult> {
  try {
    const response = await fetch(DEFAULT_API_URL, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({ jwtEncoded: jwt }),
    });

    console.log(`\nServer Response Status: ${response.status}`);
    const responseText = await response.text();
    console.log(`Server Response Body: ${responseText}`);

    if (response.ok) {
      return {
        success: true,
        response: JSON.parse(responseText)
      };
    } else {
      try {
        const errorJson = JSON.parse(responseText);
        const errorMsg = errorJson.error?.message || 'Unknown error';
        return {
          success: false,
          error: `Registration failed (${response.status}): ${errorMsg}`,
          response: errorJson
        };
      } catch {
        return {
          success: false,
          error: `Registration failed (${response.status}): ${responseText}`,
          response: responseText
        };
      }
    }
  } catch (e) {
    return {
      success: false,
      error: `Request failed: ${e}`
    };
  }
}

// Command line handling
program
    .name('did-generator')
    .description('Generate and register a new DID')
    .option('-a, --admin-did <did>', 'Admin DID', DEFAULT_ADMIN_DID)
    .option('--api-url <url>', 'Override API URL', DEFAULT_API_URL)
    .parse();

const options = program.opts();
const adminDid = options.adminDid;  // Use the option instead of args
const apiUrl = options.apiUrl;

console.log('Starting DID Generation...\n');
console.log('Using admin DID:', adminDid);  // Log the admin DID being used

(async () => {
    try {
        const result = await createAndValidateDID(adminDid);
        console.log('\nSuccessfully generated DID with admin authorization!');
        console.log('Registration JWT:', result.jwt.substring(0, 50) + '...');

        console.log('\nAttempting registration...');
        const registrationResult = await registerDID(result.jwt);
        if (registrationResult.success) {
            console.log('Registration successful!');
            console.log('Response:', JSON.stringify(registrationResult.response, null, 2));
        } else {
            console.log('Registration failed!');
            console.log('Error:', registrationResult.error);
            if (registrationResult.response) {
                console.log('Full response:', JSON.stringify(registrationResult.response, null, 2));
            }
        }
    } catch (error) {
        console.error('\nError:', error);
        process.exit(1);
    }
})();