import "dotenv/config";
import path from "node:path";
import { existsSync } from "node:fs";
import { fileURLToPath } from "node:url";
import { isAddress, type Address, type Hex } from "viem";

const rootDir = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");

const DEFAULT_API_URL = "https://3sat.network";
const DEFAULT_RPC_URL = "https://sepolia-rollup.arbitrum.io/rpc";
const DEFAULT_CHAIN_ID = 421614;
const DEFAULT_SAT_TOKEN = "0x8Fe0e3557773B200995608a43691f3dE9B2e3Fda" as Address;
const DEFAULT_USDC_TOKEN = "0x75faf114eafb1BDbe2F0316DF893fd58CE46AA4d" as Address;
const DEFAULT_BOUNTY_MANAGER = "0x942b326B190d588fE1bb3931502f509c9f9eC767" as Address;

function boolFromEnv(name: string, fallback: boolean) {
  const value = process.env[name];
  if (value === undefined) {
    return fallback;
  }
  return ["1", "true", "yes", "y"].includes(value.toLowerCase());
}

function numberFromEnv(name: string, fallback: number) {
  const value = Number(process.env[name]);
  return Number.isFinite(value) && value > 0 ? value : fallback;
}

function integerFromEnv(name: string, fallback: number) {
  return Math.floor(numberFromEnv(name, fallback));
}

function addressFromEnv(name: string, fallback: Address): Address {
  const value = process.env[name]?.trim() || fallback;
  if (!isAddress(value)) {
    throw new Error(name + " must be a valid contract address.");
  }
  return value as Address;
}

function privateKeyFromEnv(): Hex {
  const raw = process.env.SOLVER_PRIVATE_KEY;
  if (!raw) {
    throw new Error("SOLVER_PRIVATE_KEY is required.");
  }
  const value = raw.startsWith("0x") ? raw : "0x" + raw;
  if (!/^0x[a-fA-F0-9]{64}$/.test(value)) {
    throw new Error("SOLVER_PRIVATE_KEY must be a 32-byte hex private key.");
  }
  return value as Hex;
}

function firstExisting(paths: string[]) {
  return paths.find((candidate) => existsSync(candidate));
}

export function defaultCryptoMiniSatPath() {
  const configured = process.env.CRYPTOMINISAT_PATH?.trim();
  if (configured) {
    return path.resolve(configured);
  }

  return firstExisting([path.join(rootDir, "bin", "cryptominisat5")]);
}

export function loadConfig() {
  const cryptoMiniSatPath = defaultCryptoMiniSatPath();
  if (!cryptoMiniSatPath) {
    throw new Error(
      "CryptoMiniSat executable was not found. Put cryptominisat5 under bin/ or set CRYPTOMINISAT_PATH."
    );
  }

  return {
    rootDir,
    apiUrl: (process.env["3SAT_API_URL"] || DEFAULT_API_URL).replace(/\/+$/, ""),
    rpcUrl: process.env.RPC_URL || DEFAULT_RPC_URL,
    chainId: Number(process.env.CHAIN_ID || String(DEFAULT_CHAIN_ID)),
    solverPrivateKey: privateKeyFromEnv(),
    satToken: addressFromEnv("SAT_TOKEN_ADDRESS", DEFAULT_SAT_TOKEN),
    usdcToken: addressFromEnv("USDC_TOKEN_ADDRESS", DEFAULT_USDC_TOKEN),
    bountyManager: addressFromEnv("BOUNTY_MANAGER_ADDRESS", DEFAULT_BOUNTY_MANAGER),
    cryptoMiniSatPath,
    pollIntervalMs: numberFromEnv("POLL_INTERVAL_SECONDS", 60) * 1000,
    maxBountiesPerPoll: integerFromEnv("MAX_BOUNTIES_PER_POLL", 5),
    maxActiveJobs: integerFromEnv("MAX_ACTIVE_JOBS", 3),
    minCommitSecondsLeft: integerFromEnv("MIN_COMMIT_SECONDS_LEFT", 120),
    solveTimeoutMs: numberFromEnv("SOLVE_TIMEOUT_SECONDS", 600) * 1000,
    minRewardTokens: process.env.MIN_REWARD_TOKEN?.trim() || process.env.MIN_REWARD_3SAT?.trim() || "0",
    dryRun: boolFromEnv("DRY_RUN", false),
    runOnce: boolFromEnv("RUN_ONCE", false),
    allowCompetingSubmissions: boolFromEnv("ALLOW_COMPETING_SUBMISSIONS", false),
    waitForFinalization: boolFromEnv("WAIT_FOR_FINALIZATION", true),
    artifactDir: path.resolve(process.env.ARTIFACT_DIR || "data/artifacts"),
    stateFile: path.resolve(process.env.STATE_FILE || "data/solver-state.json"),
    logFile: path.resolve(process.env.LOG_FILE || "logs/solver-client.log")
  };
}

export type SolverClientConfig = ReturnType<typeof loadConfig>;

