/* eslint-disable @typescript-eslint/no-non-null-assertion */

import { expect } from "chai"
import hre, { ethers, upgrades } from "hardhat"
import { TASK_COMPILE } from "hardhat/builtin-tasks/task-names"
import { SRC_DIR } from "../../constants"
import { flatten } from "../../scripts/flatten"
import { ClearingHouse } from "../../types/ethers"
import { getImplementation } from "../contract/DeployUtil"
import { AmmInstanceName, ContractFullyQualifiedName, ContractName } from "../ContractName"
import { MigrationContext, MigrationDefinition } from "../Migration"

const migration: MigrationDefinition = {
    configPath: "hardhat.flatten.clearinghouse.config.ts",

    // deploy the flattened clearingHouse and init it just in case
    getTasks: (context: MigrationContext) => {
        let arbitrageur: string
        let initMarginRatio: string
        let oldImpAddr: string
        let insuranceFund: string
        let BTC: string
        let arbitrageurBTCPositionSize: string
        let openInterestNotional: string
        let newImplContractAddr: string
        return [
            async (): Promise<void> => {
                console.log("verifying state variables...")

                // have to first flatten contracts for creating instances
                const filename = `${ContractName.ClearingHouse}.sol`
                await flatten(SRC_DIR, hre.config.paths.sources, filename)
                // after flatten sol file we must re-compile again
                await hre.run(TASK_COMPILE)

                const clearingHouseContract = await context.factory
                    .create<ClearingHouse>(ContractFullyQualifiedName.FlattenClearingHouse)
                    .instance()

                oldImpAddr = await getImplementation(clearingHouseContract.address)
                initMarginRatio = (await clearingHouseContract.initMarginRatio()).toString()
                insuranceFund = await clearingHouseContract.insuranceFund()
                BTC = context.systemMetadataDao.getContractMetadata(context.layer, AmmInstanceName.BTCUSDC).address
                openInterestNotional = (await clearingHouseContract.openInterestNotionalMap(BTC)).toString()

                arbitrageur = context.externalContract.arbitrageur!
                arbitrageurBTCPositionSize = (
                    await clearingHouseContract.getUnadjustedPosition(BTC, arbitrageur)
                ).size.toString()
            },
            async (): Promise<void> => {
                console.log("prepare upgrading...")
                // deploy clearing house implementation
                const clearingHouseContract = await context.factory.create<ClearingHouse>(
                    ContractFullyQualifiedName.FlattenClearingHouse,
                )
                newImplContractAddr = await clearingHouseContract.prepareUpgradeContractLegacy()
            },
            async (): Promise<void> => {
                console.info("do upgrade")
                // create an impersonated signer
                const govAddr = context.externalContract.foundationGovernance
                await hre.network.provider.request({
                    method: "hardhat_impersonateAccount",
                    params: [govAddr],
                })
                const govSigner = ethers.provider.getSigner(govAddr)

                // prepare information for upgrading
                const contractName = ContractFullyQualifiedName.FlattenClearingHouse
                const proxyAddr = context.factory.create<ClearingHouse>(contractName).address!

                const proxyAdmin = await upgrades.admin.getInstance()
                await proxyAdmin.connect(govSigner).upgrade(proxyAddr, newImplContractAddr)

                console.log(
                    `upgrade: contractFullyQualifiedName=${contractName}, proxy=${proxyAddr}, implementation=${newImplContractAddr}`,
                )
            },
            async (): Promise<void> => {
                const clearingHouseContract = await context.factory
                    .create<ClearingHouse>(ContractFullyQualifiedName.FlattenClearingHouse)
                    .instance()

                // for comparing with the new implementation address
                console.log("old implementation address: ", oldImpAddr)
                console.log("new implementation address: ", await getImplementation(clearingHouseContract.address))

                const newInsuranceFund = await clearingHouseContract.insuranceFund()
                console.log("insuranceFund address (shouldn't be zero address): ", newInsuranceFund)
                expect(newInsuranceFund).to.eq(insuranceFund)
                console.log("insuranceFund verified!")

                expect((await clearingHouseContract.initMarginRatio()).toString()).to.eq(initMarginRatio)
                console.log("initMarginRatio verified!")
                expect((await clearingHouseContract.openInterestNotionalMap(BTC)).toString()).to.eq(
                    openInterestNotional,
                )
                console.log("openInterestNotional verified!")
                expect((await clearingHouseContract.getUnadjustedPosition(BTC, arbitrageur)).size.toString()).to.eq(
                    arbitrageurBTCPositionSize,
                )
                console.log("arbitrageurBTCPositionSize verified!")
            },
        ]
    },
}

export default migration