/**
 * MPC Coordinator Service
 *
 * 作为 MPC 服务网关，转发请求到 mpc-system (Go)。
 * 只缓存 username + publicKey 的映射关系。
 *
 * 调用路径:
 * other-services → mpc-service → mpc-system (Go/TSS实现)
 */

import { Injectable, Logger } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { HttpService } from '@nestjs/axios';
import { firstValueFrom } from 'rxjs';
import { PrismaService } from '../../infrastructure/persistence/prisma/prisma.service';

// ============================================================================
// Input/Output Types
// ============================================================================

export interface CreateKeygenInput {
  username: string;
  thresholdN: number;
  thresholdT: number;
  requireDelegate: boolean;
}

export interface CreateKeygenOutput {
  sessionId: string;
  username: string;
  thresholdN: number;
  thresholdT: number;
  selectedParties: string[];
  delegateParty?: string;
  status: string;
}

export interface KeygenStatusOutput {
  sessionId: string;
  status: string;
  sessionType: string;
  completedParties: number;
  totalParties: number;
  publicKey?: string;
  hasDelegate?: boolean;
  delegateShare?: {
    partyId: string;
    partyIndex: number;
    encryptedShare: string;
  };
}

export interface CreateSigningInput {
  username: string;
  messageHash: string;
  userShare?: string;
}

export interface CreateSigningOutput {
  sessionId: string;
  username: string;
  messageHash: string;
  thresholdT: number;
  selectedParties: string[];
  hasDelegate: boolean;
  delegatePartyId?: string;
  status: string;
}

export interface SigningStatusOutput {
  sessionId: string;
  status: string;
  sessionType: string;
  completedParties: number;
  totalParties: number;
  signature?: string;
}

export interface WalletOutput {
  username: string;
  publicKey: string;
}

export interface SigningPartiesInput {
  partyIds: string[];
}

export interface SigningPartiesOutput {
  username: string;
  configured: boolean;
  signingParties?: string[];
  activeParties?: string[];
  thresholdT?: number;
  message?: string;
}

export interface DelegateShareInput {
  username: string;
  partyId: string;
  partyIndex: number;
  encryptedShare: string;
}

export interface DelegateShareOutput {
  username: string;
  partyId: string;
  partyIndex: number;
  encryptedShare: string;
}

// ============================================================================
// Service
// ============================================================================

import * as jwt from 'jsonwebtoken';

@Injectable()
export class MPCCoordinatorService {
  private readonly logger = new Logger(MPCCoordinatorService.name);
  private readonly mpcSystemUrl: string;
  private readonly mpcJwtSecret: string;

  constructor(
    private readonly configService: ConfigService,
    private readonly httpService: HttpService,
    private readonly prisma: PrismaService,
  ) {
    this.mpcSystemUrl = this.configService.get<string>('MPC_ACCOUNT_SERVICE_URL', 'http://localhost:4000');
    this.mpcJwtSecret = this.configService.get<string>('MPC_JWT_SECRET', 'change_this_jwt_secret_key_to_random_value_min_32_chars');
  }

  /**
   * 生成 MPC 系统访问的 JWT token
   */
  private generateMpcAccessToken(userId: string, username: string): string {
    const now = Math.floor(Date.now() / 1000);
    const payload = {
      jti: crypto.randomUUID(),
      iss: 'mpc-service',
      sub: userId,
      party_id: username,
      token_type: 'access',
      iat: now,
      nbf: now,
      exp: now + 24 * 60 * 60, // 24 hours
    };
    return jwt.sign(payload, this.mpcJwtSecret, { algorithm: 'HS256' });
  }

  /**
   * 获取 MPC 系统的 Authorization header
   */
  private getMpcAuthHeaders(userId: string = 'service', username: string = 'mpc-service'): Record<string, string> {
    const token = this.generateMpcAccessToken(userId, username);
    return {
      'Content-Type': 'application/json',
      'Authorization': `Bearer ${token}`,
    };
  }

