/**
 * Synor Network Utilities
 *
 * Network management, verification, and faucet utilities.
 */

import { HardhatRuntimeEnvironment } from "hardhat/types";
import { NetworkInfo } from "./type-extensions";
import chalk from "chalk";

/**
 * Network utilities for Synor blockchain
 */
export class SynorNetwork {
  private hre: HardhatRuntimeEnvironment;

  constructor(hre: HardhatRuntimeEnvironment) {
    this.hre = hre;
  }

  /**
   * Gets network information
   */
  async getNetworkInfo(): Promise<NetworkInfo> {
    const provider = this.hre.synor.provider;

    const [blockNumber, gasPrice, quantumStatus, shardInfo] = await Promise.all([
      provider.getBlockNumber(),
      provider.getGasPrice(),
      provider.getQuantumStatus().catch(() => ({ enabled: false, algorithm: "ed25519" })),
      provider.getShardInfo().catch(() => ({ shardId: 0, totalShards: 1 })),
    ]);

    const block = await provider.getBlock(blockNumber);

    // Determine network name from chain ID
    const networkConfig = this.hre.network.config;
    const chainId = "chainId" in networkConfig ? networkConfig.chainId : 1337;

    let networkName: string;
    switch (chainId) {
      case 1337:
        networkName = "Synor Mainnet";
        break;
      case 1338:
        networkName = "Synor Testnet";
        break;
      case 1339:
        networkName = "Synor Devnet";
        break;
      default:
        networkName = `Synor Custom (${chainId})`;
    }

    return {
      name: networkName,
      chainId,
      blockHeight: blockNumber,
      latestBlockHash: block?.hash || "0x",
      gasPrice: (gasPrice / BigInt(1e9)).toString(),
      protocolVersion: "1.0.0",
      consensus: "GHOSTDAG/DAGKnight",
      quantumSafe: quantumStatus.enabled,
    };
  }

  /**
   * Verifies a contract on Synor explorer
   */
  async verifyContract(
    address: string,
    constructorArgs: any[] = [],
    contractName?: string
  ): Promise<void> {
    const explorerUrl = this.getExplorerApiUrl();

    console.log(chalk.blue(`\nVerifying contract at ${address}...`));

    // Get contract artifact
    let artifact: any;
    if (contractName) {
      artifact = await this.hre.artifacts.readArtifact(contractName);
    } else {
      // Try to find artifact by deployed bytecode
      const code = await this.hre.synor.provider.getCode(address);
      const allArtifacts = await this.hre.artifacts.getAllFullyQualifiedNames();

      for (const name of allArtifacts) {
        const a = await this.hre.artifacts.readArtifact(name);
        if (a.deployedBytecode && code.includes(a.deployedBytecode.slice(2, 100))) {
          artifact = a;
          break;
        }
      }
    }

    if (!artifact) {
      throw new Error("Could not find matching artifact for contract");
    }

    // Prepare verification request
    const verificationData = {
      address,
      contractName: artifact.contractName,
      sourceCode: await this.getContractSource(artifact),
      compilerVersion: artifact.metadata?.compiler?.version || "0.8.20",
      optimizationUsed: true,
      runs: 200,
      constructorArguments: this.encodeConstructorArgs(artifact, constructorArgs),
    };

    // Submit verification
    const response = await fetch(`${explorerUrl}/api/v1/contract/verify`, {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
      },
      body: JSON.stringify(verificationData),
    });

    if (!response.ok) {
      const error = await response.text();
      throw new Error(`Verification failed: ${error}`);
    }

    const result = await response.json();

