/**
 * Digital Signatures System for LocalGreenChain
 *
 * Provides cryptographic verification for:
 * - Transport handoffs
 * - Plant ownership transfers
 * - Data integrity verification
 * - Multi-party attestations
 */

import * as crypto from 'crypto';
import * as fs from 'fs';
import * as path from 'path';

// Signature Types
export type SignatureType =
  | 'OWNERSHIP_TRANSFER'
  | 'TRANSPORT_HANDOFF'
  | 'HARVEST_ATTESTATION'
  | 'QUALITY_CERTIFICATION'
  | 'ORIGIN_VERIFICATION'
  | 'DATA_INTEGRITY';

export interface KeyPair {
  publicKey: string;
  privateKey: string;
  algorithm: 'RSA' | 'ECDSA';
  createdAt: string;
}

export interface SignatureIdentity {
  id: string;
  name: string;
  publicKey: string;
  type: 'GROWER' | 'TRANSPORTER' | 'CERTIFIER' | 'CONSUMER' | 'SYSTEM';
  verified: boolean;
  createdAt: string;
  metadata?: Record<string, any>;
}

export interface DigitalSignature {
  id: string;
  type: SignatureType;
  signerId: string;
  signerPublicKey: string;
  timestamp: string;
  data: string; // JSON stringified data that was signed
  signature: string; // Base64 encoded signature
  algorithm: string;
  resourceType: string;
  resourceId: string;
  metadata?: Record<string, any>;
}

export interface MultiSignature {
  id: string;
  type: SignatureType;
  resourceType: string;
  resourceId: string;
  requiredSigners: string[];
  signatures: DigitalSignature[];
  threshold: number;
  complete: boolean;
  createdAt: string;
  completedAt?: string;
  expiresAt?: string;
}

export interface SignatureVerification {
  valid: boolean;
  signerId: string;
  timestamp: string;
  resourceId: string;
  errors?: string[];
}

export interface CertificateOfAuthenticity {
  id: string;
  plantId: string;
  plantName: string;
  variety: string;
  origin: {
    grower: string;
    location: string;
    registeredAt: string;
  };
  lineage: {
    generation: number;
    parentId?: string;
    childCount: number;
  };
  signatures: Array<{
    type: SignatureType;
    signer: string;
    timestamp: string;
    verified: boolean;
  }>;
  transportHistory: Array<{
    from: string;
    to: string;
    date: string;
    verified: boolean;
  }>;
  certifications: string[];
  qrCode?: string;
  generatedAt: string;
  hash: string;
}

class SignatureManager {
  private identities: Map<string, SignatureIdentity> = new Map();
  private signatures: Map<string, DigitalSignature> = new Map();
  private multiSignatures: Map<string, MultiSignature> = new Map();
  private dataDir: string;
  private dataFile: string;

  constructor() {
    this.dataDir = path.join(process.cwd(), 'data');
    this.dataFile = path.join(this.dataDir, 'signatures.json');
    this.load();
  }

  private load(): void {
    try {
      if (fs.existsSync(this.dataFile)) {
        const data = JSON.parse(fs.readFileSync(this.dataFile, 'utf-8'));

        if (data.identities) {
          data.identities.forEach((id: SignatureIdentity) => {
            this.identities.set(id.id, id);
          });
        }

        if (data.signatures) {
          data.signatures.forEach((sig: DigitalSignature) => {
            this.signatures.set(sig.id, sig);
          });
        }

        if (data.multiSignatures) {
          data.multiSignatures.forEach((ms: MultiSignature) => {
            this.multiSignatures.set(ms.id, ms);
          });
        }

        console.log(`[SignatureManager] Loaded ${this.identities.size} identities, ${this.signatures.size} signatures`);
      }
    } catch (error) {
      console.error('[SignatureManager] Error loading data:', error);
    }
  }

