import { v4 as uuidv4 } from 'uuid';
import { Wallet } from '../../../domain/entities/wallet';
import { Address } from '../../../domain/entities/address';
import { NotFoundError } from '../../../domain/errors/not-found-error';
import { ValidationError } from '../../../domain/errors/validation-error';
import { IWalletRepository } from '../../../domain/repositories/wallet-repository';
import { IDeviceRepository } from '../../../domain/repositories/device-repository';
import { IAddressRepository } from '../../../domain/repositories/address-repository';
import { INetworkRepository } from '../../../domain/repositories/network-repository';
import { IEncryptionService } from '../../ports/encryption-service';
import { IAddressGenerator } from '../../ports/address-generator';
import { CreateWalletInput } from '../../dto/wallet.dto';

export class CreateWalletUseCase {
  constructor(
    private readonly walletRepository: IWalletRepository,
    private readonly deviceRepository: IDeviceRepository,
    private readonly addressRepository: IAddressRepository,
    private readonly networkRepository: INetworkRepository,
    private readonly encryptionService: IEncryptionService,
    private readonly addressGenerator: IAddressGenerator,
  ) {}

  async execute(input: CreateWalletInput): Promise<Wallet> {
    for (const deviceId of input.deviceIds) {
      const device = await this.deviceRepository.findById(deviceId);
      if (!device) {
        throw new NotFoundError('Device', deviceId);
      }
    }

    const encryptedPhrase = this.encryptionService.encrypt(input.phrase);
    if (!encryptedPhrase) {
      throw new ValidationError('Failed to encrypt wallet phrase');
    }

    for (const deviceId of input.deviceIds) {
      const existing = await this.walletRepository.findByDeviceIdAndName(deviceId, input.name);
      if (existing) {
        throw new ValidationError(
          `Wallet with name "${input.name}" already exists for this device`,
        );
      }
    }

    const wallet = Wallet.create(
      uuidv4(),
      input.name,
      input.syncType,
      encryptedPhrase,
      input.deviceIds,
    );
    const savedWallet = await this.walletRepository.save(wallet);

    const activeNetworks = await this.networkRepository.findActive();
    const addressTypes = new Set<string>();

    for (const network of activeNetworks) {
      if (addressTypes.has(network.addressType)) {
        continue;
      }
      addressTypes.add(network.addressType);

      const generated = this.addressGenerator.generate(network, savedWallet);
      const address = Address.create(
        uuidv4(),
        generated.address,
        generated.type,
        savedWallet.id,
        false,
      );
      await this.addressRepository.save(address);
    }

    return savedWallet;
  }
}
