BEGIN;

CREATE TABLE IF NOT EXISTS tokens (
  id BIGSERIAL PRIMARY KEY,
  chain_id INTEGER NOT NULL,
  address TEXT NOT NULL,
  name TEXT,
  symbol TEXT,
  decimals INTEGER,
  total_supply NUMERIC(78, 0),
  logo_url TEXT,
  website_url TEXT,
  description TEXT,
  verified BOOLEAN DEFAULT FALSE,
  created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  UNIQUE (chain_id, address)
);

CREATE INDEX IF NOT EXISTS idx_tokens_chain_id ON tokens (chain_id);
CREATE INDEX IF NOT EXISTS idx_tokens_chain_symbol ON tokens (chain_id, symbol);
CREATE INDEX IF NOT EXISTS idx_tokens_chain_name ON tokens (chain_id, name);

CREATE TABLE IF NOT EXISTS token_market_data (
  chain_id INTEGER NOT NULL,
  token_address TEXT NOT NULL,
  price_usd NUMERIC(38, 18),
  price_change_24h NUMERIC(38, 18),
  volume_24h NUMERIC(38, 18) NOT NULL DEFAULT 0,
  volume_7d NUMERIC(38, 18) NOT NULL DEFAULT 0,
  volume_30d NUMERIC(38, 18) NOT NULL DEFAULT 0,
  market_cap_usd NUMERIC(38, 18),
  liquidity_usd NUMERIC(38, 18) NOT NULL DEFAULT 0,
  holders_count INTEGER NOT NULL DEFAULT 0,
  transfers_24h INTEGER NOT NULL DEFAULT 0,
  last_updated TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  PRIMARY KEY (chain_id, token_address)
);

CREATE INDEX IF NOT EXISTS idx_token_market_data_volume_24h
  ON token_market_data (chain_id, volume_24h DESC);
CREATE INDEX IF NOT EXISTS idx_token_market_data_liquidity
  ON token_market_data (chain_id, liquidity_usd DESC);

CREATE TABLE IF NOT EXISTS liquidity_pools (
  id BIGSERIAL PRIMARY KEY,
  chain_id INTEGER NOT NULL,
  pool_address TEXT NOT NULL,
  token0_address TEXT NOT NULL,
  token1_address TEXT NOT NULL,
  dex_type TEXT NOT NULL,
  factory_address TEXT,
  router_address TEXT,
  reserve0 NUMERIC(78, 0) NOT NULL DEFAULT 0,
  reserve1 NUMERIC(78, 0) NOT NULL DEFAULT 0,
  reserve0_usd NUMERIC(38, 18) NOT NULL DEFAULT 0,
  reserve1_usd NUMERIC(38, 18) NOT NULL DEFAULT 0,
  total_liquidity_usd NUMERIC(38, 18) NOT NULL DEFAULT 0,
  volume_24h NUMERIC(38, 18) NOT NULL DEFAULT 0,
  fee_tier INTEGER,
  created_at_block BIGINT,
  created_at_timestamp TIMESTAMPTZ,
  last_updated TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  UNIQUE (chain_id, pool_address)
);

CREATE INDEX IF NOT EXISTS idx_liquidity_pools_chain_liquidity
  ON liquidity_pools (chain_id, total_liquidity_usd DESC);
CREATE INDEX IF NOT EXISTS idx_liquidity_pools_chain_token0
  ON liquidity_pools (chain_id, token0_address);
CREATE INDEX IF NOT EXISTS idx_liquidity_pools_chain_token1
  ON liquidity_pools (chain_id, token1_address);

CREATE TABLE IF NOT EXISTS pool_reserves_history (
  id BIGSERIAL PRIMARY KEY,
  chain_id INTEGER NOT NULL,
  pool_address TEXT NOT NULL,
  reserve0 NUMERIC(78, 0) NOT NULL,
  reserve1 NUMERIC(78, 0) NOT NULL,
  reserve0_usd NUMERIC(38, 18),
  reserve1_usd NUMERIC(38, 18),
  total_liquidity_usd NUMERIC(38, 18),
  block_number BIGINT NOT NULL,
  timestamp TIMESTAMPTZ NOT NULL
);

