#!/bin/bash
################################################################################
# Cloudflare Detector - Identify domains using Cloudflare
################################################################################
# Scans all domains on the server and detects Cloudflare usage
# Checks: Nameservers, IP ranges, and HTTP headers
################################################################################

# Source common functions
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "$SCRIPT_DIR/../../lib/common-functions.sh" 2>/dev/null || {
    echo "[ERROR] Cannot load common-functions.sh"
    exit 1
}

# Detect control panel and system
if type -t detect_system &>/dev/null; then
    detect_system
fi

################################################################################
# Configuration
################################################################################

# Cloudflare IP ranges (IPv4) - updated as of 2024
# Source: https://www.cloudflare.com/ips-v4
CLOUDFLARE_IPV4_RANGES=(
    "173.245.48.0/20"
    "103.21.244.0/22"
    "103.22.200.0/22"
    "103.31.4.0/22"
    "141.101.64.0/18"
    "108.162.192.0/18"
    "190.93.240.0/20"
    "188.114.96.0/20"
    "197.234.240.0/22"
    "198.41.128.0/17"
    "162.158.0.0/15"
    "104.16.0.0/13"
    "104.24.0.0/14"
    "172.64.0.0/13"
    "131.0.72.0/22"
)

################################################################################
# Helper Functions
################################################################################

get_domains() {
    local domains=()

    # Get domains from cPanel userdata
    if [ -d /var/cpanel/userdata ]; then
        while IFS= read -r domain_file; do
            local domain=$(basename "$domain_file")

            # Skip cache files, main file, and config files
            if [[ "$domain_file" =~ \.cache$ ]] || \
               [[ "$domain_file" =~ /cache$ ]] || \
               [[ "$domain_file" =~ /main$ ]] || \
               [[ "$domain" =~ _SSL$ ]] || \
               [[ "$domain" =~ \.yaml$ ]] || \
               [[ "$domain" =~ \.json$ ]] || \
               [[ "$domain" =~ \.conf$ ]] || \
               [[ "$domain" =~ \.backup$ ]] || \
               [[ "$domain" =~ \.transferred$ ]] || \
               [[ "$domain" =~ \.db$ ]] || \
               [[ "$domain" =~ \.swp$ ]] || \
               [[ "$domain" =~ \.tmp$ ]] || \
               [[ "$domain" =~ php-fpm ]] || \
               [[ "$domain" =~ \.lock$ ]]; then
                continue
            fi

            # Skip system/template domains
            if [[ "$domain" =~ cloudvpstemplate\. ]] || \
               [[ "$domain" =~ cprapid\. ]] || \
               [[ "$domain" =~ ^[0-9\-]+\. ]] || \
               [[ "$domain_file" =~ /nobody/ ]]; then
                continue
            fi

            # Only add if it looks like a domain
            if [[ "$domain" != "." && "$domain" != ".." && "$domain" =~ \. ]]; then
                domains+=("$domain")
            fi
        done < <(find /var/cpanel/userdata -type f 2>/dev/null)
    fi

    printf '%s\n' "${domains[@]}" | sort -u
}

is_cloudflare_ip() {
    local ip="$1"

    # Convert IP to integer for range checking
    local ip_int=$(echo "$ip" | awk -F. '{print ($1 * 16777216) + ($2 * 65536) + ($3 * 256) + $4}')

    for range in "${CLOUDFLARE_IPV4_RANGES[@]}"; do
        local network=$(echo "$range" | cut -d'/' -f1)
        local cidr=$(echo "$range" | cut -d'/' -f2)

        # Calculate network range
        local net_int=$(echo "$network" | awk -F. '{print ($1 * 16777216) + ($2 * 65536) + ($3 * 256) + $4}')
        local mask=$((0xFFFFFFFF << (32 - cidr)))
        local start=$((net_int & mask))
        local end=$((start + (1 << (32 - cidr)) - 1))

        # Check if IP is in this range
        if [ "$ip_int" -ge "$start" ] && [ "$ip_int" -le "$end" ]; then
            return 0
        fi
    done

    return 1
}

