const {app, dialog, ipcMain} = require("electron");
const path = require("path");
const fs = require("fs");
const fetch = require("node-fetch");

class PaperclipRPC {
  constructor() {
    this.isConnected = false;
    this.rpcUrl = "http://localhost:26657";
    this.logEvents = false;
    
    // create the user data dir (needed for MacOS)
    if (!fs.existsSync(app.getPath("userData"))) {
      fs.mkdirSync(app.getPath("userData"));
    }

    if (this.logEvents) {
      this.logStream = fs.createWriteStream(path.join(app.getPath("userData"), "paperclip-rpc.log"), {flags: "a"});
    }
  }

  _writeLog(text) {
    if (this.logEvents) {
      this.logStream.write(`${new Date().toISOString()}: ${text}\n`);
    }
    // Suppress console logging in production
    if (process.env.NODE_ENV === 'development') {
      console.log("PaperclipRPC:", text);
    }
  }

  async _makeRPCCall(method, params = {}) {
    try {
      const url = `${this.rpcUrl}/${method}`;
      const queryParams = new URLSearchParams();
      
      Object.keys(params).forEach(key => {
        queryParams.append(key, params[key]);
      });
      
      const fullUrl = queryParams.toString() ? `${url}?${queryParams}` : url;
      
      this._writeLog(`Making RPC call to: ${fullUrl}`);
      
      const response = await fetch(fullUrl);
      if (!response.ok) {
        throw new Error(`HTTP error! status: ${response.status}`);
      }
      
      const data = await response.json();
      return data;
    } catch (error) {
      this._writeLog(`RPC call failed: ${error.message}`);
      throw error;
    }
  }

  async getStatus() {
    try {
      const result = await this._makeRPCCall("status");
      this.isConnected = true;
      return result;
    } catch (error) {
      this.isConnected = false;
      throw error;
    }
  }

  async getBalance(address) {
    try {
      const result = await this._makeRPCCall("abci_query", { 
        path: `"balance:${address}"` 
      });
      
      if (result.result && result.result.response && result.result.response.value) {
        // Decode base64 response
        const balance = Buffer.from(result.result.response.value, 'base64').toString();
        return parseInt(balance) || 0;
      }
      return 0;
    } catch (error) {
      this._writeLog(`Failed to get balance for ${address}: ${error.message}`);
      return 0;
    }
  }

  async getNonce(address) {
    try {
      const result = await this._makeRPCCall("abci_query", { 
        path: `"nonce:${address}"` 
      });
      
      if (result.result && result.result.response && result.result.response.value) {
        const nonce = Buffer.from(result.result.response.value, 'base64').toString();
        return parseInt(nonce) || 0;
      }
      return 0;
    } catch (error) {
      this._writeLog(`Failed to get nonce for ${address}: ${error.message}`);
      return 0;
    }
  }

  async broadcastTransaction(txHex) {
    try {
      const result = await this._makeRPCCall("broadcast_tx_commit", { 
        tx: txHex 
      });
      
      if (result.result && result.result.check_tx && result.result.check_tx.code === 0) {
        this._writeLog(`Transaction broadcast successful: ${result.result.hash}`);
        return {
          success: true,
          hash: result.result.hash,
          height: result.result.height
        };
      } else {
        const error = result.result?.check_tx?.log || "Transaction failed";
        throw new Error(error);
      }
    } catch (error) {
      this._writeLog(`Failed to broadcast transaction: ${error.message}`);
      throw error;
    }
  }

  async getTransaction(hash) {
    try {
      const result = await this._makeRPCCall("tx", { 
        hash: hash,
        prove: "false"
      });
      return result;
    } catch (error) {
      this._writeLog(`Failed to get transaction ${hash}: ${error.message}`);
      throw error;
    }
  }

  async getBlock(height = null) {
    try {
      const params = height ? { height: height.toString() } : {};
      const result = await this._makeRPCCall("block", params);
      return result;
    } catch (error) {
      this._writeLog(`Failed to get block: ${error.message}`);
      throw error;
    }
  }

  async getContractInfo(contractAddress) {
    try {
      const result = await this._makeRPCCall("abci_query", { 
        path: `"contract:${contractAddress}"` 
      });
      
      if (result.result && result.result.response && result.result.response.value) {
        const contractInfo = Buffer.from(result.result.response.value, 'base64').toString();
        return JSON.parse(contractInfo);
      }
      return null;
    } catch (error) {
      this._writeLog(`Failed to get contract info for ${contractAddress}: ${error.message}`);
      return null;
    }
  }

  async getMultisigInfo(multisigAddress) {
    try {
      const result = await this._makeRPCCall("abci_query", { 
        path: `"multisig:${multisigAddress}"` 
      });
      
      if (result.result && result.result.response && result.result.response.value) {
        const multisigInfo = Buffer.from(result.result.response.value, 'base64').toString();
        return JSON.parse(multisigInfo);
      }
      return null;
    } catch (error) {
      this._writeLog(`Failed to get multisig info for ${multisigAddress}: ${error.message}`);
      return null;
    }
  }