CREATE INDEX IF NOT EXISTS idx_pool_reserves_history_lookup
  ON pool_reserves_history (chain_id, pool_address, timestamp DESC);

CREATE TABLE IF NOT EXISTS swap_events (
  id BIGSERIAL PRIMARY KEY,
  chain_id INTEGER NOT NULL,
  pool_address TEXT NOT NULL,
  token0_address TEXT NOT NULL,
  token1_address TEXT NOT NULL,
  amount_usd NUMERIC(38, 18) NOT NULL DEFAULT 0,
  price_usd NUMERIC(38, 18),
  transaction_hash TEXT,
  log_index INTEGER,
  block_number BIGINT,
  timestamp TIMESTAMPTZ NOT NULL
);

CREATE INDEX IF NOT EXISTS idx_swap_events_pool_time
  ON swap_events (chain_id, pool_address, timestamp DESC);
CREATE INDEX IF NOT EXISTS idx_swap_events_token_time
  ON swap_events (chain_id, token0_address, token1_address, timestamp DESC);
CREATE UNIQUE INDEX IF NOT EXISTS idx_swap_events_unique_log
  ON swap_events (
    chain_id,
    pool_address,
    COALESCE(transaction_hash, ''),
    COALESCE(log_index, -1)
  );

CREATE TABLE IF NOT EXISTS token_ohlcv (
  id BIGSERIAL PRIMARY KEY,
  chain_id INTEGER NOT NULL,
  token_address TEXT NOT NULL,
  pool_address TEXT NOT NULL DEFAULT '',
  interval_type TEXT NOT NULL,
  open_price NUMERIC(38, 18) NOT NULL,
  high_price NUMERIC(38, 18) NOT NULL,
  low_price NUMERIC(38, 18) NOT NULL,
  close_price NUMERIC(38, 18) NOT NULL,
  volume NUMERIC(38, 18) NOT NULL DEFAULT 0,
  volume_usd NUMERIC(38, 18) NOT NULL DEFAULT 0,
  timestamp TIMESTAMPTZ NOT NULL,
  UNIQUE (chain_id, token_address, pool_address, interval_type, timestamp)
);

CREATE INDEX IF NOT EXISTS idx_token_ohlcv_lookup
  ON token_ohlcv (chain_id, token_address, interval_type, timestamp DESC);

CREATE TABLE IF NOT EXISTS provider_health_snapshots (
  id BIGSERIAL PRIMARY KEY,
  chain_id INTEGER NOT NULL,
  provider TEXT NOT NULL,
  status TEXT NOT NULL,
  supports_execution BOOLEAN NOT NULL DEFAULT FALSE,
  supports_quote BOOLEAN NOT NULL DEFAULT FALSE,
  metadata JSONB NOT NULL DEFAULT '{}'::jsonb,
  captured_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

CREATE INDEX IF NOT EXISTS idx_provider_health_snapshots_lookup
  ON provider_health_snapshots (chain_id, provider, captured_at DESC);

CREATE TABLE IF NOT EXISTS route_plan_cache (
  plan_id TEXT PRIMARY KEY,
  request_hash TEXT NOT NULL,
  chain_id INTEGER NOT NULL,
  destination_chain_id INTEGER NOT NULL,
  decision TEXT NOT NULL,
  response_json JSONB NOT NULL,
  created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  expires_at TIMESTAMPTZ
);

CREATE INDEX IF NOT EXISTS idx_route_plan_cache_lookup
  ON route_plan_cache (request_hash, expires_at DESC);

CREATE TABLE IF NOT EXISTS route_execution_metrics (
  id BIGSERIAL PRIMARY KEY,
  plan_id TEXT NOT NULL,
  chain_id INTEGER NOT NULL,
  provider TEXT NOT NULL,
  hop_index INTEGER NOT NULL,
  token_in_address TEXT NOT NULL,
  token_out_address TEXT NOT NULL,
  estimated_amount_out NUMERIC(78, 0),
  actual_amount_out NUMERIC(78, 0),
  status TEXT NOT NULL,
  metadata JSONB NOT NULL DEFAULT '{}'::jsonb,
  created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

CREATE INDEX IF NOT EXISTS idx_route_execution_metrics_lookup
  ON route_execution_metrics (plan_id, hop_index, created_at DESC);

COMMIT;