  private save(): void {
    try {
      if (!fs.existsSync(this.dataDir)) {
        fs.mkdirSync(this.dataDir, { recursive: true });
      }

      const data = {
        identities: Array.from(this.identities.values()),
        signatures: Array.from(this.signatures.values()),
        multiSignatures: Array.from(this.multiSignatures.values()),
        savedAt: new Date().toISOString()
      };

      fs.writeFileSync(this.dataFile, JSON.stringify(data, null, 2));
    } catch (error) {
      console.error('[SignatureManager] Error saving data:', error);
    }
  }

  /**
   * Generate a new key pair
   */
  generateKeyPair(algorithm: 'RSA' | 'ECDSA' = 'ECDSA'): KeyPair {
    if (algorithm === 'RSA') {
      const { publicKey, privateKey } = crypto.generateKeyPairSync('rsa', {
        modulusLength: 2048,
        publicKeyEncoding: { type: 'spki', format: 'pem' },
        privateKeyEncoding: { type: 'pkcs8', format: 'pem' }
      });

      return {
        publicKey,
        privateKey,
        algorithm: 'RSA',
        createdAt: new Date().toISOString()
      };
    } else {
      const { publicKey, privateKey } = crypto.generateKeyPairSync('ec', {
        namedCurve: 'secp256k1',
        publicKeyEncoding: { type: 'spki', format: 'pem' },
        privateKeyEncoding: { type: 'pkcs8', format: 'pem' }
      });

      return {
        publicKey,
        privateKey,
        algorithm: 'ECDSA',
        createdAt: new Date().toISOString()
      };
    }
  }

  /**
   * Register a new identity
   */
  registerIdentity(
    name: string,
    type: SignatureIdentity['type'],
    publicKey: string,
    metadata?: Record<string, any>
  ): SignatureIdentity {
    const id = `id_${crypto.randomBytes(16).toString('hex')}`;

    const identity: SignatureIdentity = {
      id,
      name,
      publicKey,
      type,
      verified: false,
      createdAt: new Date().toISOString(),
      metadata
    };

    this.identities.set(id, identity);
    this.save();

    return identity;
  }

  /**
   * Get identity by ID
   */
  getIdentity(id: string): SignatureIdentity | undefined {
    return this.identities.get(id);
  }

  /**
   * Get all identities
   */
  getAllIdentities(): SignatureIdentity[] {
    return Array.from(this.identities.values());
  }

  /**
   * Verify an identity
   */
  verifyIdentity(id: string): boolean {
    const identity = this.identities.get(id);
    if (identity) {
      identity.verified = true;
      this.save();
      return true;
    }
    return false;
  }

  /**
   * Sign data
   */
  sign(
    type: SignatureType,
    resourceType: string,
    resourceId: string,
    data: any,
    privateKey: string,
    signerId: string,
    metadata?: Record<string, any>
  ): DigitalSignature {
    const dataString = JSON.stringify(data);

    // Create signature
    const sign = crypto.createSign('SHA256');
    sign.update(dataString);
    const signature = sign.sign(privateKey, 'base64');

    const id = `sig_${Date.now()}_${crypto.randomBytes(8).toString('hex')}`;

    const identity = this.identities.get(signerId);

    const digitalSignature: DigitalSignature = {
      id,
      type,
      signerId,
      signerPublicKey: identity?.publicKey || '',
      timestamp: new Date().toISOString(),
      data: dataString,
      signature,
      algorithm: 'SHA256',
      resourceType,
      resourceId,
      metadata
    };

    this.signatures.set(id, digitalSignature);
    this.save();

    return digitalSignature;
  }

