#!/usr/bin/env node
import { select, input, confirm, password } from "@inquirer/prompts";
import { JsonRpcProvider, Wallet, parseEther, isHexString, isAddress } from "ethers";
import { getAxieIdsFromAccount, delegateAxie, batchDelegateAxies, batchRevokeDelegations, revokeDelegation } from "./lib/axie";
import { refreshToken } from "./lib/marketplace/access-token";
import { batchTransferAxies, transferAxie } from "./lib/transfers";
import {
  askToContinue,
  ensureMarketplaceToken,
  getAccountInfo,
  getAxieId,
} from "./lib/utils";
import {
  approveMarketplaceContract,
  approveWETH,
} from "./lib/marketplace/approve";
import buyMarketplaceOrder from "./lib/marketplace/settle-order";
import cancelMarketplaceOrder from "./lib/marketplace/cancel-order";
import createMarketplaceOrder from "./lib/marketplace/create-order";
import { checkBlessings, isActivated } from "./lib/atia";
import { signDelegation } from "./lib/atia/delegation";
import { delegateAtiaBlessing, getAtiaBlessingDelegations, revokeAtiaBlessingDelegation } from "./lib/atia/graphql";
import "dotenv/config";

async function main() {
  // Check for PRIVATE_KEY before the main loop
  let privateKey = process.env.PRIVATE_KEY;
  if (!privateKey) {
    privateKey = await password({
      message: "🔐 Enter your private key:",
      validate: (value) => {
        if (!value) return false;
        // Private keys are 32 bytes (64 characters) + optional "0x" prefix
        return isHexString(value, 32) || isHexString(`0x${value}`, 32);
      },
    });
    // Ensure "0x" prefix
    privateKey = privateKey.startsWith("0x") ? privateKey : `0x${privateKey}`;
  }

  // Initialize provider with a reliable RPC endpoint
  const provider = new JsonRpcProvider("https://api.roninchain.com/rpc");

  // Initialize wallet with the previously obtained private key
  const wallet = new Wallet(privateKey, provider);
  const address = await wallet.getAddress();

  while (true) {
    try {
      const action = await select({
        message: "What would you like to do?",
        choices: [
          { name: "Get account info", value: "account" },
          { name: "Refresh access token", value: "refresh-token" },
          { name: "Approve WETH", value: "approve-weth" },
          { name: "Approve marketplace", value: "approve-marketplace" },
          { name: "Buy axie", value: "buy" },
          { name: "Delist axie", value: "delist" },
          { name: "Delist multiple axies", value: "delist-all" },
          { name: "List axie", value: "list" },
          { name: "List multiple axies", value: "list-all" },
          { name: "Transfer axie", value: "transfer" },
          { name: "Transfer multiple axies", value: "transfer-all" },
          { name: "Delegate axie", value: "delegate-axie" },
          { name: "Revoke axie delegation", value: "revoke-delegation-axie" },
          { name: "Delegate multiple axies", value: "batch-delegate" },
          { name: "Revoke multiple delegations", value: "batch-revoke" },
          { name: "Check Atia blessing status", value: "check-blessing" },
          { name: "Pray for Atia blessing", value: "pray-blessing" },
          { name: "Create Atia Blessing delegation", value: "delegate" },
          { name: "List Atia Blessing delegations", value: "list-delegations" },
          { name: "Revoke Atia Blessing delegation", value: "revoke-delegation" },
        ],
      });

      switch (action) {
        case "account": {
          const info = await getAccountInfo(address, provider);
          console.log(`📬 Address: ${info.address}`);
          console.log("💰 RON Balance:", info.ronBalance);
          console.log("💰 WETH Balance:", info.wethBalance);
          console.log("💰 USDC Balance:", info.usdcBalance);
          console.log(
            "🛒 Marketplace WETH allowance:",
            info.allowance !== 0n ? "✅ Granted" : "❌ Not granted",
          );
          console.log(
            "🔐 Marketplace approval for Axies:",
            info.isApprovedForAll ? "✅ Approved" : "❌ Not approved",
          );
          console.log(`🐾 Number of Axies: ${info.axieIds.length}`);
          if (info.axieIds.length > 0) {
            console.log(`🆔 Axie IDs: ${info.axieIds.join(", ")}`);
          }
          break;
        }
        case "refresh-token": {
          let refreshTokenValue = process.env.MARKETPLACE_REFRESH_TOKEN;

          if (!refreshTokenValue) {
            refreshTokenValue = await input({
              message: "Enter refresh token",
              validate: (value) => value.length > 0,
            });
          }

          const result = await refreshToken(refreshTokenValue);
          console.log("New access token:", result.newAccessToken);
          console.log("New refresh token:", result.newRefreshToken);

          process.env.MARKETPLACE_ACCESS_TOKEN = result.newAccessToken;
          process.env.MARKETPLACE_REFRESH_TOKEN = result.newRefreshToken;

          break;
        }
        case "approve-weth": {
          await approveWETH(wallet);
          break;
        }
        case "approve-marketplace": {
          await approveMarketplaceContract(wallet);
          break;
        }
        case "buy": {
          const token = await ensureMarketplaceToken();
          const axieId = await getAxieId();
          if (!axieId) break;
          await approveWETH(wallet);
          const receipt = await buyMarketplaceOrder(
            axieId,
            wallet,
            token
          );
          if (receipt) {
            console.log("🚀 Transaction successful! Hash:", receipt.hash);
            console.log(
              `🔗 View transaction: https://app.roninchain.com/tx/${receipt.hash}`,
            );
          }
          break;
        }
        case "list": {
          const axieId = await getAxieId();
          if (!axieId) break;

          const token = await ensureMarketplaceToken();
          const basePrice = await input({
            message: "Enter base price in ETH",
            validate: (value) => parseEther(value) > 0n,
          });

          await approveMarketplaceContract(wallet);

          const currentBlock = await provider.getBlock("latest");
          const startedAt = currentBlock?.timestamp ?? Math.floor(Date.now() / 1000);
          const expiredAt = startedAt + 15634800; // ~6 months

          const orderData = {
            address,
            axieId: axieId.toString(),
            basePrice: parseEther(basePrice).toString(),
            endedPrice: "0",
            startedAt,
            endedAt: 0,
            expiredAt,
          };

          const result = await createMarketplaceOrder(
            orderData,
            token,
            wallet
          );
          if (result === null || result.errors || !result.data) {
            console.error(
              "❌ Error:",
              result?.errors?.[0]?.message || "Unknown error",
            );
            break;
          }

          console.log(
            `✅ Listed Axie ${axieId}! Current price in USD: ${result.data.createOrder.currentPriceUsd}`,
          );
          break;
        }
        case "list-all": {
          const token = await ensureMarketplaceToken();
          const basePrice = await input({
            message: "Enter base price in ETH (for all Axies)",
            validate: (value) => parseEther(value) > 0n,
          });

          await approveMarketplaceContract(wallet);

          let axieIds = await getAxieIdsFromAccount(address, provider);

          if (axieIds.length > 100) {
            console.log(
              "⚠️ Warning: Can only list up to 100 Axies at once, only listing the first 100",
            );
            axieIds = axieIds.slice(0, 100);
          }

          const currentBlock = await provider.getBlock("latest");
          const startedAt = currentBlock?.timestamp ?? Math.floor(Date.now() / 1000);
          const expiredAt = startedAt + 15634800; // ~6 months

          for (const axieId of axieIds) {
            const orderData = {
              address,
              axieId: axieId.toString(),
              basePrice: parseEther(basePrice).toString(),
              endedPrice: "0",
              startedAt,
              endedAt: 0,
              expiredAt,
            };

            const result = await createMarketplaceOrder(
              orderData,            token,
            wallet,
            );

            if (result === null || result.errors || !result.data) {
              console.error(
                `❌ Error listing Axie ${axieId}:`,
                result?.errors?.[0]?.message || "Unknown error",
              );
              continue;
            }

            console.log(
              `✅ Listed Axie ${axieId}! Current price in USD: ${result.data.createOrder.currentPriceUsd}`,
            );
          }
          break;
        }
        case "delist": {
          const axieId = await getAxieId();
          if (!axieId) break;

          const receipt = await cancelMarketplaceOrder(
            axieId,
            wallet
          );
          if (receipt) {
            console.log("✅ Axie delisted! Transaction hash:", receipt.hash);
            console.log(
              `🔗 View transaction: https://app.roninchain.com/tx/${receipt.hash}`,
            );
          }
          break;
        }
        case "delist-all": {
          const axieIdsInput = await input({
            message: "Enter comma-separated Axie IDs to delist (e.g. 123, 456, 789)",
            validate: (value) => {
              const ids = value.split(",").map(id => id.trim());
              return ids.length > 0 && ids.every(id => !Number.isNaN(Number(id)));
            },
          });

          let axieIds = axieIdsInput.split(",").map(id => id.trim());

          if (axieIds.length > 100) {
            console.log(
              "⚠️ Warning: Can only delist up to 100 Axies at once, only delisting the first 100",
            );
            axieIds = axieIds.slice(0, 100);
          }

          const receipt = await batchTransferAxies(
            wallet,
            await wallet.getAddress(),
            axieIds,
          );
          if (receipt) {
            console.log("✅ Axies delisted! Transaction hash:", receipt.hash);
            console.log(
              `🔗 View transaction: https://app.roninchain.com/tx/${receipt.hash}`,
            );
          } else {
            console.log("❌ Error delisting Axies");
          }
          break;
        }

        case "transfer": {
          const axieId = await getAxieId();
          if (!axieId) break;

          const address = await input({
            message: "Enter recipient address",
            validate: (value) => value.length > 0,
          });
          const receipt = await transferAxie(wallet, address, axieId);
          if (receipt) {
            console.log("✅ Axie transferred! Transaction hash:", receipt.hash);
          }
          break;
        }
        case "transfer-all": {
          const axieIdsInput = await input({
            message: "Enter comma-separated Axie IDs (e.g. 123, 456, 789)",
            validate: (value) => {
              const ids = value.split(",").map(id => id.trim());
              return ids.length > 0 && ids.every(id => !Number.isNaN(Number(id)));
            },
          });

          let axieIds = axieIdsInput.split(",").map(id => id.trim());

          if (axieIds.length > 100) {
            console.log(
              "⚠️ Warning: Can only transfer up to 100 Axies at once, only transferring the first 100",
            );
            axieIds = axieIds.slice(0, 100);
          }

          const address = await input({
            message: "Enter recipient address",
            validate: (value) => value.length > 0,
          });

          const receipt = await batchTransferAxies(wallet, address, axieIds);
          if (receipt) {
            console.log(
              "✅ Axies transferred! Transaction hash:",
              receipt.hash,
            );
          }
          break;
        }
        case "delegate-axie": {
          const axieId = await getAxieId();
          if (!axieId) break;

          const address = await input({
            message: "Enter delegatee address",
            validate: (value) => value.length > 0,
          });

          const receipt = await delegateAxie(wallet, axieId, address);
          if (receipt) {
            console.log("✅ Axie delegated! Transaction hash:", receipt.hash);
            console.log(
              `🔗 View transaction: https://app.roninchain.com/tx/${receipt.hash}`,
            );
          }
          break;
        }
        case "revoke-delegation-axie": {
          const axieId = await getAxieId();
          if (!axieId) break;

          console.log("🔄 Starting delegation revocation process (this requires multiple transactions)...");
          const receipt = await revokeDelegation(wallet, axieId);
          if (receipt) {
            console.log("✅ Axie delegation fully revoked! Final transaction hash:", receipt.hash);
            console.log(
              `🔗 View transaction: https://app.roninchain.com/tx/${receipt.hash}`,
            );
          }
          break;
        }
        case "batch-delegate": {
          const axieIdsInput = await input({
            message: "Enter comma-separated Axie IDs to delegate (e.g. 123, 456, 789)",
            validate: (value) => {
              const ids = value.split(",").map(id => id.trim());
              return ids.length > 0 && ids.every(id => !Number.isNaN(Number(id)));
            },
          });

          let axieIds = axieIdsInput.split(",").map(id => id.trim());

          if (axieIds.length === 0) {
            console.log("❌ No Axie IDs provided");
            break;
          }

          if (axieIds.length > 100) {
            console.log(
              "⚠️ Warning: Can only delegate up to 100 Axies at once, only delegating the first 100",
            );
            axieIds = axieIds.slice(0, 100);
          }

          const address = await input({
            message: "Enter delegatee address",
            validate: (value) => value.length > 0,
          });

          console.log("\n🔄 Starting batch delegation process...");

          try {
            const receipt = await batchDelegateAxies(wallet, axieIds, address);
            if (receipt) {
              console.log("\n✅ Batch operation completed! Final transaction hash:", receipt.hash);
              console.log(
                `🔗 View transaction: https://app.roninchain.com/tx/${receipt.hash}`,
              );
            }
          } catch (error: unknown) {
            console.log("\n❌ Some operations failed. Check the logs above for details.");
          }
          break;
        }

        case "batch-revoke": {
          const axieIdsInput = await input({
            message: "Enter comma-separated Axie IDs to revoke delegation (e.g. 123, 456, 789)",
            validate: (value) => {
              const ids = value.split(",").map(id => id.trim());
              return ids.length > 0 && ids.every(id => !Number.isNaN(Number(id)));
            },
          });

          let axieIds = axieIdsInput.split(",").map(id => id.trim());

          if (axieIds.length === 0) {
            console.log("❌ No Axie IDs provided");
            break;
          }

          if (axieIds.length > 100) {
            console.log(
              "⚠️ Warning: Can only revoke up to 100 Axies at once, only revoking the first 100",
            );
            axieIds = axieIds.slice(0, 100);
          }

          console.log("\n🔄 Starting batch revocation process...");
          console.log("This will:");
          console.log("1. Check and approve delegation contract if needed");
          console.log("2. Request detachment for each Axie");
          console.log("3. Perform the revocation\n");

          try {
            const receipt = await batchRevokeDelegations(wallet, axieIds);
            if (receipt) {
              console.log("\n✅ Batch operation completed! Final transaction hash:", receipt.hash);
              console.log(
                `🔗 View transaction: https://app.roninchain.com/tx/${receipt.hash}`,
              );
            }
          } catch (error: unknown) {
            console.log("\n❌ Some operations failed. Check the logs above for details.");
          }
          break;
        }

        case "check-blessing": {
          const address = await input({
            message: "Enter address to check blessing status (press Enter to use your address)",
            validate: () => true,
          });

          const targetAddress = address || wallet.address;
          const { status, streak } = await isActivated(targetAddress, provider);

          if (status) {
            console.log(`✅ Address ${targetAddress.slice(-4)} has already prayed today (Current streak: ${streak})`);
          } else {
            console.log(`❌ Address ${targetAddress.slice(-4)} has not prayed today (Current streak: ${streak})`);
          }
          break;
        }

        case "pray-blessing": {
          const token = await ensureMarketplaceToken();
          await checkBlessings(wallet, token);
          break;
        }

        case "delegate": {
          const token = await ensureMarketplaceToken();
          if (!token) {
            console.log("❌ No marketplace token available. Please refresh token first.");
            break;
          }

          const toAddress = await input({
            message: "Enter delegatee address:",
            validate: (value) => isAddress(value),
          });

          const percentage = await input({
            message: "Enter delegator slips percentage (default: 100):",
            validate: (value) => {
              if (value === "") return true;
              const num = Number.parseInt(value);
              return !Number.isNaN(num) && num > 0 && num <= 100;
            },
            default: "100"
          });

          const delegatedAt = Math.floor(Date.now() / 1000);

          console.log("\n🔄 Creating delegation signature...");
          const { message, signature } = await signDelegation(
            wallet,
            toAddress,
            delegatedAt,
            Number.parseInt(percentage)
          );

          console.log("✅ Signature created!");
          console.log("\n📝 Message:", message);
          console.log("✍️  Signature:", signature);

          console.log("\n🔄 Submitting delegation...");
          const success = await delegateAtiaBlessing(
            token,
            signature,
            wallet.address,
            toAddress,
            delegatedAt,
            Number.parseInt(percentage)
          );

          if (success) {
            console.log("✅ Delegation submitted successfully!");
          } else {
            console.log("❌ Failed to submit delegation");
          }
          break;
        }

        case "revoke-delegation": {
          const token = await ensureMarketplaceToken();
          if (!token) {
            console.log("❌ No marketplace token available. Please refresh token first.");
            break;
          }

          const toAddress = await input({
            message: "Enter delegatee address to revoke:",
            validate: (value) => isAddress(value),
          });

          console.log("\n🔄 Revoking delegation...");
          const success = await revokeAtiaBlessingDelegation(
            token,
            wallet.address, // userAddress
            toAddress,
            wallet.address  // fromAddress
          );

          if (success) {
            console.log("✅ Delegation revoked successfully!");
          } else {
            console.log("❌ Failed to revoke delegation");
          }
          break;
        }

        case "list-delegations": {
          const token = await ensureMarketplaceToken();
          if (!token) {
            console.log("❌ No marketplace token available. Please refresh token first.");
            break;
          }

          console.log("\n📋 Checking delegations...");
          const delegations = await getAtiaBlessingDelegations(token, wallet.address);

          if (delegations.length === 0) {
            console.log("ℹ️ No delegations found");
            break;
          }

          console.log("\n📋 Delegations:");
          for (const delegation of delegations) {
            const fromName = delegation.fromProfile?.name || delegation.fromAddress.slice(-4);
            const toName = delegation.toProfile?.name || delegation.toAddress.slice(-4);

            console.log(`\n🔄 From: ${fromName} (${delegation.fromAddress})`);
            console.log(`   To: ${toName} (${delegation.toAddress})`);
            console.log(`   Percentage: ${delegation.delegatorSlipsPercent}%`);
            console.log(`   Last Prayed: ${delegation.lastPrayedAt ? new Date(delegation.lastPrayedAt).toLocaleString() : 'Never'}`);
            console.log(`   Delegated At: ${new Date(delegation.delegatedAt * 1000).toLocaleString()}`);
          }
          break;
        }
      }
    } catch (error) {
      if (error instanceof Error) {
        console.error("❌ Error:", error.message);
      } else {
        console.error("❌ Error:", error);
      }
    } finally {
      await askToContinue();
    }
  }
}

main();
