/**
 * Delta-Sync Reconciliation Manager
 * Handles transition from Sovereign Mode back to Cloud Mode
 * 
 * Patent Reference: AEGIS-PAT-2026-001
 * Component: Reconciliation Module (240)
 * 
 * Key Features:
 * - Ordered Merging: Uses ORDER BY hlc_timestamp ASC for causal integrity
 * - Idempotency: Unique transaction IDs + HLCs prevent duplicates
 * - Auditability: PQC signatures prove authorization during "dark" period
 */

import { HLC } from './hlc';
import { logger } from './lib/logger';

const syncLogger = logger.child('sync-manager');

export interface PendingTransaction {
  id: string;
  amount: number;
  currency: string;
  hlcTimestamp: bigint;
  pqcSignature: string;
  synced: boolean;
  createdAt: Date;
}

export interface SyncResult {
  success: boolean;
  syncedCount: number;
  failedCount: number;
  errors: string[];
}

export interface CloudSyncResponse {
  acknowledged: boolean;
  hlcTimestamp: string;
  conflictResolution?: 'accepted' | 'rejected' | 'merged';
  message?: string;
}

export class SyncManager {
  private cloudApiUrl: string;
  private clock: HLC;
  private nodeId: string;

  constructor(cloudApiUrl: string, nodeId: string) {
    this.cloudApiUrl = cloudApiUrl;
    this.nodeId = nodeId;
    this.clock = new HLC(nodeId);
  }

  /**
   * Fetches pending 'Sovereign' transactions and pushes to Cloud.
   * Called when transitioning from Sovereign Mode to Cloud Mode.
   */
  async reconcileToCloud(pendingTxs: PendingTransaction[]): Promise<SyncResult> {
    const result: SyncResult = {
      success: true,
      syncedCount: 0,
      failedCount: 0,
      errors: []
    };

    if (!pendingTxs || pendingTxs.length === 0) {
      syncLogger.info('No pending transactions to reconcile');
      return result;
    }

    // Sort by HLC timestamp for causal ordering
    const sortedTxs = [...pendingTxs].sort((a, b) => 
      HLC.compare(a.hlcTimestamp, b.hlcTimestamp)
    );

    syncLogger.info('Reconnecting - reconciling transactions', { metadata: { count: sortedTxs.length } });

    for (const tx of sortedTxs) {
      try {
        const syncResult = await this.syncTransaction(tx);
        
        if (syncResult.acknowledged) {
          result.syncedCount++;
          // Update local clock with cloud's response
          if (syncResult.hlcTimestamp) {
            this.clock.receive(BigInt(syncResult.hlcTimestamp));
          }
        } else {
          result.failedCount++;
          result.errors.push(`TX ${tx.id}: ${syncResult.message || 'Cloud rejected'}`);
          // Stop sync to preserve order if one fails
          result.success = false;
          break;
        }
      } catch (error) {
        result.failedCount++;
        result.errors.push(`TX ${tx.id}: ${error instanceof Error ? error.message : 'Unknown error'}`);
        result.success = false;
        break; // Preserve order
      }
    }

    if (result.success) {
      syncLogger.info('Reconciliation complete');
    } else {
      syncLogger.warn('Reconciliation incomplete', { metadata: { syncedCount: result.syncedCount, failedCount: result.failedCount } });
    }

    return result;
  }