  // ==========================================================================
  // Keygen APIs
  // ==========================================================================

  /**
   * 创建 keygen 会话 - 转发到 mpc-system
   */
  async createKeygenSession(input: CreateKeygenInput): Promise<CreateKeygenOutput> {
    this.logger.log(`Creating keygen session: username=${input.username}`);

    const response = await firstValueFrom(
      this.httpService.post<{
        session_id: string;
        session_type: string;
        username: string;
        threshold_n: number;
        threshold_t: number;
        selected_parties: string[];
        delegate_party: string;
        status: string;
      }>(
        `${this.mpcSystemUrl}/api/v1/mpc/keygen`,
        {
          username: input.username,
          threshold_n: input.thresholdN,
          threshold_t: input.thresholdT,
          require_delegate: input.requireDelegate,
        },
        {
          headers: this.getMpcAuthHeaders(),
          timeout: 30000,
        },
      ),
    );

    const data = response.data;
    this.logger.log(`Keygen session created: ${data.session_id}`);

    return {
      sessionId: data.session_id,
      username: input.username,
      thresholdN: data.threshold_n,
      thresholdT: data.threshold_t,
      selectedParties: data.selected_parties,
      delegateParty: data.delegate_party,
      status: data.status,
    };
  }

  /**
   * 获取 keygen 会话状态 - 转发到 mpc-system
   * 如果完成，缓存公钥到本地
   */
  async getKeygenStatus(sessionId: string): Promise<KeygenStatusOutput> {
    const response = await firstValueFrom(
      this.httpService.get<{
        session_id: string;
        status: string;
        session_type: string;
        completed_parties: number;
        total_parties: number;
        public_key?: string;
        has_delegate?: boolean;
        delegate_share?: {
          encrypted_share: string;
          party_index: number;
          party_id: string;
        };
      }>(
        `${this.mpcSystemUrl}/api/v1/mpc/sessions/${sessionId}`,
        {
          headers: this.getMpcAuthHeaders(),
          timeout: 10000,
        },
      ),
    );

    const data = response.data;

    // 如果完成且有公钥，缓存到本地
    if (data.status === 'completed' && data.public_key) {
      await this.cachePublicKey(sessionId, data.public_key);
    }

    const result: KeygenStatusOutput = {
      sessionId: data.session_id,
      status: data.status,
      sessionType: data.session_type,
      completedParties: data.completed_parties,
      totalParties: data.total_parties,
      publicKey: data.public_key,
      hasDelegate: data.has_delegate,
    };

    if (data.delegate_share) {
      result.delegateShare = {
        partyId: data.delegate_share.party_id,
        partyIndex: data.delegate_share.party_index,
        encryptedShare: data.delegate_share.encrypted_share,
      };
    }

    return result;
  }

  // ==========================================================================
  // Signing APIs
  // ==========================================================================

  /**
   * 创建签名会话 - 转发到 mpc-system
   */
  async createSigningSession(input: CreateSigningInput): Promise<CreateSigningOutput> {
    this.logger.log(`Creating signing session: username=${input.username}`);

    const response = await firstValueFrom(
      this.httpService.post<{
        session_id: string;
        session_type: string;
        username: string;
        message_hash: string;
        threshold_t: number;
        selected_parties: string[];
        has_delegate: boolean;
        delegate_party_id?: string;
        status: string;
      }>(
        `${this.mpcSystemUrl}/api/v1/mpc/sign`,
        {
          username: input.username,
          message_hash: input.messageHash,
          user_share: input.userShare,
        },
        {
          headers: this.getMpcAuthHeaders(),
          timeout: 30000,
        },
      ),
    );

    const data = response.data;
    this.logger.log(`Signing session created: ${data.session_id}`);

    return {
      sessionId: data.session_id,
      username: input.username,
      messageHash: data.message_hash,
      thresholdT: data.threshold_t,
      selectedParties: data.selected_parties,
      hasDelegate: data.has_delegate,
      delegatePartyId: data.delegate_party_id,
      status: data.status,
    };
  }