check_nameservers() {
    local domain="$1"

    # Get nameservers
    local nameservers=$(dig +short NS "$domain" 2>/dev/null | tr '[:upper:]' '[:lower:]')

    if [ -z "$nameservers" ]; then
        echo "UNKNOWN"
        return 2
    fi

    # Check if any nameserver contains cloudflare
    if echo "$nameservers" | grep -q "cloudflare"; then
        echo "CLOUDFLARE"
        return 0
    fi

    echo "OTHER"
    return 1
}

check_ip_address() {
    local domain="$1"

    # Get A record
    local ip=$(dig +short A "$domain" 2>/dev/null | head -1)

    if [ -z "$ip" ]; then
        echo "UNKNOWN"
        return 2
    fi

    # Check if IP is in Cloudflare range
    if is_cloudflare_ip "$ip"; then
        echo "CLOUDFLARE"
        return 0
    fi

    echo "DIRECT"
    return 1
}

check_http_headers() {
    local domain="$1"

    # Try HTTP request and look for Cloudflare headers
    local headers=$(curl -sI -m 3 "http://$domain" 2>/dev/null)

    if [ -z "$headers" ]; then
        echo "UNKNOWN"
        return 2
    fi

    # Check for CF-RAY header (definitive Cloudflare indicator)
    if echo "$headers" | grep -qi "CF-RAY:"; then
        echo "CLOUDFLARE"
        return 0
    fi

    # Check for Server: cloudflare
    if echo "$headers" | grep -qi "Server:.*cloudflare"; then
        echo "CLOUDFLARE"
        return 0
    fi

    echo "DIRECT"
    return 1
}

get_location_name() {
    local code="$1"

    # Map IATA codes to city names
    case "$code" in
        # North America
        ORD) echo "Chicago" ;;
        LAX) echo "Los Angeles" ;;
        IAD) echo "Ashburn, VA" ;;
        DFW) echo "Dallas" ;;
        ATL) echo "Atlanta" ;;
        SEA) echo "Seattle" ;;
        SJC) echo "San Jose, CA" ;;
        MIA) echo "Miami" ;;
        YYZ) echo "Toronto" ;;
        BNA) echo "Nashville" ;;
        DEN) echo "Denver" ;;
        PHX) echo "Phoenix" ;;
        EWR) echo "Newark, NJ" ;;
        BOS) echo "Boston" ;;

        # Europe
        LHR) echo "London" ;;
        FRA) echo "Frankfurt" ;;
        AMS) echo "Amsterdam" ;;
        CDG) echo "Paris" ;;
        MAD) echo "Madrid" ;;
        MAN) echo "Manchester" ;;
        ARN) echo "Stockholm" ;;
        WAW) echo "Warsaw" ;;
        VIE) echo "Vienna" ;;

        # Asia Pacific
        SIN) echo "Singapore" ;;
        NRT) echo "Tokyo" ;;
        HKG) echo "Hong Kong" ;;
        SYD) echo "Sydney" ;;
        ICN) echo "Seoul" ;;
        BOM) echo "Mumbai" ;;
        DEL) echo "New Delhi" ;;

        # Middle East
        DXB) echo "Dubai" ;;
        TLV) echo "Tel Aviv" ;;

        # South America
        GRU) echo "São Paulo" ;;
        EZE) echo "Buenos Aires" ;;

        # If unknown, show the code
        *) echo "$code" ;;
    esac
}

get_cloudflare_location() {
    local domain="$1"

    # Get CF-RAY header which contains datacenter code
    local cf_ray=$(curl -sI -m 3 "http://$domain" 2>/dev/null | grep -i "CF-RAY:" | head -1 | awk '{print $2}' | tr -d '\r')

    if [ -z "$cf_ray" ]; then
        echo "N/A"
        return 1
    fi

    # Extract datacenter code (last 3 characters, e.g., "ORD" from "9c1b340e5cdacc3a-ORD")
    local colo=$(echo "$cf_ray" | awk -F'-' '{print $NF}')

    if [ -n "$colo" ]; then
        # Return city name instead of code
        local city=$(get_location_name "$colo")
        echo "$city"
        return 0
    fi

    echo "N/A"
    return 1
}

domain_resolves() {
    local domain="$1"

    # Check if domain has any A records
    local ip=$(dig +short A "$domain" 2>/dev/null | head -1)

    # Also check AAAA for IPv6-only domains
    if [ -z "$ip" ]; then
        ip=$(dig +short AAAA "$domain" 2>/dev/null | head -1)
    fi

    # Return 0 if domain resolves, 1 if it doesn't
    [ -n "$ip" ]
}