  /**
   * Verify a signature
   */
  verify(signatureId: string): SignatureVerification {
    const sig = this.signatures.get(signatureId);

    if (!sig) {
      return {
        valid: false,
        signerId: '',
        timestamp: '',
        resourceId: '',
        errors: ['Signature not found']
      };
    }

    try {
      const verify = crypto.createVerify('SHA256');
      verify.update(sig.data);
      const isValid = verify.verify(sig.signerPublicKey, sig.signature, 'base64');

      return {
        valid: isValid,
        signerId: sig.signerId,
        timestamp: sig.timestamp,
        resourceId: sig.resourceId,
        errors: isValid ? undefined : ['Signature verification failed']
      };
    } catch (error) {
      return {
        valid: false,
        signerId: sig.signerId,
        timestamp: sig.timestamp,
        resourceId: sig.resourceId,
        errors: [`Verification error: ${error}`]
      };
    }
  }

  /**
   * Verify data with signature directly
   */
  verifyData(data: any, signature: string, publicKey: string): boolean {
    try {
      const dataString = JSON.stringify(data);
      const verify = crypto.createVerify('SHA256');
      verify.update(dataString);
      return verify.verify(publicKey, signature, 'base64');
    } catch {
      return false;
    }
  }

  /**
   * Create a multi-signature request
   */
  createMultiSignature(
    type: SignatureType,
    resourceType: string,
    resourceId: string,
    requiredSigners: string[],
    threshold?: number,
    expiresIn?: number
  ): MultiSignature {
    const id = `msig_${Date.now()}_${crypto.randomBytes(8).toString('hex')}`;

    const multiSig: MultiSignature = {
      id,
      type,
      resourceType,
      resourceId,
      requiredSigners,
      signatures: [],
      threshold: threshold || requiredSigners.length,
      complete: false,
      createdAt: new Date().toISOString(),
      expiresAt: expiresIn ? new Date(Date.now() + expiresIn).toISOString() : undefined
    };

    this.multiSignatures.set(id, multiSig);
    this.save();

    return multiSig;
  }

  /**
   * Add a signature to a multi-signature request
   */
  addToMultiSignature(
    multiSigId: string,
    signature: DigitalSignature
  ): MultiSignature | null {
    const multiSig = this.multiSignatures.get(multiSigId);

    if (!multiSig) return null;

    // Check if signer is required
    if (!multiSig.requiredSigners.includes(signature.signerId)) {
      return null;
    }

    // Check if already signed
    if (multiSig.signatures.some(s => s.signerId === signature.signerId)) {
      return multiSig;
    }

    // Check expiration
    if (multiSig.expiresAt && new Date(multiSig.expiresAt) < new Date()) {
      return null;
    }

    multiSig.signatures.push(signature);

    // Check if complete
    if (multiSig.signatures.length >= multiSig.threshold) {
      multiSig.complete = true;
      multiSig.completedAt = new Date().toISOString();
    }

    this.save();

    return multiSig;
  }

  /**
   * Get multi-signature status
   */
  getMultiSignature(id: string): MultiSignature | undefined {
    return this.multiSignatures.get(id);
  }

  /**
   * Get signatures for a resource
   */
  getSignaturesForResource(resourceType: string, resourceId: string): DigitalSignature[] {
    return Array.from(this.signatures.values()).filter(
      s => s.resourceType === resourceType && s.resourceId === resourceId
    );
  }

