import { Contract, BigNumber, ContractTransaction, Signer } from 'ethers';

import { RevenueSharedNFTRentalsAbi } from './abi/RevenueSharedNFTRentalsAbi';
import { NetworkConfig, SupportedChainIds } from './networkConfig';
import { IRevenueSharedNFTRentals, NFTStandard, PaymentToken } from './types';
import {
  bigNumberToWei,
  prepareBatch,
  prepareRevenueShareBatch,
} from './utils';

export class RevenueSharedNFTRentals implements IRevenueSharedNFTRentals {
  readonly signer: Signer;
  protected contract: Contract;

  constructor(_signer: Signer, chainId: SupportedChainIds) {
    this.signer = _signer;
    var _address = undefined;
    if (
      NetworkConfig[chainId] === undefined ||
      NetworkConfig[chainId].revenueSharedNFTRentalsContractAddresses ===
        undefined ||
      NetworkConfig[chainId].revenueSharedNFTRentalsContractAddresses.length <=
        0
    ) {
      throw new Error('Requested for unsupported chain');
    } else {
      _address =
        NetworkConfig[chainId].revenueSharedNFTRentalsContractAddresses[0];
      this.contract = new Contract(
        _address,
        RevenueSharedNFTRentalsAbi,
        this.signer
      );
    }
  }

  // {
  //   nftStandard: NFTStandard;
  //   nftAddress: string;
  //   tokenId: BigNumber;
  //   lendAmount: BigNumber;
  //   upfrontFee: BigNumber;
  //   maxRentDuration: number;
  //   revenueShareInfo:  [string, number][];
  //   allowedRenter: string[][];
  //   paymentOption: PaymentToken;
  // }
  async lend(
    lendingRequests: [
      NFTStandard,
      string,
      BigNumber,
      BigNumber,
      string,
      number,
      [string[], number[]],
      [string[]],
      PaymentToken
    ][]
  ): Promise<ContractTransaction> {
    const args = lendingRequests.map(req => [
      Number(req[0]),
      req[1],
      BigNumber.from(req[2]),
      BigNumber.from(req[3]),
      bigNumberToWei(req[4].toString()).toString(),
      req[5],
      req[6],
      req[7],
      req[8],
    ]);
    // const args = prepareBatch({
    //    lendingRequests: lendingRequests.map(req => Number(req.nftStandard)),
    //      nftStandards: nftStandards.map(nft => Number(nft)),
    //      nftAddresses: nftAddresses.map(nft => String(nft).toLowerCase()),
    //      tokenIds: tokenIds.map(id => BigNumber.from(id)),
    //      lendAmounts: lendAmounts.map(amt => Number(amt)),
    //      maxRentDurations: maxRentDurations.map(x => Number(x)),
    //      minRentDurations: minRentDurations.map(x => Number(x)),
    //      dailyRentPrices: dailyRentPrices.map(x => packPrice(Number(x))),
    //      paymentOptions,
    //      collateralPrices: collateralPrices.map(x => packPrice(Number(x))),
    // });

    return await this.contract.lend(args);
  }

  async rent(
    nftAddresses: string[],
    tokenIds: BigNumber[],
    lendingIds: BigNumber[],
    rentDurations: number[],
    rentAmounts: BigNumber[]
  ): Promise<ContractTransaction> {
    const args = prepareRevenueShareBatch({
      nftAddresses: nftAddresses.map(nft => String(nft).toLowerCase()),
      tokenIds: tokenIds.map(id => BigNumber.from(id)),
      lendingIds: lendingIds.map(id => BigNumber.from(id)),
      rentDurations: rentDurations.map(x => Number(x)),
      rentAmounts: rentAmounts.map(amount => BigNumber.from(amount)),
    });

    return await this.contract.rent(
      args.nftAddresses,
      args.tokenIds,
      args.lendingIds,
      args.rentDurations,
      args.rentAmounts
    );
  }

  async stopRenting(
    nftAddresses: string[],
    tokenIds: BigNumber[],
    lendingIds: BigNumber[],
    rentingIds: BigNumber[]
  ): Promise<ContractTransaction> {
    const args = prepareRevenueShareBatch({
      nftAddresses: nftAddresses.map(nft => String(nft).toLowerCase()),
      tokenIds: tokenIds.map(id => BigNumber.from(id)),
      lendingIds: lendingIds.map(id => BigNumber.from(id)),
      rentingIds: rentingIds.map(id => BigNumber.from(id)),
    });

    return await this.contract.stopRenting(
      args.nftAddresses,
      args.tokenIds,
      args.lendingIds,
      args.rentingIds
    );
  }

  async stopLending(
    nftStandards: NFTStandard[],
    nftAddresses: string[],
    tokenIds: BigNumber[],
    lendingIds: BigNumber[]
  ): Promise<ContractTransaction> {
    const args = prepareBatch({
      nftStandards: nftStandards.map(nft => Number(nft)),
      nftAddresses: nftAddresses.map(nft => String(nft).toLowerCase()),
      tokenIds: tokenIds.map(id => BigNumber.from(id)),
      lendingIds: lendingIds.map(id => BigNumber.from(id)),
    });

    return await this.contract.stopLending(
      args.nftStandards,
      args.nftAddresses,
      args.tokenIds,
      args.lendingIds
    );
  }

  async shareRevenue(
    nftAddresses: string[],
    tokenIds: BigNumber[],
    lendingIds: BigNumber[],
    revenueAmounts: BigNumber[],
    renters: string[],
    revenueTokenAddress: string[]
  ): Promise<ContractTransaction> {
    const args = prepareRevenueShareBatch({
      nftAddresses: nftAddresses.map(nft => String(nft).toLowerCase()),
      tokenIds: tokenIds.map(id => BigNumber.from(id)),
      lendingIds: lendingIds.map(id => BigNumber.from(id)),
      revenueAmounts: revenueAmounts.map(amount => BigNumber.from(amount)),
      renters: renters.map(address => String(address).toLowerCase()),
      revenueTokenAddress: revenueTokenAddress.map(address =>
        String(address).toLowerCase()
      ),
    });

    return await this.contract.shareRevenue(
      args.nftAddresses,
      args.tokenIds,
      args.lendingIds,
      args.revenueAmounts,
      args.renters,
      args.revenueTokenAddress
    );
  }
}