  /**
   * 获取签名会话状态 - 转发到 mpc-system
   */
  async getSigningStatus(sessionId: string): Promise<SigningStatusOutput> {
    const response = await firstValueFrom(
      this.httpService.get<{
        session_id: string;
        status: string;
        session_type: string;
        completed_parties: number;
        total_parties: number;
        signature?: string;
      }>(
        `${this.mpcSystemUrl}/api/v1/mpc/sessions/${sessionId}`,
        {
          headers: this.getMpcAuthHeaders(),
          timeout: 10000,
        },
      ),
    );

    const data = response.data;

    return {
      sessionId: data.session_id,
      status: data.status,
      sessionType: data.session_type,
      completedParties: data.completed_parties,
      totalParties: data.total_parties,
      signature: data.signature,
    };
  }

  // ==========================================================================
  // Wallet APIs
  // ==========================================================================

  /**
   * 根据用户名获取公钥 - 先查本地缓存，没有则转发到 mpc-system
   */
  async getPublicKeyByUsername(username: string): Promise<WalletOutput> {
    // 先查本地缓存
    const cached = await this.prisma.mpcWallet.findUnique({
      where: { username },
    });

    if (cached) {
      return {
        username: cached.username,
        publicKey: cached.publicKey,
      };
    }

    // 本地没有，转发到 mpc-system
    const response = await firstValueFrom(
      this.httpService.get<{
        account: {
          username: string;
          public_key: string;
        };
      }>(
        `${this.mpcSystemUrl}/api/v1/accounts`,
        {
          params: { username },
          headers: this.getMpcAuthHeaders(),
          timeout: 10000,
        },
      ),
    );

    const publicKey = response.data.account.public_key;

    // 缓存到本地
    await this.prisma.mpcWallet.upsert({
      where: { username },
      update: { publicKey },
      create: { username, publicKey },
    });

    return {
      username,
      publicKey,
    };
  }

  // ==========================================================================
  // Signing Parties Configuration APIs
  // ==========================================================================

  /**
   * 设定签名 party - 转发到 mpc-system
   */
  async setSigningParties(username: string, input: SigningPartiesInput): Promise<SigningPartiesOutput> {
    this.logger.log(`Setting signing parties: username=${username}, parties=${input.partyIds.join(',')}`);

    const response = await firstValueFrom(
      this.httpService.post<{
        message: string;
        username: string;
        signing_parties: string[];
        threshold_t: number;
      }>(
        `${this.mpcSystemUrl}/api/v1/accounts/by-username/${username}/signing-config`,
        {
          party_ids: input.partyIds,
        },
        {
          headers: this.getMpcAuthHeaders(),
          timeout: 10000,
        },
      ),
    );

    return {
      username: response.data.username,
      configured: true,
      signingParties: response.data.signing_parties,
      thresholdT: response.data.threshold_t,
      message: response.data.message,
    };
  }

  /**
   * 更新签名 party - 转发到 mpc-system
   */
  async updateSigningParties(username: string, input: SigningPartiesInput): Promise<SigningPartiesOutput> {
    this.logger.log(`Updating signing parties: username=${username}, parties=${input.partyIds.join(',')}`);

    const response = await firstValueFrom(
      this.httpService.put<{
        message: string;
        username: string;
        signing_parties: string[];
        threshold_t: number;
      }>(
        `${this.mpcSystemUrl}/api/v1/accounts/by-username/${username}/signing-config`,
        {
          party_ids: input.partyIds,
        },
        {
          headers: this.getMpcAuthHeaders(),
          timeout: 10000,
        },
      ),
    );

    return {
      username: response.data.username,
      configured: true,
      signingParties: response.data.signing_parties,
      thresholdT: response.data.threshold_t,
      message: response.data.message,
    };
  }

