#!/bin/bash

# Fetch Ostium contract addresses and ABIs
# Usage: ./fetch-contracts.sh [network] [force]
# network: mainnet|testnet|both (default: both)
# force: true|false (default: false)

set -e

NETWORK=${1:-both}
FORCE=${2:-false}

echo "🚀 Fetching Ostium contract data..."
echo "Network: $NETWORK"
echo "Force update: $FORCE"

# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color

# Function to log with colors
log_info() {
    echo -e "${BLUE}ℹ️  $1${NC}"
}

log_success() {
    echo -e "${GREEN}✅ $1${NC}"
}

log_warning() {
    echo -e "${YELLOW}⚠️  $1${NC}"
}

log_error() {
    echo -e "${RED}❌ $1${NC}"
}

# Install dependencies if needed
install_dependencies() {
    log_info "Installing dependencies..."
    
    # Install Foundry if not present
    if ! command -v cast &> /dev/null; then
        log_info "Installing Foundry..."
        curl -L https://foundry.paradigm.xyz | bash
        source ~/.bashrc
        foundryup
    fi
    
    # Install jq if not present
    if ! command -v jq &> /dev/null; then
        log_info "Installing jq..."
        if [[ "$OSTYPE" == "darwin"* ]]; then
            brew install jq
        else
            sudo apt-get update && sudo apt-get install -y jq
        fi
    fi
}

# Fetch contract addresses from Ostium repository
fetch_addresses_from_repo() {
    local network=$1
    log_info "Fetching addresses for $network from Ostium repository..."
    
    # Define possible URLs to check
    local urls=(
        "https://raw.githubusercontent.com/0xOstium/smart-contracts-public/main/broadcast/Deploy.s.sol/42161/run-latest.json"
        "https://raw.githubusercontent.com/0xOstium/smart-contracts-public/main/broadcast/Deploy.s.sol/421614/run-latest.json"
        "https://raw.githubusercontent.com/0xOstium/smart-contracts-public/main/deployments/${network}.json"
        "https://raw.githubusercontent.com/0xOstium/smart-contracts-public/main/deployments/arbitrum.json"
        "https://raw.githubusercontent.com/0xOstium/smart-contracts-public/main/deployments/arbitrum-sepolia.json"
        "https://raw.githubusercontent.com/0xOstium/smart-contracts-public/main/config/contracts.json"
        "https://raw.githubusercontent.com/0xOstium/smart-contracts-public/main/addresses.json"
        "https://raw.githubusercontent.com/0xOstium/smart-contracts-public/main/contracts.json"
    )
    
    for url in "${urls[@]}"; do
        log_info "Checking: $url"
        if curl -s -f "$url" -o temp_contracts.json 2>/dev/null; then
            log_success "Found deployment data at: $url"
            
            # Parse the JSON and extract addresses
            if jq -e '.transactions' temp_contracts.json > /dev/null 2>&1; then
                # Foundry broadcast format
                log_info "Parsing Foundry broadcast format..."
                jq -r '.transactions[] | select(.contractName and .contractAddress) | "\(.contractName):\(.contractAddress)"' temp_contracts.json
            elif jq -e 'type == "object"' temp_contracts.json > /dev/null 2>&1; then
                # Direct mapping format
                log_info "Parsing direct address mapping format..."
                jq -r 'to_entries[] | "\(.key):\(.value)"' temp_contracts.json
            fi
            
            rm -f temp_contracts.json
            return 0
        fi
    done
    
    log_warning "Could not fetch addresses from repository for $network"
    rm -f temp_contracts.json
    return 1
}

# Fetch ABI using cast
fetch_abi_with_cast() {
    local address=$1
    local network=$2
    local rpc_url=$3
    
    log_info "Fetching ABI for $address using cast..."
    
    # Try to get ABI using cast
    if cast interface --chain "$network" "$address" --rpc-url "$rpc_url" 2>/dev/null; then
        log_success "Successfully fetched ABI using cast"
        return 0
    else
        log_warning "Failed to fetch ABI using cast for $address"
        return 1
    fi
}

# Fetch ABI from block explorer
fetch_abi_from_explorer() {
    local address=$1
    local network=$2
    
    log_info "Fetching ABI for $address from block explorer..."
    
    local api_url
    if [[ "$network" == "mainnet" ]]; then
        api_url="https://api.arbiscan.io/api?module=contract&action=getabi&address=$address"
    else
        api_url="https://api-sepolia.arbiscan.io/api?module=contract&action=getabi&address=$address"
    fi
    
    local response
    response=$(curl -s "$api_url")
    
    if echo "$response" | jq -e '.status == "1" and .result' > /dev/null; then
        echo "$response" | jq -r '.result'
        log_success "Successfully fetched ABI from block explorer"
        return 0
    else
        log_warning "Failed to fetch ABI from block explorer for $address"
        return 1
    fi
}

# Generate Rust ABI file
generate_rust_abi() {
    local abi_json=$1
    local contract_name=$2
    local output_file=$3
    
    log_info "Generating Rust ABI file for $contract_name..."
    
    cat > "$output_file" << EOF
use ethers::abi::Abi;

pub const ${contract_name^^}_ABI: &str = r#"$abi_json"#;

pub fn get_${contract_name,,}_abi() -> Abi {
    serde_json::from_str(${contract_name^^}_ABI).expect("Valid ABI")
}
EOF
    
    log_success "Generated $output_file"
}