    if (result.status === "success") {
      console.log(chalk.green(`✓ Contract verified successfully`));
      console.log(chalk.gray(`  View at: ${this.getExplorerUrl()}/address/${address}#code`));
    } else {
      throw new Error(`Verification failed: ${result.message}`);
    }
  }

  /**
   * Gets contract source code
   */
  private async getContractSource(artifact: any): Promise<string> {
    // In production, this would flatten and return source code
    // For now, return a placeholder
    return `// Source code for ${artifact.contractName}\n// Please provide full source for verification`;
  }

  /**
   * Encodes constructor arguments
   */
  private encodeConstructorArgs(artifact: any, args: any[]): string {
    if (args.length === 0) return "";

    const abi = artifact.abi;
    const constructor = abi.find((item: any) => item.type === "constructor");

    if (!constructor) return "";

    // Encode using ethers
    const { AbiCoder } = require("ethers");
    const coder = new AbiCoder();
    const types = constructor.inputs.map((input: any) => input.type);

    return coder.encode(types, args).slice(2); // Remove 0x prefix
  }

  /**
   * Gets explorer URL for current network
   */
  private getExplorerUrl(): string {
    const networkConfig = this.hre.network.config;
    const chainId = "chainId" in networkConfig ? networkConfig.chainId : 1337;

    switch (chainId) {
      case 1337:
        return "https://explorer.synor.cc";
      case 1338:
        return "https://testnet-explorer.synor.cc";
      case 1339:
        return "http://localhost:4000";
      default:
        return "https://explorer.synor.cc";
    }
  }

  /**
   * Gets explorer API URL
   */
  private getExplorerApiUrl(): string {
    return this.getExplorerUrl();
  }

  /**
   * Requests tokens from faucet
   */
  async requestFaucet(address: string, amount: string = "10"): Promise<string> {
    const networkConfig = this.hre.network.config;
    const chainId = "chainId" in networkConfig ? networkConfig.chainId : 1337;

    if (chainId === 1337) {
      throw new Error("Faucet not available on mainnet");
    }

    const faucetUrl = chainId === 1338
      ? "https://faucet.synor.cc"
      : "http://localhost:3001";

    console.log(chalk.blue(`\nRequesting ${amount} SYN from faucet...`));

    const response = await fetch(`${faucetUrl}/api/faucet`, {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        address,
        amount,
      }),
    });

    if (!response.ok) {
      const error = await response.text();
      throw new Error(`Faucet request failed: ${error}`);
    }

    const result = await response.json();

    console.log(chalk.green(`✓ Received ${amount} SYN`));
    console.log(chalk.gray(`  Transaction: ${result.txHash}`));

    return result.txHash;
  }

  /**
   * Forks the network at a specific block
   */
  async fork(blockNumber: string | number = "latest"): Promise<void> {
    const block = blockNumber === "latest"
      ? await this.hre.synor.provider.getBlockNumber()
      : typeof blockNumber === "string"
        ? parseInt(blockNumber)
        : blockNumber;

    console.log(chalk.blue(`\nForking network at block ${block}...`));

    // Configure hardhat network for forking
    await this.hre.network.provider.request({
      method: "hardhat_reset",
      params: [
        {
          forking: {
            jsonRpcUrl: this.getNetworkRpcUrl(),
            blockNumber: block,
          },
        },
      ],
    });

    console.log(chalk.green(`✓ Network forked at block ${block}`));
  }

  /**
   * Gets RPC URL for current network
   */
  private getNetworkRpcUrl(): string {
    const networkConfig = this.hre.network.config;
    if ("url" in networkConfig) {
      return networkConfig.url;
    }
    return "http://localhost:8545";
  }

  /**
   * Impersonates an account (for testing)
   */
  async impersonate(address: string): Promise<void> {
    await this.hre.network.provider.request({
      method: "hardhat_impersonateAccount",
      params: [address],
    });

    console.log(chalk.yellow(`Impersonating account: ${address}`));
  }

  /**
   * Stops impersonating an account
   */
  async stopImpersonating(address: string): Promise<void> {
    await this.hre.network.provider.request({
      method: "hardhat_stopImpersonatingAccount",
      params: [address],
    });
  }

  /**
   * Sets account balance (for testing)
   */
  async setBalance(address: string, balance: bigint): Promise<void> {
    await this.hre.network.provider.request({
      method: "hardhat_setBalance",
      params: [address, `0x${balance.toString(16)}`],
    });

    console.log(chalk.yellow(`Set balance for ${address}: ${balance} wei`));
  }

  /**
   * Mines a block (for testing)
   */
  async mineBlock(timestamp?: number): Promise<void> {
    if (timestamp) {
      await this.hre.network.provider.request({
        method: "evm_setNextBlockTimestamp",
        params: [timestamp],
      });
    }

    await this.hre.network.provider.request({
      method: "evm_mine",
      params: [],
    });
  }

  /**
   * Advances time (for testing)
   */
  async increaseTime(seconds: number): Promise<void> {
    await this.hre.network.provider.request({
      method: "evm_increaseTime",
      params: [seconds],
    });

    await this.mineBlock();

    console.log(chalk.yellow(`Advanced time by ${seconds} seconds`));
  }

  /**
   * Takes a snapshot (for testing)
   */
  async snapshot(): Promise<string> {
    const snapshotId = await this.hre.network.provider.request({
      method: "evm_snapshot",
      params: [],
    }) as string;

    console.log(chalk.gray(`Snapshot taken: ${snapshotId}`));
    return snapshotId;
  }

  /**
   * Reverts to a snapshot (for testing)
   */
  async revert(snapshotId: string): Promise<void> {
    await this.hre.network.provider.request({
      method: "evm_revert",
      params: [snapshotId],
    });

    console.log(chalk.gray(`Reverted to snapshot: ${snapshotId}`));
  }
}
