/**
 * @synor/hardhat-plugin
 *
 * Hardhat plugin for Synor blockchain development.
 * Provides seamless integration between Hardhat and Synor network.
 */

import { extendConfig, extendEnvironment, task } from "hardhat/config";
import { HardhatConfig, HardhatUserConfig, HardhatRuntimeEnvironment } from "hardhat/types";
import { SynorProvider } from "./provider";
import { SynorDeployer } from "./deployer";
import { SynorNetwork } from "./network";
import "./type-extensions";

// Default Synor network configurations
const SYNOR_NETWORKS = {
  mainnet: {
    url: "https://rpc.synor.cc",
    chainId: 1337,
    accounts: [],
  },
  testnet: {
    url: "https://testnet-rpc.synor.cc",
    chainId: 1338,
    accounts: [],
  },
  devnet: {
    url: "http://localhost:8545",
    chainId: 1339,
    accounts: [],
  },
};

// Extend Hardhat config with Synor options
extendConfig((config: HardhatConfig, userConfig: Readonly<HardhatUserConfig>) => {
  // Add Synor-specific configuration
  config.synor = {
    defaultNetwork: userConfig.synor?.defaultNetwork ?? "devnet",
    gasPrice: userConfig.synor?.gasPrice ?? "auto",
    gasLimit: userConfig.synor?.gasLimit ?? 3000000,
    confirmations: userConfig.synor?.confirmations ?? 1,
    timeout: userConfig.synor?.timeout ?? 60000,
    verifyContracts: userConfig.synor?.verifyContracts ?? true,
    quantumSafe: userConfig.synor?.quantumSafe ?? true,
  };

  // Add Synor networks if not already defined
  for (const [name, network] of Object.entries(SYNOR_NETWORKS)) {
    const networkName = `synor-${name}`;
    if (!config.networks[networkName]) {
      config.networks[networkName] = {
        ...network,
        ...(userConfig.networks?.[networkName] ?? {}),
      };
    }
  }
});

// Extend Hardhat runtime environment
extendEnvironment((hre: HardhatRuntimeEnvironment) => {
  // Add Synor provider
  hre.synor = {
    provider: new SynorProvider(hre),
    deployer: new SynorDeployer(hre),
    network: new SynorNetwork(hre),
    getBalance: async (address: string) => {
      return hre.synor.provider.getBalance(address);
    },
    getBlockNumber: async () => {
      return hre.synor.provider.getBlockNumber();
    },
    sendTransaction: async (tx: any) => {
      return hre.synor.provider.sendTransaction(tx);
    },
  };
});

// Task: Deploy contract to Synor
task("synor:deploy", "Deploy a contract to Synor network")
  .addParam("contract", "Contract name to deploy")
  .addOptionalVariadicPositionalParam("args", "Constructor arguments")
  .setAction(async (taskArgs, hre) => {
    const { contract, args = [] } = taskArgs;

    console.log(`Deploying ${contract} to Synor ${hre.network.name}...`);

    const deployed = await hre.synor.deployer.deploy(contract, args);

    console.log(`Contract deployed at: ${deployed.address}`);
    console.log(`Transaction hash: ${deployed.deployTransaction.hash}`);

    if (hre.config.synor.verifyContracts) {
      console.log("Verifying contract...");
      await hre.synor.network.verifyContract(deployed.address, args);
      console.log("Contract verified!");
    }

    return deployed;
  });

// Task: Verify contract on Synor explorer
task("synor:verify", "Verify a contract on Synor explorer")
  .addParam("address", "Contract address")
  .addParam("contract", "Contract name")
  .addOptionalVariadicPositionalParam("args", "Constructor arguments")
  .setAction(async (taskArgs, hre) => {
    const { address, contract, args = [] } = taskArgs;

    console.log(`Verifying ${contract} at ${address}...`);
    await hre.synor.network.verifyContract(address, args, contract);
    console.log("Contract verified!");
  });

// Task: Get network info
task("synor:info", "Get Synor network information")
  .setAction(async (_, hre) => {
    const info = await hre.synor.network.getNetworkInfo();

    console.log("\nSynor Network Information:");
    console.log("==========================");
    console.log(`Network: ${info.name}`);
    console.log(`Chain ID: ${info.chainId}`);
    console.log(`Block Height: ${info.blockHeight}`);
    console.log(`Latest Block Hash: ${info.latestBlockHash}`);
    console.log(`Gas Price: ${info.gasPrice} gwei`);
    console.log(`Protocol Version: ${info.protocolVersion}`);
    console.log(`Consensus: ${info.consensus}`);
    console.log(`Quantum Safe: ${info.quantumSafe}`);
  });

// Task: Fund account from faucet (testnet/devnet only)
task("synor:faucet", "Request tokens from Synor faucet")
  .addParam("address", "Address to fund")
  .addOptionalParam("amount", "Amount to request (in SYN)", "10")
  .setAction(async (taskArgs, hre) => {
    const { address, amount } = taskArgs;

    if (hre.network.name.includes("mainnet")) {
      throw new Error("Faucet not available on mainnet");
    }

    console.log(`Requesting ${amount} SYN for ${address}...`);
    const txHash = await hre.synor.network.requestFaucet(address, amount);
    console.log(`Faucet transaction: ${txHash}`);
  });

// Task: Compile contracts for Synor (WASM target)
task("synor:compile", "Compile contracts for Synor WASM runtime")
  .setAction(async (_, hre) => {
    console.log("Compiling contracts for Synor...");

    // First run standard compilation
    await hre.run("compile");

    // Then generate WASM artifacts
    await hre.synor.deployer.compileToWasm();

    console.log("Compilation complete!");
  });

// Task: Run tests on Synor fork
task("synor:test", "Run tests against Synor network fork")
  .addOptionalParam("fork", "Block number to fork from", "latest")
  .setAction(async (taskArgs, hre) => {
    const { fork } = taskArgs;

    console.log(`Forking Synor at block ${fork}...`);

    // Set up fork
    await hre.synor.network.fork(fork);

    // Run tests
    await hre.run("test");
  });

// Task: Generate TypeScript types for contracts
task("synor:typechain", "Generate TypeScript types for Synor contracts")
  .setAction(async (_, hre) => {
    console.log("Generating TypeScript types...");
    await hre.synor.deployer.generateTypes();
    console.log("Types generated in typechain-types/");
  });

// Export types and utilities
export { SynorProvider } from "./provider";
export { SynorDeployer } from "./deployer";
export { SynorNetwork } from "./network";
export * from "./types";