# Process a single network
process_network() {
    local network=$1
    local rpc_url=$2
    local chain_id=$3
    
    log_info "Processing $network network (Chain ID: $chain_id)..."
    
    # Define fallback addresses
    declare -A fallback_addresses
    if [[ "$network" == "mainnet" ]]; then
        fallback_addresses[usdc]="0xaf88d065e77c8cC2239327C5EDb3A432268e5831"
        fallback_addresses[trading]="0x6D0bA1f9996DBD8885827e1b2e8f6593e7702411"
        fallback_addresses[storage]="0xcCd5891083A8acD2074690F65d3024E7D13d66E7"
    else
        fallback_addresses[usdc]="0xe73B11Fb1e3eeEe8AF2a23079A4410Fe1B370548"
        fallback_addresses[trading]="0x2A9B9c988393f46a2537B0ff11E98c2C15a95afe"
        fallback_addresses[storage]="0x0b9F5243B29938668c9Cfbd7557A389EC7Ef88b8"
    fi
    
    # Try to fetch addresses from repository
    declare -A contract_addresses
    if fetch_addresses_from_repo "$network" > contract_data.txt 2>/dev/null; then
        while IFS=':' read -r name address; do
            name_lower=$(echo "$name" | tr '[:upper:]' '[:lower:]')
            if [[ "$name_lower" == *"trading"* && "$name_lower" != *"storage"* ]]; then
                contract_addresses[trading]="$address"
            elif [[ "$name_lower" == *"storage"* ]]; then
                contract_addresses[storage]="$address"
            elif [[ "$name_lower" == *"usdc"* ]]; then
                contract_addresses[usdc]="$address"
            fi
        done < contract_data.txt
        rm -f contract_data.txt
    fi
    
    # Use fallback addresses for missing contracts
    for contract in usdc trading storage; do
        if [[ -z "${contract_addresses[$contract]}" ]]; then
            log_warning "Using fallback address for $contract"
            contract_addresses[$contract]="${fallback_addresses[$contract]}"
        fi
    done
    
    # Fetch ABIs for each contract
    local has_changes=false
    for contract in "${!contract_addresses[@]}"; do
        local address="${contract_addresses[$contract]}"
        local output_file="src/abi/${contract}.rs"
        
        log_info "Processing $contract at $address..."
        
        # Try multiple methods to get ABI
        local abi_json=""
        
        # Method 1: Use cast (most reliable for getting current ABI)
        if command -v cast &> /dev/null; then
            if abi_json=$(fetch_abi_with_cast "$address" "$chain_id" "$rpc_url"); then
                # Convert interface format to ABI JSON if needed
                if [[ "$abi_json" != \[* ]]; then
                    log_info "Converting interface to ABI format..."
                    # This would need additional processing
                fi
            fi
        fi
        
        # Method 2: Block explorer API
        if [[ -z "$abi_json" ]]; then
            abi_json=$(fetch_abi_from_explorer "$address" "$network")
        fi
        
        if [[ -n "$abi_json" ]]; then
            # Check if ABI has changed
            local should_update=false
            if [[ "$FORCE" == "true" ]] || [[ ! -f "$output_file" ]]; then
                should_update=true
            else
                # Compare with existing ABI
                local existing_abi
                if existing_abi=$(grep -o 'r#"\[.*\]"#' "$output_file" | sed 's/r#"//g' | sed 's/"#//g'); then
                    local normalized_new
                    local normalized_existing
                    normalized_new=$(echo "$abi_json" | jq -c 'sort_by(.name)')
                    normalized_existing=$(echo "$existing_abi" | jq -c 'sort_by(.name)')
                    
                    if [[ "$normalized_new" != "$normalized_existing" ]]; then
                        should_update=true
                    fi
                else
                    should_update=true
                fi
            fi
            
            if [[ "$should_update" == "true" ]]; then
                mkdir -p "$(dirname "$output_file")"
                generate_rust_abi "$abi_json" "$contract" "$output_file"
                has_changes=true
                echo "- Updated $contract ABI ($network)" >> abi-changes.txt
            else
                log_success "No changes for $contract ABI"
            fi
        else
            log_error "Failed to fetch ABI for $contract"
        fi
    done
    
    return $([ "$has_changes" = true ] && echo 0 || echo 1)
}

# Main execution
main() {
    install_dependencies
    
    # Create changes file
    > abi-changes.txt
    
    local overall_changes=false
    
    if [[ "$NETWORK" == "both" ]]; then
        if process_network "mainnet" "https://arb1.arbitrum.io/rpc" "42161"; then
            overall_changes=true
        fi
        if process_network "testnet" "https://sepolia-rollup.arbitrum.io/rpc" "421614"; then
            overall_changes=true
        fi
    else
        local rpc_url
        local chain_id
        if [[ "$NETWORK" == "mainnet" ]]; then
            rpc_url="https://arb1.arbitrum.io/rpc"
            chain_id="42161"
        else
            rpc_url="https://sepolia-rollup.arbitrum.io/rpc"
            chain_id="421614"
        fi
        
        if process_network "$NETWORK" "$rpc_url" "$chain_id"; then
            overall_changes=true
        fi
    fi
    
    # Update mod.rs
    log_info "Updating mod.rs..."
    {
        echo "// Auto-generated module exports"
        find src/abi -name "*.rs" -not -name "mod.rs" | while read -r file; do
            module=$(basename "$file" .rs)
            echo "pub mod $module;"
        done
        echo ""
        find src/abi -name "*.rs" -not -name "mod.rs" | while read -r file; do
            module=$(basename "$file" .rs)
            echo "pub use $module::${module^^}_ABI;"
        done
    } > src/abi/mod.rs
    
    if [[ "$overall_changes" == "true" ]]; then
        log_success "ABI update completed with changes!"
        if [[ -s abi-changes.txt ]]; then
            echo "Changes:"
            cat abi-changes.txt
        fi
        exit 1  # Exit with 1 to signal changes for CI
    else
        log_success "ABI update completed - no changes detected"
        exit 0
    fi
}

main "$@" 