detect_cloudflare() {
    local domain="$1"

    # Skip domains that don't resolve at all
    if ! domain_resolves "$domain"; then
        echo "NXDOMAIN"
        return 3
    fi

    local ns_result=$(check_nameservers "$domain")
    local ip_result=$(check_ip_address "$domain")
    local http_result=$(check_http_headers "$domain")

    # PROXIED (Orange Cloud): Traffic goes through Cloudflare CDN
    # Confirmed by: IP in Cloudflare range OR CF-RAY header present
    # This is what most people mean by "using Cloudflare"
    if [ "$ip_result" = "CLOUDFLARE" ] || [ "$http_result" = "CLOUDFLARE" ]; then
        echo "CLOUDFLARE"
        return 0
    fi

    # DNS-ONLY (Gray Cloud): Using Cloudflare nameservers but traffic is direct
    # These domains use CF for DNS management but NOT for CDN/caching/protection
    if [ "$ns_result" = "CLOUDFLARE" ] && [ "$ip_result" = "DIRECT" ]; then
        echo "DNS-ONLY"
        return 4
    fi

    # DIRECT: Not using Cloudflare at all
    if [ "$ns_result" = "OTHER" ] && [ "$ip_result" = "DIRECT" ] && [ "$http_result" = "DIRECT" ]; then
        echo "DIRECT"
        return 1
    fi

    # Otherwise, uncertain
    echo "UNKNOWN"
    return 2
}

################################################################################
# Main Functions
################################################################################