  /**
   * 查询签名 party - 转发到 mpc-system
   */
  async getSigningParties(username: string): Promise<SigningPartiesOutput> {
    this.logger.debug(`Getting signing parties: username=${username}`);

    const response = await firstValueFrom(
      this.httpService.get<{
        configured: boolean;
        username: string;
        signing_parties?: string[];
        active_parties?: string[];
        threshold_t: number;
        message?: string;
      }>(
        `${this.mpcSystemUrl}/api/v1/accounts/by-username/${username}/signing-config`,
        {
          headers: this.getMpcAuthHeaders(),
          timeout: 10000,
        },
      ),
    );

    return {
      username: response.data.username,
      configured: response.data.configured,
      signingParties: response.data.signing_parties,
      activeParties: response.data.active_parties,
      thresholdT: response.data.threshold_t,
      message: response.data.message,
    };
  }

  /**
   * 删除签名 party 配置 - 转发到 mpc-system
   */
  async clearSigningParties(username: string): Promise<SigningPartiesOutput> {
    this.logger.log(`Clearing signing parties: username=${username}`);

    const response = await firstValueFrom(
      this.httpService.delete<{
        message: string;
        username: string;
      }>(
        `${this.mpcSystemUrl}/api/v1/accounts/by-username/${username}/signing-config`,
        {
          headers: this.getMpcAuthHeaders(),
          timeout: 10000,
        },
      ),
    );

    return {
      username: response.data.username,
      configured: false,
      message: response.data.message,
    };
  }

  // ==========================================================================
  // Internal Helpers
  // ==========================================================================

  /**
   * 缓存公钥到本地数据库
   */
  private async cachePublicKey(sessionId: string, publicKey: string): Promise<void> {
    try {
      // 从 mpc-system 获取 username (通过 session)
      // 这里简化处理，实际可能需要额外 API 调用
      // 暂时用 sessionId 的前缀作为 username 占位
      this.logger.debug(`Caching public key for session: ${sessionId}`);

      // TODO: 获取真实的 username 并缓存
      // 目前 keygen 完成后由调用方传入 username
    } catch (error) {
      this.logger.warn(`Failed to cache public key: ${error}`);
    }
  }

  /**
   * 保存公钥缓存（供 controller 调用）
   */
  async savePublicKeyCache(username: string, publicKey: string): Promise<void> {
    await this.prisma.mpcWallet.upsert({
      where: { username },
      update: { publicKey },
      create: { username, publicKey },
    });
    this.logger.log(`Public key cached: username=${username}`);
  }

  // ==========================================================================
  // Delegate Share APIs
  // ==========================================================================

  /**
   * 保存 delegate share（keygen 完成后由 controller 调用）
   * delegate share 由 mpc-system 的 delegate server-party-api 返回
   */
  async saveDelegateShare(input: DelegateShareInput): Promise<void> {
    await this.prisma.mpcShare.upsert({
      where: { username: input.username },
      update: {
        partyId: input.partyId,
        partyIndex: input.partyIndex,
        encryptedShare: input.encryptedShare,
      },
      create: {
        username: input.username,
        partyId: input.partyId,
        partyIndex: input.partyIndex,
        encryptedShare: input.encryptedShare,
      },
    });
    this.logger.log(`Delegate share saved: username=${input.username}, partyId=${input.partyId}`);
  }

  /**
   * 获取 delegate share（签名时使用）
   */
  async getDelegateShare(username: string): Promise<DelegateShareOutput | null> {
    const share = await this.prisma.mpcShare.findUnique({
      where: { username },
    });

    if (!share) {
      return null;
    }

    return {
      username: share.username,
      partyId: share.partyId,
      partyIndex: share.partyIndex,
      encryptedShare: share.encryptedShare,
    };
  }

  /**
   * 删除 delegate share
   */
  async deleteDelegateShare(username: string): Promise<void> {
    await this.prisma.mpcShare.delete({
      where: { username },
    });
    this.logger.log(`Delegate share deleted: username=${username}`);
  }
}