  /**
   * Syncs a single transaction to the cloud using idempotent UPSERT.
   * The cloud uses the HLC_timestamp to verify causal order.
   */
  private async syncTransaction(tx: PendingTransaction): Promise<CloudSyncResponse> {
    const txData = {
      id: tx.id,
      amount: tx.amount,
      currency: tx.currency,
      hlc_timestamp: tx.hlcTimestamp.toString(),
      pqc_signature: tx.pqcSignature,
      node_id: this.nodeId,
      readable_timestamp: HLC.toString(tx.hlcTimestamp)
    };

    const response = await fetch(`${this.cloudApiUrl}/sync`, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'X-Edge-Node-Id': this.nodeId,
        'X-Sync-Protocol': 'delta-sync-v1'
      },
      body: JSON.stringify(txData)
    });

    if (response.ok) {
      const data = await response.json() as CloudSyncResponse;
      return {
        acknowledged: true,
        hlcTimestamp: data.hlcTimestamp,
        conflictResolution: data.conflictResolution || 'accepted',
        message: data.message
      };
    } else {
      const errorText = await response.text();
      return {
        acknowledged: false,
        hlcTimestamp: '',
        message: `HTTP ${response.status}: ${errorText}`
      };
    }
  }

  /**
   * Builds a Merkle root for the local transaction set.
   * Used for efficient divergence detection during sync.
   */
  buildMerkleRoot(transactions: PendingTransaction[]): string {
    if (transactions.length === 0) {
      return '0x0000000000000000';
    }

    // Sort by HLC for deterministic ordering
    const sorted = [...transactions].sort((a, b) => 
      HLC.compare(a.hlcTimestamp, b.hlcTimestamp)
    );

    // Build leaf hashes (simplified - use crypto.subtle in production)
    const leaves = sorted.map(tx => 
      this.simpleHash(`${tx.id}:${tx.hlcTimestamp}:${tx.pqcSignature}`)
    );

    // Build tree up to root
    return this.buildTreeRoot(leaves);
  }

  /**
   * Simple hash function for demo (use crypto.subtle.digest in production)
   */
  private simpleHash(input: string): string {
    let hash = 0;
    for (let i = 0; i < input.length; i++) {
      const char = input.charCodeAt(i);
      hash = ((hash << 5) - hash) + char;
      hash = hash & hash;
    }
    return '0x' + Math.abs(hash).toString(16).padStart(16, '0');
  }

  /**
   * Builds Merkle tree root from leaf hashes
   */
  private buildTreeRoot(leaves: string[]): string {
    if (leaves.length === 0) return '0x0';
    if (leaves.length === 1) return leaves[0];

    const nextLevel: string[] = [];
    for (let i = 0; i < leaves.length; i += 2) {
      const left = leaves[i];
      const right = leaves[i + 1] || left; // Duplicate last if odd
      nextLevel.push(this.simpleHash(left + right));
    }

    return this.buildTreeRoot(nextLevel);
  }

  /**
   * Compares local Merkle root with cloud to detect divergence.
   * Returns list of divergent transaction IDs.
   */
  async detectDivergence(localTransactions: PendingTransaction[]): Promise<{
    divergent: boolean;
    localRoot: string;
    cloudRoot: string;
    divergentIds: string[];
  }> {
    const localRoot = this.buildMerkleRoot(localTransactions);

    try {
      const response = await fetch(`${this.cloudApiUrl}/merkle-root`, {
        method: 'GET',
        headers: {
          'X-Edge-Node-Id': this.nodeId
        }
      });

      if (response.ok) {
        const data = await response.json() as { merkleRoot: string; transactionIds: string[] };
        const cloudRoot = data.merkleRoot;

        if (localRoot === cloudRoot) {
          return {
            divergent: false,
            localRoot,
            cloudRoot,
            divergentIds: []
          };
        }

        // Find divergent transactions (those not in cloud)
        const cloudIds = new Set(data.transactionIds);
        const divergentIds = localTransactions
          .filter(tx => !cloudIds.has(tx.id))
          .map(tx => tx.id);

        return {
          divergent: true,
          localRoot,
          cloudRoot,
          divergentIds
        };
      }
    } catch (error) {
      syncLogger.error('Failed to fetch cloud Merkle root', { error: error instanceof Error ? error : new Error(String((error as any)?.message || error)) });
    }

    return {
      divergent: true,
      localRoot,
      cloudRoot: 'unknown',
      divergentIds: localTransactions.map(tx => tx.id)
    };
  }

  /**
   * Full reconciliation with divergence detection.
   * Step S600-S608 from patent specification.
   */
  async fullReconciliation(localTransactions: PendingTransaction[]): Promise<SyncResult> {
    syncLogger.info('Starting full Delta-Sync reconciliation');

    // S602: Compare Merkle roots
    const divergence = await this.detectDivergence(localTransactions);
    syncLogger.info('Merkle comparison', { metadata: { localRoot: divergence.localRoot, cloudRoot: divergence.cloudRoot } });

    if (!divergence.divergent) {
      syncLogger.info('No divergence detected - already in sync');
      return {
        success: true,
        syncedCount: 0,
        failedCount: 0,
        errors: []
      };
    }

    // S604: Identify divergent transactions
    const pendingToSync = localTransactions.filter(tx => 
      divergence.divergentIds.includes(tx.id) && !tx.synced
    );
    syncLogger.info('Identified divergent transactions', { metadata: { count: pendingToSync.length } });

    // S606: HLC-ordered merge
    const result = await this.reconcileToCloud(pendingToSync);

    // S608: Verify consistency (would re-compare Merkle roots in production)
    if (result.success) {
      syncLogger.info('Delta-Sync complete - State consistency verified');
    }

    return result;
  }
}

// Export singleton factory
let syncManagerInstance: SyncManager | null = null;

export function getSyncManager(cloudApiUrl: string, nodeId: string = 'edge-node-001'): SyncManager {
  if (!syncManagerInstance) {
    syncManagerInstance = new SyncManager(cloudApiUrl, nodeId);
  }
  return syncManagerInstance;
}