  async getFeePool() {
    try {
      const result = await this._makeRPCCall("abci_query", { 
        path: `"fee_pool"` 
      });
      
      if (result.result && result.result.response && result.result.response.value) {
        const feePool = Buffer.from(result.result.response.value, 'base64').toString();
        return parseInt(feePool) || 0;
      }
      return 0;
    } catch (error) {
      this._writeLog(`Failed to get fee pool: ${error.message}`);
      return 0;
    }
  }

  // Staking Methods

  async getValidators() {
    try {
      const result = await this._makeRPCCall("abci_query", { 
        path: `"validators"` 
      });
      
      if (result.result && result.result.response && result.result.response.value) {
        const validators = Buffer.from(result.result.response.value, 'base64').toString();
        return JSON.parse(validators);
      }
      return [];
    } catch (error) {
      this._writeLog(`Failed to get validators: ${error.message}`);
      return [];
    }
  }

  async getStakingInfo(address) {
    try {
      const result = await this._makeRPCCall("abci_query", { 
        path: `"staker:${address}"` 
      });
      
      if (result.result && result.result.response && result.result.response.value) {
        const stakingInfo = Buffer.from(result.result.response.value, 'base64').toString();
        return JSON.parse(stakingInfo);
      }
      return null;
    } catch (error) {
      this._writeLog(`Failed to get staking info for ${address}: ${error.message}`);
      return null;
    }
  }

  async getValidatorInfo(validatorAddress) {
    try {
      const result = await this._makeRPCCall("abci_query", { 
        path: `"validator:${validatorAddress}"` 
      });
      
      if (result.result && result.result.response && result.result.response.value) {
        const validatorInfo = Buffer.from(result.result.response.value, 'base64').toString();
        return JSON.parse(validatorInfo);
      }
      return null;
    } catch (error) {
      this._writeLog(`Failed to get validator info for ${validatorAddress}: ${error.message}`);
      return null;
    }
  }

  async getStakingRewards(address) {
    try {
      const result = await this._makeRPCCall("abci_query", { 
        path: `"rewards:${address}"` 
      });
      
      if (result.result && result.result.response && result.result.response.value) {
        const rewards = Buffer.from(result.result.response.value, 'base64').toString();
        return parseInt(rewards) || 0;
      }
      return 0;
    } catch (error) {
      this._writeLog(`Failed to get staking rewards for ${address}: ${error.message}`);
      return 0;
    }
  }

  async getTotalStaked() {
    try {
      const result = await this._makeRPCCall("abci_query", { 
        path: `"total_staked"` 
      });
      
      if (result.result && result.result.response && result.result.response.value) {
        const totalStaked = Buffer.from(result.result.response.value, 'base64').toString();
        return parseInt(totalStaked) || 0;
      }
      return 0;
    } catch (error) {
      this._writeLog(`Failed to get total staked: ${error.message}`);
      return 0;
    }
  }

  setRPCUrl(url) {
    this.rpcUrl = url;
    this._writeLog(`RPC URL updated to: ${url}`);
  }

  startConnection() {
    this._writeLog("Starting PaperclipChain RPC connection");
    
    // Test connection
    this.getStatus()
      .then(() => {
        this._writeLog("Connected to PaperclipChain node");
      })
      .catch((error) => {
        this._writeLog(`Connection failed: ${error.message}`);
      });
  }

  stopConnection() {
    this._writeLog("Stopping PaperclipChain RPC connection");
    this.isConnected = false;
    
    if (this.logStream) {
      this.logStream.end();
    }
  }
}

const PaperclipNode = new PaperclipRPC();

// IPC handlers for renderer process
ipcMain.handle("paperclip-get-status", async () => {
  return await PaperclipNode.getStatus();
});

ipcMain.handle("paperclip-get-balance", async (event, address) => {
  return await PaperclipNode.getBalance(address);
});

ipcMain.handle("paperclip-get-nonce", async (event, address) => {
  return await PaperclipNode.getNonce(address);
});

ipcMain.handle("paperclip-broadcast-tx", async (event, txHex) => {
  return await PaperclipNode.broadcastTransaction(txHex);
});

ipcMain.handle("paperclip-get-transaction", async (event, hash) => {
  return await PaperclipNode.getTransaction(hash);
});

ipcMain.handle("paperclip-get-block", async (event, height) => {
  return await PaperclipNode.getBlock(height);
});

// Staking IPC handlers
ipcMain.handle("paperclip-get-validators", async () => {
  return await PaperclipNode.getValidators();
});

ipcMain.handle("paperclip-get-staking-info", async (event, address) => {
  return await PaperclipNode.getStakingInfo(address);
});

ipcMain.handle("paperclip-get-validator-info", async (event, validatorAddress) => {
  return await PaperclipNode.getValidatorInfo(validatorAddress);
});

ipcMain.handle("paperclip-get-staking-rewards", async (event, address) => {
  return await PaperclipNode.getStakingRewards(address);
});

ipcMain.handle("paperclip-get-total-staked", async () => {
  return await PaperclipNode.getTotalStaked();
});

module.exports = PaperclipNode;