/**
 * TSS Client
 * High-level API for TSS operations in the browser
 */

import { initTSSWasm, isTSSWasmReady } from './tss-wasm-loader';
import { GrpcWebClient } from './grpc-web-client';

export interface Participant {
  partyId: string;
  partyIndex: number;
  name?: string;
}

export interface KeygenParams {
  sessionId: string;
  partyId: string;
  partyIndex: number;
  threshold: number; // T value
  totalParties: number; // N value
  participants: Participant[];
  password?: string; // Optional password for encryption
}

export interface SigningParams {
  sessionId: string;
  partyId: string;
  partyIndex: number;
  thresholdT: number;
  participants: Participant[];
  saveData: string; // JSON string of keygen save data
  messageHash: string; // base64 encoded
}

export interface KeygenResult {
  publicKey: string; // hex encoded
  share: string; // JSON string of the share data
  partyIndex: number;
}

export interface SigningResult {
  signature: string; // base64 encoded
  partyIndex: number;
}

export interface TSSMessage {
  type: string;
  isBroadcast: boolean;
  toParties: string[];
  payload: string;
}

export type ProgressCallback = (current: number, total: number) => void;
export type MessageCallback = (message: TSSMessage) => void;
export type ErrorCallback = (error: string) => void;

/**
 * TSS Client for browser-based TSS operations
 */
export class TSSClient {
  private grpcClient: GrpcWebClient;
  private activeSessionId: string | null = null;
  private messageSubscription: (() => void) | null = null;

  constructor(messageRouterUrl: string) {
    this.grpcClient = new GrpcWebClient(messageRouterUrl);
  }

  /**
   * Initialize the TSS client
   */
  async init(): Promise<void> {
    await initTSSWasm();
    await this.grpcClient.connect();
  }

  /**
   * Check if initialized
   */
  isReady(): boolean {
    return isTSSWasmReady() && this.grpcClient.isConnected();
  }

  /**
   * Register as a party with the Message Router
   */
  async registerParty(partyId: string, role: string = 'temporary'): Promise<void> {
    await this.grpcClient.registerParty(partyId, role);
  }

  /**
   * Perform keygen operation
   */
  async keygen(
    params: KeygenParams,
    onProgress?: ProgressCallback,
    onError?: ErrorCallback
  ): Promise<KeygenResult> {
    if (!isTSSWasmReady()) {
      throw new Error('TSS WASM not initialized');
    }

    this.activeSessionId = params.sessionId;

    // Subscribe to incoming messages
    this.messageSubscription = this.grpcClient.subscribeMessages(
      params.sessionId,
      params.partyId,
      (message) => {
        // Forward message to WASM
        window.tssHandleMessage(
          params.sessionId,
          message.fromPartyIndex,
          message.isBroadcast,
          message.payload
        );
      }
    );

    return new Promise<KeygenResult>((resolve, reject) => {
      const participantsJSON = JSON.stringify(params.participants);

      const result = window.tssStartKeygen(
        params.sessionId,
        params.partyId,
        params.partyIndex,
        params.threshold,
        params.totalParties,
        participantsJSON,
        params.password || '',
        // onMessage - route to other parties
        (msgJSON: string) => {
          try {
            const msg: TSSMessage = JSON.parse(msgJSON);
            this.grpcClient.routeMessage(
              params.sessionId,
              params.partyId,
              msg.toParties,
              msg.isBroadcast,
              msg.payload
            );
          } catch (e) {
            console.error('Failed to route message:', e);
          }
        },
        // onProgress
        (current: number, total: number) => {
          onProgress?.(current, total);
        },
        // onComplete
        (resultJSON: string) => {
          this.cleanup();
          try {
            const parsedResult = JSON.parse(resultJSON);
            resolve({
              publicKey: parsedResult.publicKey,
              share: parsedResult.share || parsedResult.encryptedShare,
              partyIndex: parsedResult.partyIndex,
            });
          } catch (e) {
            reject(new Error('Failed to parse keygen result'));
          }
        },
        // onError
        (error: string) => {
          this.cleanup();
          onError?.(error);
          reject(new Error(error));
        }
      );

      if (!result.success) {
        this.cleanup();
        reject(new Error(result.error || 'Failed to start keygen'));
      }
    });
  }

  /**
   * Perform signing operation
   */
  async sign(
    params: SigningParams,
    onProgress?: ProgressCallback,
    onError?: ErrorCallback
  ): Promise<SigningResult> {
    if (!isTSSWasmReady()) {
      throw new Error('TSS WASM not initialized');
    }

    this.activeSessionId = params.sessionId;

    // Subscribe to incoming messages
    this.messageSubscription = this.grpcClient.subscribeMessages(
      params.sessionId,
      params.partyId,
      (message) => {
        window.tssHandleMessage(
          params.sessionId,
          message.fromPartyIndex,
          message.isBroadcast,
          message.payload
        );
      }
    );

    return new Promise<SigningResult>((resolve, reject) => {
      const participantsJSON = JSON.stringify(params.participants);

      const result = window.tssStartSigning(
        params.sessionId,
        params.partyId,
        params.partyIndex,
        params.thresholdT,
        participantsJSON,
        params.saveData,
        params.messageHash,
        // onMessage
        (msgJSON: string) => {
          try {
            const msg: TSSMessage = JSON.parse(msgJSON);
            this.grpcClient.routeMessage(
              params.sessionId,
              params.partyId,
              msg.toParties,
              msg.isBroadcast,
              msg.payload
            );
          } catch (e) {
            console.error('Failed to route message:', e);
          }
        },
        // onProgress
        (current: number, total: number) => {
          onProgress?.(current, total);
        },
        // onComplete
        (resultJSON: string) => {
          this.cleanup();
          try {
            const result = JSON.parse(resultJSON);
            resolve({
              signature: result.signature,
              partyIndex: result.partyIndex,
            });
          } catch (e) {
            reject(new Error('Failed to parse signing result'));
          }
        },
        // onError
        (error: string) => {
          this.cleanup();
          onError?.(error);
          reject(new Error(error));
        }
      );

      if (!result.success) {
        this.cleanup();
        reject(new Error(result.error || 'Failed to start signing'));
      }
    });
  }

  /**
   * Stop active session
   */
  stop(): void {
    if (this.activeSessionId) {
      window.tssStopSession(this.activeSessionId);
    }
    this.cleanup();
  }

  /**
   * Cleanup resources
   */
  private cleanup(): void {
    if (this.messageSubscription) {
      this.messageSubscription();
      this.messageSubscription = null;
    }
    this.activeSessionId = null;
  }

  /**
   * Disconnect from Message Router
   */
  disconnect(): void {
    this.stop();
    this.grpcClient.disconnect();
  }
}

/**
 * Create a TSS client instance
 */
export function createTSSClient(messageRouterUrl: string): TSSClient {
  return new TSSClient(messageRouterUrl);
}
