import { Injectable, Logger, Inject } from '@nestjs/common';
import { ethers } from 'ethers';
import * as crypto from 'crypto';
import { Wallet } from '../../database/models/wallet.model';

const ENCRYPTION_KEY = process.env.WALLET_ENCRYPTION_KEY || 'goa-gel-demo-encryption-key-32b';

@Injectable()
export class WalletService {
  private readonly logger = new Logger(WalletService.name);

  constructor(
    @Inject(Wallet)
    private readonly walletModel: typeof Wallet,
  ) {}

  /**
   * Encrypt a private key
   */
  private encryptPrivateKey(privateKey: string): string {
    const iv = crypto.randomBytes(16);
    const key = crypto.scryptSync(ENCRYPTION_KEY, 'salt', 32);
    const cipher = crypto.createCipheriv('aes-256-cbc', key, iv);
    let encrypted = cipher.update(privateKey, 'utf8', 'hex');
    encrypted += cipher.final('hex');
    return iv.toString('hex') + ':' + encrypted;
  }

  /**
   * Decrypt a private key
   */
  private decryptPrivateKey(encryptedPrivateKey: string): string {
    const [ivHex, encryptedHex] = encryptedPrivateKey.split(':');
    const iv = Buffer.from(ivHex, 'hex');
    const key = crypto.scryptSync(ENCRYPTION_KEY, 'salt', 32);
    const decipher = crypto.createDecipheriv('aes-256-cbc', key, iv);
    let decrypted = decipher.update(encryptedHex, 'hex', 'utf8');
    decrypted += decipher.final('utf8');
    return decrypted;
  }

  /**
   * Create a new wallet and store it in the database
   */
  async createWallet(ownerType: 'USER' | 'DEPARTMENT', ownerId: string): Promise<Wallet> {
    this.logger.debug(`Creating new wallet for ${ownerType}: ${ownerId}`);

    // Generate new Ethereum wallet
    const ethWallet = ethers.Wallet.createRandom();
    const encryptedPrivateKey = this.encryptPrivateKey(ethWallet.privateKey);

    // Store in database
    const wallet = await this.walletModel.query().insertAndFetch({
      address: ethWallet.address,
      encryptedPrivateKey,
      ownerType,
      ownerId,
      isActive: true,
    });

    this.logger.log(`Wallet created successfully: ${wallet.address} for ${ownerType}: ${ownerId}`);
    return wallet;
  }

  /**
   * Get wallet by owner
   */
  async getWalletByOwner(
    ownerType: 'USER' | 'DEPARTMENT',
    ownerId: string,
  ): Promise<Wallet | undefined> {
    return this.walletModel.query().findOne({
      ownerType,
      ownerId,
      isActive: true,
    });
  }

  /**
   * Get wallet by address
   */
  async getWalletByAddress(address: string): Promise<Wallet | undefined> {
    return this.walletModel.query().findOne({ address, isActive: true });
  }

  /**
   * Get decrypted private key (use with caution!)
   */
  async getPrivateKey(walletId: string): Promise<string> {
    const wallet = await this.walletModel.query().findById(walletId);
    if (!wallet) {
      throw new Error('Wallet not found');
    }
    return this.decryptPrivateKey(wallet.encryptedPrivateKey);
  }

  /**
   * Get all wallets for an owner
   */
  async getWalletsByOwner(ownerType: 'USER' | 'DEPARTMENT', ownerId: string): Promise<Wallet[]> {
    return this.walletModel.query().where({
      ownerType,
      ownerId,
    });
  }

  /**
   * Deactivate a wallet
   */
  async deactivateWallet(walletId: string): Promise<void> {
    await this.walletModel.query().patchAndFetchById(walletId, {
      isActive: false,
    });
    this.logger.log(`Wallet deactivated: ${walletId}`);
  }
}
