import "dotenv/config";

import { getAddress, isAddress, parseEther, type Address, type Hex } from "viem";
import { privateKeyToAccount } from "viem/accounts";
import { z } from "zod";

const blankToUndefined = (value: unknown) => value === "" ? undefined : value;

const environmentSchema = z.object({
  ROBINHOOD_RPC_URL: z.string().url().default("https://rpc.mainnet.chain.robinhood.com"),
  ROBINHOOD_RPC_URLS: z.preprocess(blankToUndefined, z.string().optional()),
  EVM_PRIVATE_KEY: z.preprocess(blankToUndefined, z.string().regex(/^0x[a-fA-F0-9]{64}$/).optional()),
  FEE_WALLET: z.preprocess(blankToUndefined, z.string().refine(isAddress, "Invalid fee wallet").optional()),
  AGENT_MODE: z.enum(["observe", "live"]).default("observe"),
  LIVE_TRADING_ACK: z.preprocess(blankToUndefined, z.string().optional()),
  MIN_GAS_RESERVE_ETH: z.string().default("0.005"),
  MAX_LAUNCH_TOTAL_ETH: z.string().default("0.05"),
  MAX_SLIPPAGE_BPS: z.coerce.number().int().min(1).max(2_000).default(200),
  MAX_PRICE_IMPACT_BPS: z.coerce.number().int().min(1).max(2_000).default(150),
  MIN_BUYBACK_ETH: z.string().default("0.001"),
  XAPI_BASE_URL: z.string().url().default("https://action.xapi.to"),
  XAPI_KEY: z.preprocess(blankToUndefined, z.string().optional()),
  TREND_LOCATION: z.string().default("worldwide"),
  TREND_QUERY: z.string().default("(crypto OR memecoin OR token) lang:en"),
  SCAN_INTERVAL_SECONDS: z.coerce.number().int().min(30).max(86_400).default(300),
  TREND_MAX_CANDIDATES: z.coerce.number().int().min(1).max(20).default(5),
  MIN_LAUNCH_SCORE: z.coerce.number().int().min(1).max(100).default(80),
  MIN_BUYBACK_SCORE: z.coerce.number().int().min(1).max(100).default(80),
  MODEL_BASE_URL: z.preprocess(blankToUndefined, z.string().url().optional()),
  MODEL_API_KEY: z.preprocess(blankToUndefined, z.string().optional()),
  MODEL_NAME: z.preprocess(blankToUndefined, z.string().optional()),
  IMAGE_MODEL_BASE_URL: z.preprocess(blankToUndefined, z.string().url().optional()),
  IMAGE_MODEL_API_KEY: z.preprocess(blankToUndefined, z.string().optional()),
  IMAGE_MODEL_NAME: z.preprocess(blankToUndefined, z.string().optional()),
  AGENT_ID: z.preprocess(blankToUndefined, z.string().regex(/^[a-z0-9][a-z0-9-]{2,63}$/).optional()),
  AGENT_EVENT_ENDPOINT: z.preprocess(blankToUndefined, z.string().url().optional()),
  AGENT_STATE_DB_PATH: z.string().default("./data/agent.db"),
  AUTO_CLAIM_ENABLED: z.enum(["true", "false"]).default("true"),
  AUTO_LAUNCH_ENABLED: z.enum(["true", "false"]).default("false"),
  AGGRESSIVE_LAUNCH_MODE: z.enum(["true", "false"]).default("false"),
  AUTO_BUYBACK_ENABLED: z.enum(["true", "false"]).default("true"),
  INITIAL_BUY_ETH: z.string().default("0"),
  TOKEN_ASSET_ENDPOINT: z.preprocess(blankToUndefined, z.string().url().optional()),
  TOKEN_ASSET_PUBLIC_BASE_URL: z.preprocess(blankToUndefined, z.string().url().optional()),
  BUYBACK_CHUNK_BPS: z.coerce.number().int().min(1).max(10_000).default(10_000),
  LAUNCH_COOLDOWN_MINUTES: z.coerce.number().int().min(1).max(10_080).default(60),
  CONTROL_ENABLED: z.enum(["true", "false"]).default("false"),
  CONTROL_PORT: z.coerce.number().int().min(1).max(65_535).default(8787),
  CONTROL_PASSWORD: z.preprocess(blankToUndefined, z.string().min(12).optional()),
  CONTROL_PUBLIC_ORIGIN: z.preprocess(blankToUndefined, z.string().url().optional()),
  CONTROL_INTERNAL_TOKEN: z.preprocess(blankToUndefined, z.string().min(32).optional()),
  WALLET_MASTER_KEY: z.preprocess(blankToUndefined, z.string().min(43).optional()),
});

export const LIVE_TRADING_ACK = "I_ACCEPT_REAL_MONEY_EXECUTION";

export type AgentConfig = ReturnType<typeof loadConfig>;