scan_all_domains() {
    print_banner "Cloudflare Detection Scan"

    echo "Scanning all domains on this server..."
    echo "Checking: Nameservers, IP addresses, HTTP headers"
    echo ""

    # Get all domains
    local domains=$(get_domains)

    if [ -z "$domains" ]; then
        print_error "No domains found on this server"
        echo ""
        echo "This tool requires cPanel with configured domains"
        press_enter
        return 1
    fi

    local domain_count=$(echo "$domains" | wc -l)

    echo "Found $domain_count domains to check..."
    echo ""
    sleep 1

    # Arrays to store results
    local -a cloudflare_domains=()
    local -a cloudflare_locations=()
    local -a dns_only_domains=()
    local -a direct_domains=()
    local -a unknown_domains=()
    local -a nxdomain_domains=()

    # Progress tracking
    local current=0

    while IFS= read -r domain; do
        current=$((current + 1))

        # Show progress
        printf "\r[%d/%d] Checking: %-50s" "$current" "$domain_count" "$domain"

        # Detect Cloudflare
        local result=$(detect_cloudflare "$domain")

        case "$result" in
            "CLOUDFLARE")
                cloudflare_domains+=("$domain")
                # Get Cloudflare datacenter location
                local location=$(get_cloudflare_location "$domain")
                cloudflare_locations+=("$location")
                ;;
            "DNS-ONLY")
                dns_only_domains+=("$domain")
                ;;
            "DIRECT")
                direct_domains+=("$domain")
                ;;
            "NXDOMAIN")
                nxdomain_domains+=("$domain")
                ;;
            *)
                unknown_domains+=("$domain")
                ;;
        esac
    done <<< "$domains"

    echo ""
    echo ""

    # Display results
    echo "═══════════════════════════════════════════════════════════════"
    echo "                     SCAN RESULTS"
    echo "═══════════════════════════════════════════════════════════════"
    echo ""

    # Cloudflare proxied domains (orange cloud)
    if [ ${#cloudflare_domains[@]} -gt 0 ]; then
        print_success "🔶 Cloudflare Proxied (Orange Cloud): ${#cloudflare_domains[@]}"
        echo "  Traffic routed through Cloudflare CDN - caching, DDoS protection, etc."
        echo ""
        for i in "${!cloudflare_domains[@]}"; do
            local domain="${cloudflare_domains[$i]}"
            local location="${cloudflare_locations[$i]}"
            if [ "$location" != "N/A" ]; then
                printf "  ✓ %-50s [%s]\n" "$domain" "$location"
            else
                echo "  ✓ $domain"
            fi
        done
        echo ""
    else
        echo "🔶 Domains using Cloudflare: 0"
        echo ""
    fi

    # DNS-only domains (gray cloud)
    if [ ${#dns_only_domains[@]} -gt 0 ]; then
        print_warning "☁️  Cloudflare DNS-Only (Gray Cloud): ${#dns_only_domains[@]}"
        echo ""
        echo "  These domains use Cloudflare nameservers but traffic goes DIRECT to your server."
        echo "  Not using: CDN caching, DDoS protection, or Cloudflare features."
        echo ""
        for domain in "${dns_only_domains[@]}"; do
            echo "  ○ $domain"
        done
        echo ""
    fi

    # Direct domains
    if [ ${#direct_domains[@]} -gt 0 ]; then
        print_info "🌐 Domains NOT using Cloudflare: ${#direct_domains[@]}"
        echo ""
        for domain in "${direct_domains[@]}"; do
            echo "  • $domain"
        done
        echo ""
    else
        echo "🌐 Domains NOT using Cloudflare: 0"
        echo ""
    fi

    # NXDOMAIN domains (don't resolve)
    if [ ${#nxdomain_domains[@]} -gt 0 ]; then
        print_warning "⚠ Domains that don't resolve (NXDOMAIN): ${#nxdomain_domains[@]}"
        echo ""
        for domain in "${nxdomain_domains[@]}"; do
            echo "  ✗ $domain"
        done
        echo ""
        echo "  💡 Tip: These domains are configured in cPanel but don't have DNS records."
        echo "     Consider removing them or checking your DNS configuration."
        echo ""
    fi

    # Unknown domains
    if [ ${#unknown_domains[@]} -gt 0 ]; then
        print_warning "❓ Uncertain (DNS/connectivity issues): ${#unknown_domains[@]}"
        echo ""
        for domain in "${unknown_domains[@]}"; do
            echo "  ? $domain"
        done
        echo ""
    fi

    echo "═══════════════════════════════════════════════════════════════"
    echo ""
    echo "Summary:"
    echo "  Total domains:     $domain_count"
    echo "  Cloudflare Proxy:  ${#cloudflare_domains[@]} (orange cloud - using CDN/caching)"
    if [ ${#dns_only_domains[@]} -gt 0 ]; then
        echo "  Cloudflare DNS:    ${#dns_only_domains[@]} (gray cloud - DNS only)"
    fi
    echo "  Direct:            ${#direct_domains[@]} (not using Cloudflare)"
    if [ ${#nxdomain_domains[@]} -gt 0 ]; then
        echo "  NXDOMAIN:          ${#nxdomain_domains[@]} (don't resolve)"
    fi
    if [ ${#unknown_domains[@]} -gt 0 ]; then
        echo "  Unknown:           ${#unknown_domains[@]}"
    fi
    echo ""

    press_enter
}

check_single_domain() {
    print_banner "Check Single Domain"

    echo ""
    read -p "Enter domain name to check: " domain

    if [ -z "$domain" ]; then
        print_error "No domain entered"
        press_enter
        return 1
    fi

    echo ""
    echo "Checking: $domain"
    echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
    echo ""

    # Check if domain resolves
    if ! domain_resolves "$domain"; then
        print_error "✗ Domain does not resolve (NXDOMAIN)"
        echo ""
        echo "This domain has no DNS A or AAAA records."
        echo "It may be misconfigured, deleted, or not yet propagated."
        echo ""
        press_enter
        return 1
    fi

    # Check nameservers
    print_info "1. Nameserver Check:"
    local ns_result=$(check_nameservers "$domain")
    local nameservers=$(dig +short NS "$domain" 2>/dev/null)

    echo "   Nameservers:"
    if [ -n "$nameservers" ]; then
        echo "$nameservers" | sed 's/^/     /'
    else
        echo "     (none found)"
    fi
    echo "   Status: $ns_result"
    echo ""

    # Check IP address
    print_info "2. IP Address Check:"
    local ip=$(dig +short A "$domain" 2>/dev/null | head -1)

    if [ -n "$ip" ]; then
        echo "   IP: $ip"
        local ip_result=$(check_ip_address "$domain")
        echo "   Status: $ip_result"
    else
        echo "   (no A record found)"
    fi
    echo ""

    # Check HTTP headers
    print_info "3. HTTP Header Check:"
    echo "   Testing: http://$domain"
    local http_result=$(check_http_headers "$domain")
    echo "   Status: $http_result"

    # Get location if using Cloudflare
    if [ "$http_result" = "CLOUDFLARE" ]; then
        local location=$(get_cloudflare_location "$domain")
        echo "   Datacenter: $location"
    fi
    echo ""

    # Final result
    echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
    echo ""
    local final_result=$(detect_cloudflare "$domain")

    case "$final_result" in
        "CLOUDFLARE")
            local location=$(get_cloudflare_location "$domain")
            if [ "$location" != "N/A" ]; then
                print_success "✓ $domain is PROXIED through Cloudflare (Datacenter: $location)"
            else
                print_success "✓ $domain is PROXIED through Cloudflare"
            fi
            echo ""
            echo "  🔶 Orange Cloud: Traffic goes through Cloudflare CDN"
            echo "  Benefits: Caching, DDoS protection, firewall, etc."
            ;;
        "DNS-ONLY")
            print_warning "○ $domain uses Cloudflare DNS-ONLY (Gray Cloud)"
            echo ""
            echo "  ☁️  Traffic goes DIRECT to your server (not proxied)"
            echo "  Using: Cloudflare nameservers for DNS management"
            echo "  NOT using: CDN caching, DDoS protection, firewall"
            echo ""
            echo "  💡 Tip: To enable full Cloudflare protection, set to 'Proxied'"
            echo "     (orange cloud) in your Cloudflare DNS settings."
            ;;
        "DIRECT")
            print_info "• $domain is NOT using Cloudflare"
            ;;
        *)
            print_warning "? Could not determine Cloudflare status for $domain"
            ;;
    esac

    echo ""
    press_enter
}

show_info() {
    print_banner "About Cloudflare Detection"

    echo ""
    echo "How Detection Works:"
    echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
    echo ""
    echo "This tool checks three indicators to detect Cloudflare:"
    echo ""
    echo "  1. Nameservers:"
    echo "     Checks if domain uses Cloudflare nameservers"
    echo "     (e.g., ns1.cloudflare.com)"
    echo ""
    echo "  2. IP Address:"
    echo "     Checks if A record points to Cloudflare IP range"
    echo "     (e.g., 104.16.x.x, 172.64.x.x, etc.)"
    echo ""
    echo "  3. HTTP Headers:"
    echo "     Checks for CF-RAY header in HTTP response"
    echo "     (definitive proof of Cloudflare proxy)"
    echo ""
    echo "  4. Datacenter Location:"
    echo "     Extracts IATA airport code from CF-RAY header"
    echo "     (e.g., ORD=Chicago, LAX=Los Angeles, LHR=London)"
    echo ""
    echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
    echo ""
    echo "Why This Matters:"
    echo ""
    echo "  • Cloudflare domains hide origin server IP"
    echo "  • Useful for cache debugging (Cloudflare vs Varnish)"
    echo "  • Important for firewall/security configuration"
    echo "  • Helps identify which domains get CDN benefits"
    echo "  • Datacenter location useful for debugging regional outages"
    echo ""
    echo "Common Datacenter Codes:"
    echo "  ORD - Chicago    |  LAX - Los Angeles  |  IAD - Virginia"
    echo "  DFW - Dallas     |  ATL - Atlanta      |  SEA - Seattle"
    echo "  LHR - London     |  FRA - Frankfurt    |  AMS - Amsterdam"
    echo "  SYD - Sydney     |  SIN - Singapore    |  NRT - Tokyo"
    echo ""
    press_enter
}

################################################################################
# Main Menu
################################################################################

show_menu() {
    clear
    print_banner "Cloudflare Domain Detector"

    echo ""
    echo -e "${BOLD}Scan Options:${NC}"
    echo ""
    echo "  1) Scan All Domains       - Check all domains on this server"
    echo "  2) Check Single Domain    - Detailed check for one domain"
    echo "  3) About / How It Works   - Detection methodology"
    echo ""
    echo "  0) Back to Website Menu"
    echo ""
    echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
    echo -n "Select option: "
}

main() {
    while true; do
        show_menu
        read -r choice

        case $choice in
            1) scan_all_domains ;;
            2) check_single_domain ;;
            3) show_info ;;
            0)
                clear
                exit 0
                ;;
            *)
                echo ""
                print_error "Invalid option"
                sleep 1
                ;;
        esac
    done
}

# Run main menu
main