  /**
   * Generate a Certificate of Authenticity
   */
  async generateCertificate(plantId: string): Promise<CertificateOfAuthenticity | null> {
    try {
      // Load plant data
      const chainFile = path.join(this.dataDir, 'plantchain.json');
      if (!fs.existsSync(chainFile)) {
        return null;
      }

      const chainData = JSON.parse(fs.readFileSync(chainFile, 'utf-8'));
      const chain = chainData.chain || [];

      // Find plant
      const plantBlock = chain.find((b: any) => b.plant?.id === plantId);
      if (!plantBlock) {
        return null;
      }

      const plant = plantBlock.plant;

      // Get signatures for this plant
      const plantSignatures = this.getSignaturesForResource('plant', plantId);

      // Build certificate
      const certificate: CertificateOfAuthenticity = {
        id: `cert_${crypto.randomBytes(16).toString('hex')}`,
        plantId,
        plantName: plant.name || 'Unknown',
        variety: plant.variety || 'Unknown',
        origin: {
          grower: plant.ownerId || 'Unknown',
          location: plant.location?.region || 'Unknown',
          registeredAt: plantBlock.timestamp
        },
        lineage: {
          generation: plant.generation || 1,
          parentId: plant.parentId,
          childCount: plant.childPlants?.length || 0
        },
        signatures: plantSignatures.map(sig => ({
          type: sig.type,
          signer: sig.signerId,
          timestamp: sig.timestamp,
          verified: this.verify(sig.id).valid
        })),
        transportHistory: [],
        certifications: [],
        generatedAt: new Date().toISOString(),
        hash: ''
      };

      // Calculate certificate hash
      certificate.hash = crypto
        .createHash('sha256')
        .update(JSON.stringify({
          plantId: certificate.plantId,
          origin: certificate.origin,
          lineage: certificate.lineage,
          signatures: certificate.signatures
        }))
        .digest('hex');

      return certificate;
    } catch (error) {
      console.error('[SignatureManager] Error generating certificate:', error);
      return null;
    }
  }

  /**
   * Verify a certificate
   */
  verifyCertificate(certificate: CertificateOfAuthenticity): { valid: boolean; errors: string[] } {
    const errors: string[] = [];

    // Verify hash
    const expectedHash = crypto
      .createHash('sha256')
      .update(JSON.stringify({
        plantId: certificate.plantId,
        origin: certificate.origin,
        lineage: certificate.lineage,
        signatures: certificate.signatures
      }))
      .digest('hex');

    if (certificate.hash !== expectedHash) {
      errors.push('Certificate hash mismatch - data may have been tampered');
    }

    // Verify each signature
    for (const sig of certificate.signatures) {
      if (!sig.verified) {
        errors.push(`Signature from ${sig.signer} is not verified`);
      }
    }

    return {
      valid: errors.length === 0,
      errors
    };
  }

  /**
   * Create a transport handoff signature
   */
  createTransportHandoff(
    plantId: string,
    fromParty: string,
    toParty: string,
    location: { lat: number; lng: number },
    fromPrivateKey: string
  ): DigitalSignature {
    const handoffData = {
      plantId,
      fromParty,
      toParty,
      location,
      timestamp: new Date().toISOString(),
      type: 'TRANSPORT_HANDOFF'
    };

    return this.sign(
      'TRANSPORT_HANDOFF',
      'transport',
      plantId,
      handoffData,
      fromPrivateKey,
      fromParty,
      { toParty, location }
    );
  }

  /**
   * Get statistics
   */
  getStats(): {
    totalIdentities: number;
    verifiedIdentities: number;
    totalSignatures: number;
    signaturesByType: Record<string, number>;
    pendingMultiSigs: number;
    completedMultiSigs: number;
  } {
    const signaturesByType: Record<string, number> = {};

    for (const sig of this.signatures.values()) {
      signaturesByType[sig.type] = (signaturesByType[sig.type] || 0) + 1;
    }

    const multiSigArray = Array.from(this.multiSignatures.values());

    return {
      totalIdentities: this.identities.size,
      verifiedIdentities: Array.from(this.identities.values()).filter(i => i.verified).length,
      totalSignatures: this.signatures.size,
      signaturesByType,
      pendingMultiSigs: multiSigArray.filter(ms => !ms.complete).length,
      completedMultiSigs: multiSigArray.filter(ms => ms.complete).length
    };
  }
}

// Singleton instance
let signatureManagerInstance: SignatureManager | null = null;

export function getSignatureManager(): SignatureManager {
  if (!signatureManagerInstance) {
    signatureManagerInstance = new SignatureManager();
  }
  return signatureManagerInstance;
}

export default SignatureManager;