export const loadConfig = () => {
  const env = environmentSchema.parse(process.env);
  const rpcUrls = [
    env.ROBINHOOD_RPC_URL,
    ...(env.ROBINHOOD_RPC_URLS ?? "").split(",").map((value) => value.trim()).filter(Boolean),
  ].filter((value, index, values) => values.indexOf(value) === index);
  for (const rpcUrl of rpcUrls) new URL(rpcUrl);
  const account = env.EVM_PRIVATE_KEY ? privateKeyToAccount(env.EVM_PRIVATE_KEY as Hex) : null;
  const feeWallet = env.FEE_WALLET
    ? getAddress(env.FEE_WALLET)
    : account?.address ?? null;

  if (env.CONTROL_ENABLED === "true") {
    if (!env.CONTROL_PASSWORD) throw new Error("CONTROL_PASSWORD is required when the operator control service is enabled");
    if (!env.CONTROL_PUBLIC_ORIGIN) throw new Error("CONTROL_PUBLIC_ORIGIN is required when the operator control service is enabled");
    if (!env.CONTROL_INTERNAL_TOKEN) throw new Error("CONTROL_INTERNAL_TOKEN is required when the operator control service is enabled");
    if (!env.WALLET_MASTER_KEY) throw new Error("WALLET_MASTER_KEY is required when the operator control service is enabled");
  }

  if (env.AGENT_MODE === "live") {
    if (!account && !env.WALLET_MASTER_KEY) throw new Error("EVM_PRIVATE_KEY or WALLET_MASTER_KEY is required in live mode");
    if (!feeWallet && !env.WALLET_MASTER_KEY) throw new Error("FEE_WALLET or a wallet vault is required in live mode");
    if (env.LIVE_TRADING_ACK !== LIVE_TRADING_ACK) {
      throw new Error(`Live mode requires LIVE_TRADING_ACK=${LIVE_TRADING_ACK}`);
    }
  }

  return {
    rpcUrl: rpcUrls[0],
    rpcUrls,
    account,
    feeWallet: feeWallet as Address | null,
    mode: env.AGENT_MODE,
    minGasReserveWei: parseEther(env.MIN_GAS_RESERVE_ETH),
    maxLaunchTotalWei: parseEther(env.MAX_LAUNCH_TOTAL_ETH),
    maxSlippageBps: env.MAX_SLIPPAGE_BPS,
    maxPriceImpactBps: env.MAX_PRICE_IMPACT_BPS,
    minBuybackWei: parseEther(env.MIN_BUYBACK_ETH),
    xApi: {
      baseUrl: env.XAPI_BASE_URL,
      key: env.XAPI_KEY,
      location: env.TREND_LOCATION,
      query: env.TREND_QUERY,
    },
    model: {
      baseUrl: env.MODEL_BASE_URL,
      apiKey: env.MODEL_API_KEY,
      name: env.MODEL_NAME,
    },
    imageModel: {
      baseUrl: env.IMAGE_MODEL_BASE_URL ?? env.MODEL_BASE_URL,
      apiKey: env.IMAGE_MODEL_API_KEY ?? env.MODEL_API_KEY,
      name: env.IMAGE_MODEL_NAME ?? "gpt-image-2",
    },
    scanIntervalSeconds: env.SCAN_INTERVAL_SECONDS,
    trendMaxCandidates: env.TREND_MAX_CANDIDATES,
    minLaunchScore: env.MIN_LAUNCH_SCORE,
    minBuybackScore: env.MIN_BUYBACK_SCORE,
    publicEvents: {
      agentId: env.AGENT_ID,
      endpoint: env.AGENT_EVENT_ENDPOINT,
    },
    stateDbPath: env.AGENT_STATE_DB_PATH,
    autoClaimEnabled: env.AUTO_CLAIM_ENABLED === "true",
    autoLaunchEnabled: env.AUTO_LAUNCH_ENABLED === "true",
    aggressiveLaunchMode: env.AGGRESSIVE_LAUNCH_MODE === "true",
    autoBuybackEnabled: env.AUTO_BUYBACK_ENABLED === "true",
    initialBuyEth: env.INITIAL_BUY_ETH,
    tokenAssets: {
      endpoint: env.TOKEN_ASSET_ENDPOINT,
      publicBaseUrl: env.TOKEN_ASSET_PUBLIC_BASE_URL,
    },
    buybackChunkBps: env.BUYBACK_CHUNK_BPS,
    launchCooldownMinutes: env.LAUNCH_COOLDOWN_MINUTES,
    control: {
      enabled: env.CONTROL_ENABLED === "true",
      port: env.CONTROL_PORT,
      password: env.CONTROL_PASSWORD,
      publicOrigin: env.CONTROL_PUBLIC_ORIGIN,
      internalToken: env.CONTROL_INTERNAL_TOKEN,
      walletMasterKey: env.WALLET_MASTER_KEY,
    },
  };
};
