import puppeteer, { Browser, Page, BrowserContext } from 'puppeteer-core'
import { promises as fs } from 'fs'
import * as path from 'path'

export interface TradingViewCredentials {
  email: string
  password: string
}

// Environment variables fallback
const TRADINGVIEW_EMAIL = process.env.TRADINGVIEW_EMAIL
const TRADINGVIEW_PASSWORD = process.env.TRADINGVIEW_PASSWORD

// Utility function to replace Puppeteer's waitForTimeout
const sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms))

// Helper function to check if element is visible using Puppeteer APIs
async function isElementVisible(page: Page, selector: string, timeout: number = 1000): Promise<boolean> {
  try {
    await page.waitForSelector(selector, { timeout, visible: true })
    return true
  } catch {
    return false
  }
}

// Helper function to get elements using Puppeteer APIs
async function getElements(page: Page, selector: string) {
  return await page.$$(selector)
}

export interface NavigationOptions {
  symbol?: string // e.g., 'SOLUSD', 'BTCUSD'
  timeframe?: string // e.g., '5', '15', '1H'
  waitForChart?: boolean
}

// Session persistence configuration
const SESSION_DATA_DIR = path.join(process.cwd(), '.tradingview-session')
const COOKIES_FILE = path.join(SESSION_DATA_DIR, 'cookies.json')
const SESSION_STORAGE_FILE = path.join(SESSION_DATA_DIR, 'session-storage.json')

export class TradingViewAutomation {
  private browser: Browser | null = null
  private page: Page | null = null
  private isAuthenticated: boolean = false
  private static instance: TradingViewAutomation | null = null
  private initPromise: Promise<void> | null = null
  private operationLock: boolean = false
  private lastRequestTime = 0
  private requestCount = 0
  private sessionFingerprint: string | null = null
  private humanBehaviorEnabled = true
  private lastMousePosition = { x: 0, y: 0 }
  private loginAttempts = 0
  private maxLoginAttempts = 3

  // Singleton pattern to prevent multiple browser instances
  static getInstance(): TradingViewAutomation {
    if (!TradingViewAutomation.instance) {
      TradingViewAutomation.instance = new TradingViewAutomation()
    }
    return TradingViewAutomation.instance
  }

  /**
   * Acquire operation lock to prevent concurrent operations
   */
  private async acquireOperationLock(timeout = 30000): Promise<void> {
    const startTime = Date.now()
    while (this.operationLock) {
      if (Date.now() - startTime > timeout) {
        throw new Error('Operation lock timeout - another operation is in progress')
      }
      await new Promise(resolve => setTimeout(resolve, 100))
    }
    this.operationLock = true
  }

  /**
   * Release operation lock
   */
  private releaseOperationLock(): void {
    this.operationLock = false
  }

  async init(): Promise<void> {
    // Acquire operation lock
    await this.acquireOperationLock()
    
    try {
      // Prevent multiple initialization calls
      if (this.initPromise) {
        console.log('🔄 Browser initialization already in progress, waiting...')
        return this.initPromise
      }

      if (this.browser && !this.browser.isConnected()) {
        console.log('🔄 Browser disconnected, cleaning up...')
        await this.forceCleanup()
      }

      if (this.browser) {
        console.log('SUCCESS: Browser already initialized and connected')
        return
      }

      this.initPromise = this._doInit()
      try {
        await this.initPromise
      } finally {
        this.initPromise = null
      }
    } finally {
      this.releaseOperationLock()
    }
  }

  private async _doInit(): Promise<void> {
    console.log('🚀 Initializing TradingView automation with session persistence...')
    
    // Ensure session directory exists
    await fs.mkdir(SESSION_DATA_DIR, { recursive: true })
    
    // Use a random port to avoid conflicts
    const debugPort = 9222 + Math.floor(Math.random() * 1000)
    
    try {
      this.browser = await puppeteer.launch({
        headless: true, // Must be true for Docker containers
        executablePath: process.env.PUPPETEER_EXECUTABLE_PATH || process.env.CHROME_PATH || '/usr/bin/chromium',
        timeout: 60000, // Reduce timeout to 60 seconds
        args: [
          '--no-sandbox',
          '--disable-setuid-sandbox',
          '--disable-dev-shm-usage',
          '--disable-accelerated-2d-canvas',
          '--no-first-run',
          '--no-zygote',
          '--disable-gpu',
          '--disable-web-security',
          '--disable-features=VizDisplayCompositor',
          '--disable-background-timer-throttling',
          '--disable-backgrounding-occluded-windows',
          '--disable-renderer-backgrounding',
          '--disable-features=TranslateUI',
          '--disable-ipc-flooding-protection',
          '--disable-extensions',
          '--disable-default-apps',
          '--disable-sync',
          '--metrics-recording-only',
          '--safebrowsing-disable-auto-update',
          '--disable-component-extensions-with-background-pages',
          '--disable-background-networking',
          '--disable-software-rasterizer',
          `--remote-debugging-port=${debugPort}`,
          // Additional args to reduce captcha detection
          '--disable-blink-features=AutomationControlled',
          '--disable-features=VizDisplayCompositor,VizHitTestSurfaceLayer',
          '--disable-features=ScriptStreaming',
          '--user-agent=Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'
        ]
      })
    } catch (error) {
      console.error('ERROR: Failed to launch browser:', error)
      // Cleanup any partial state
      await this.forceCleanup()
      throw new Error('Failed to launch browser: ' + error)
    }
    
    if (!this.browser) {
      throw new Error('Failed to launch browser')
    }
    
    // Load saved session if available (Puppeteer doesn't use contexts like Playwright)
    await this.loadSession()
    
    this.page = await this.browser.newPage()
    
    if (!this.page) {
      throw new Error('Failed to create new page')
    }

    // Set viewport for page
    await this.page.setViewport({ width: 1920, height: 1080 })

    // Set user agent
    await this.page.setUserAgent('Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36')

    // Set extra HTTP headers to appear more human-like
    await this.page.setExtraHTTPHeaders({
      'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7',
      'Accept-Language': 'en-US,en;q=0.9,de;q=0.8',
      'Accept-Encoding': 'gzip, deflate, br',
      'Cache-Control': 'max-age=0',
      'Sec-Ch-Ua': '"Not_A Brand";v="8", "Chromium";v="120", "Google Chrome";v="120"',
      'Sec-Ch-Ua-Mobile': '?0',
      'Sec-Ch-Ua-Platform': '"Linux"',
      'Sec-Fetch-Dest': 'document',
      'Sec-Fetch-Mode': 'navigate',
      'Sec-Fetch-Site': 'none',
      'Sec-Fetch-User': '?1',
      'Upgrade-Insecure-Requests': '1',
      'Dnt': '1'
    })

    // Add enhanced stealth measures to reduce bot detection
    await this.page.evaluateOnNewDocument(() => {
      // Remove webdriver property completely
      Object.defineProperty(navigator, 'webdriver', {
        get: () => undefined,
      });
      
      // Enhanced plugins simulation
      Object.defineProperty(navigator, 'plugins', {
        get: () => {
          const plugins = [
            { name: 'Chrome PDF Plugin', filename: 'internal-pdf-viewer' },
            { name: 'Chrome PDF Viewer', filename: 'mhjfbmdgcfjbbpaeojofohoefgiehjai' },
            { name: 'Native Client', filename: 'internal-nacl-plugin' }
          ];
          plugins.length = 3;
          return plugins;
        },
      });
      
      // Enhanced language simulation
      Object.defineProperty(navigator, 'languages', {
        get: () => ['en-US', 'en', 'de'],
      });
      
      // Mock hardware concurrency
      Object.defineProperty(navigator, 'hardwareConcurrency', {
        get: () => 8,
      });
      
      // Mock device memory
      Object.defineProperty(navigator, 'deviceMemory', {
        get: () => 8,
      });
      
      // Mock connection
      Object.defineProperty(navigator, 'connection', {
        get: () => ({
          effectiveType: '4g',
          rtt: 50,
          downlink: 10
        }),
      });
      
      // Override permissions API with realistic responses
      const originalQuery = window.navigator.permissions.query;
      window.navigator.permissions.query = (parameters: any) => {
        const permission = parameters.name;
        let state = 'prompt';
        
        if (permission === 'notifications') {
          state = 'default';
        } else if (permission === 'geolocation') {
          state = 'prompt';
        }
        
        return Promise.resolve({
          state: state,
          name: permission,
          onchange: null,
          addEventListener: () => {},
          removeEventListener: () => {},
          dispatchEvent: () => false
        } as PermissionStatus);
      };
      
      // Mock WebGL fingerprinting resistance
      const getParameter = WebGLRenderingContext.prototype.getParameter;
      WebGLRenderingContext.prototype.getParameter = function(parameter: any) {
        if (parameter === 37445) { // UNMASKED_VENDOR_WEBGL
          return 'Intel Inc.';
        }
        if (parameter === 37446) { // UNMASKED_RENDERER_WEBGL
          return 'Intel Iris OpenGL Engine';
        }
        return getParameter.call(this, parameter);
      };
      
      // Mock screen properties to be consistent
      Object.defineProperties(screen, {
        width: { get: () => 1920 },
        height: { get: () => 1080 },
        availWidth: { get: () => 1920 },
        availHeight: { get: () => 1040 },
        colorDepth: { get: () => 24 },
        pixelDepth: { get: () => 24 }
      });
      
      // Remove automation detection markers
      delete (window as any).chrome.runtime.onConnect;
      delete (window as any).chrome.runtime.onMessage;
      
      // Mock battery API
      Object.defineProperty(navigator, 'getBattery', {
        get: () => () => Promise.resolve({
          charging: true,
          chargingTime: 0,
          dischargingTime: Infinity,
          level: 1
        }),
      });
    })
    
    console.log('SUCCESS: Browser and session initialized successfully')
  }

  /**
   * Check if user is already logged in to TradingView
   */
  async checkLoginStatus(): Promise<boolean> {
    if (!this.page) return false

    try {
      console.log('CHECKING: Checking login status...')
      
      // Navigate to TradingView if not already there
      const currentUrl = await this.page.url()
      if (!currentUrl.includes('tradingview.com')) {
        console.log('📄 Navigating to TradingView...')
        await this.page.goto('https://www.tradingview.com', {
          waitUntil: 'domcontentloaded',
          timeout: 30000
        })
        
        // Restore session storage after navigation
        await this.restoreSessionStorage()
        
        // Wait for page to settle
        await new Promise(resolve => setTimeout(resolve, 5000))
      }

      // Take a debug screenshot to see the current state
      await this.takeDebugScreenshot('login_status_check')

      // Enhanced login detection with multiple strategies
      console.log('CHECKING: Strategy 1: Checking for user account indicators...')
      
      // Strategy 1: Look for user account elements (more comprehensive)
      const userAccountSelectors = [
        // User menu and profile elements
        '[data-name="header-user-menu"]',
        '[data-name="user-menu"]', 
        '.tv-header__user-menu-button:not(.tv-header__user-menu-button--anonymous)',
        '.tv-header__user-menu',
        '.js-header-user-menu-button',
        
        // Account/profile indicators
        '[data-name="account-menu"]',
        '[data-testid="header-user-menu"]',
        '.tv-header__dropdown-toggle',
        
        // Watchlist indicators (user-specific)
        '[data-name="watchlist-button"]',
        '.tv-header__watchlist-button',
        '.tv-watchlist-container',
        
        // Personal layout elements
        'button:has-text("M")', // Watchlist "M" button
        '[data-name="watchlist-dropdown"]',
        
        // Pro/subscription indicators
        '.tv-header__pro-button',
        '[data-name="go-pro-button"]'
      ]

      let foundUserElement = false
      for (const selector of userAccountSelectors) {
        try {
          if (await isElementVisible(this.page, selector, 1500)) {
            console.log("SUCCESS: Found user account element: " + selector)
            foundUserElement = true
            break
          }
        } catch (e) {
          continue
        }
      }

      // Strategy 2: Check for sign-in/anonymous indicators (should NOT be present if logged in)
      console.log('CHECKING: Strategy 2: Checking for anonymous/sign-in indicators...')
      
      const anonymousSelectors = [
        // Sign in buttons/links
        'a[href*="signin"]',
        'a[href*="/accounts/signin"]',
        'button:has-text("Sign in")',
        'button:has-text("Log in")',
        'a:has-text("Sign in")',
        'a:has-text("Log in")',
        
        // Anonymous user indicators
        '.tv-header__user-menu-button--anonymous',
        '[data-name="header-user-menu-sign-in"]',
        '.tv-header__sign-in',
        
        // Guest mode indicators
        'text="Continue as guest"',
        'text="Sign up"',
        'button:has-text("Sign up")'
      ]

      let foundAnonymousElement = false
      for (const selector of anonymousSelectors) {
        try {
          if (await isElementVisible(this.page, selector, 1500)) {
            console.log(`ERROR: Found anonymous indicator: ${selector} - not logged in`)
            foundAnonymousElement = true
            break
          }
        } catch (e) {
          continue
        }
      }

      // Strategy 3: Check page URL patterns for authentication
      console.log('CHECKING: Strategy 3: Checking URL patterns...')
      
      const url = await this.page.url()
      const isOnLoginPage = url.includes('/accounts/signin') || 
                           url.includes('/signin') || 
                           url.includes('/login')
      
      if (isOnLoginPage) {
        console.log("ERROR: Currently on login page: " + url)
        this.isAuthenticated = false
        return false
      }

      // Strategy 4: Check for authentication-specific cookies
      console.log('CHECKING: Strategy 4: Checking authentication cookies...')
      
      let hasAuthCookies = false
      if (this.context) {
        const cookies = await this.context.cookies()
        const authCookieNames = [
          'sessionid',
          'auth_token', 
          'user_token',
          'session_token',
          'authentication',
          'logged_in',
          'tv_auth',
          'tradingview_auth'
        ]
        
        for (const cookie of cookies) {
          if (authCookieNames.some(name => cookie.name.toLowerCase().includes(name.toLowerCase()))) {
            console.log("🍪 Found potential auth cookie: " + cookie.name)
            hasAuthCookies = true
            break
          }
        }
        
        console.log('DATA: Total cookies: ' + cookies.length + ', Auth cookies found: ' + hasAuthCookies)
      }

      // Strategy 5: Try to detect personal elements by checking page content
      console.log('CHECKING: Strategy 5: Checking for personal content...')
      
      let hasPersonalContent = false
      try {
        // Look for elements that indicate a logged-in user
        const personalContentSelectors = [
          // Watchlist with custom symbols
          '.tv-screener-table',
          '.tv-widget-watch-list',
          
          // Personal layout elements
          '.layout-with-border-radius',
          '.tv-chart-view',
          
          // Settings/customization elements available only to logged-in users
          '[data-name="chart-settings"]',
          '[data-name="chart-properties"]'
        ]
        
        for (const selector of personalContentSelectors) {
          try {
            if (await isElementVisible(this.page, selector, 1000)) {
              console.log("SUCCESS: Found personal content: " + selector)
              hasPersonalContent = true
              break
            }
          } catch (e) {
            continue
          }
        }
        
        // Additional check: see if we can access account-specific features
        const pageText = await this.page.textContent('body') || ''
        if (pageText.includes('My Watchlist') || 
            pageText.includes('Portfolio') || 
            pageText.includes('Alerts') ||
            pageText.includes('Account')) {
          console.log('SUCCESS: Found account-specific text content')
          hasPersonalContent = true
        }
        
      } catch (e) {
        console.log('WARNING: Error checking personal content:', e)
      }

      // Final decision logic
      console.log('DATA: Login detection summary:')
      console.log("   User elements found: " + foundUserElement)
      console.log("   Anonymous elements found: " + foundAnonymousElement)
      console.log("   On login page: " + isOnLoginPage)
      console.log("   Has auth cookies: " + hasAuthCookies)
      console.log("   Has personal content: " + hasPersonalContent)

      // Determine login status based on multiple indicators
      const isLoggedIn = (foundUserElement || hasPersonalContent || hasAuthCookies) && 
                        !foundAnonymousElement && 
                        !isOnLoginPage

      if (isLoggedIn) {
        console.log('SUCCESS: User appears to be logged in')
        this.isAuthenticated = true
        return true
      } else {
        console.log('ERROR: User appears to be NOT logged in')
        this.isAuthenticated = false
        return false
      }

    } catch (error) {
      console.error('ERROR: Error checking login status:', error)
      await this.takeDebugScreenshot('login_status_error')
      this.isAuthenticated = false
      return false
    }
  }

  async login(credentials?: TradingViewCredentials): Promise<boolean> {
    if (!this.page) throw new Error('Page not initialized')

    // Use provided credentials or fall back to environment variables
    const email = credentials?.email || TRADINGVIEW_EMAIL
    const password = credentials?.password || TRADINGVIEW_PASSWORD

    if (!email || !password) {
      throw new Error('TradingView credentials not provided. Either pass credentials parameter or set TRADINGVIEW_EMAIL and TRADINGVIEW_PASSWORD in .env file')
    }

    try {
      // Check if already logged in with enhanced detection
      const loggedIn = await this.checkLoginStatus()
      if (loggedIn) {
        console.log('SUCCESS: Already logged in, skipping login steps')
        return true
      }

      console.log('🔐 Starting login process...')
      
      // Clear any existing session first to ensure clean login
      if (this.context) {
        try {
          await this.context.clearCookies()
          console.log('🧹 Cleared existing cookies for clean login')
        } catch (e) {
          console.log('WARNING: Could not clear cookies:', e)
        }
      }
      
      // Navigate to login page with multiple attempts
      console.log('📄 Navigating to TradingView login page...')
      
      const loginUrls = [
        'https://www.tradingview.com/accounts/signin/',
        'https://www.tradingview.com/'
      ]
      
      let loginPageLoaded = false
      for (const url of loginUrls) {
        try {
          console.log("🔄 Trying URL: " + url)
          await this.page.goto(url, {
            waitUntil: 'domcontentloaded',
            timeout: 30000
          })
          
          // Wait for page to settle
          await sleep(3000)
          
          const currentUrl = await this.page.url()
          console.log("📍 Current URL after navigation: " + currentUrl)
          
          if (currentUrl.includes('signin') || currentUrl.includes('login')) {
            loginPageLoaded = true
            break
          } else if (url === 'https://www.tradingview.com/') {
            // Try to find and click sign in button
            console.log('CHECKING: Looking for Sign In button on main page...')
            
            const signInSelectors = [
              'a[href*="signin"]:visible',
              'a[href*="/accounts/signin/"]:visible',
              'button:has-text("Sign in"):visible',
              'a:has-text("Sign in"):visible',
              '.tv-header__user-menu-button--anonymous:visible',
              '[data-name="header-user-menu-sign-in"]:visible'
            ]
            
            for (const selector of signInSelectors) {
              try {
                console.log("TARGET: Trying sign in selector: " + selector)
                const element = this.page.locator(selector).first()
                if (await element.isVisible({ timeout: 3000 })) {
                  await element.click()
                  console.log("SUCCESS: Clicked sign in button: " + selector)
                  
                  // Wait for navigation to login page
                  await sleep(3000)
                  
                  const newUrl = await this.page.url()
                  if (newUrl.includes('signin') || newUrl.includes('login')) {
                    console.log('SUCCESS: Successfully navigated to login page')
                    loginPageLoaded = true
                    break
                  }
                }
              } catch (e) {
                console.log("ERROR: Sign in selector failed: " + selector)
                continue
              }
            }
            
            if (loginPageLoaded) break
          }
          
        } catch (e) {
          console.log(`ERROR: Failed to load ${url}:`, e)
          continue
        }
      }
      
      if (!loginPageLoaded) {
        throw new Error('Could not reach TradingView login page')
      }

      // Take screenshot of login page
      await this.takeDebugScreenshot('login_page_loaded')

      // Wait for login form to be ready
      console.log('⏳ Waiting for login form to be ready...')
      await sleep(5000)

      // CRITICAL: Look for and click "Email" button if present (TradingView uses this pattern)
      console.log('CHECKING: Looking for Email login option...')
      
      // First try Playwright locator approach
      const emailTriggers = [
        'button:has-text("Email")',
        'button:has-text("email")', 
        'text="Email"',
        'text="Continue with email"',
        'text="Sign in with email"',
        '[data-name="email"]',
        '[data-testid="email-button"]'
      ]
      
      let emailFormVisible = false
      for (const trigger of emailTriggers) {
        try {
          const element = this.page.locator(trigger).first()
          if (await element.isVisible({ timeout: 2000 })) {
            console.log("TARGET: Found email trigger: " + trigger)
            await element.click()
            console.log('SUCCESS: Clicked email trigger')
            
            // Wait for email form to appear
            await sleep(3000)
            emailFormVisible = true
            break
          }
        } catch (e) {
          continue
        }
      }

      // If locator approach failed, use manual button enumeration (like old working code)
      if (!emailFormVisible) {
        console.log('🔄 Locator approach failed, trying manual button search...')
        try {
          // Wait for buttons to be available
          await this.page.waitForSelector('button', { timeout: 10000 })
          
          // Get all buttons and check their text content
          const buttons = await this.page.locator('button').all()
          console.log(`CHECKING: Found ${buttons.length} buttons to check`)
          
          for (let i = 0; i < buttons.length; i++) {
            try {
              const button = buttons[i]
              if (await button.isVisible({ timeout: 1000 })) {
                const text = await button.textContent() || ''
                const trimmedText = text.trim().toLowerCase()
                
                console.log(`INFO: Button ${i + 1}: "${trimmedText}"`)
                
                if (trimmedText.includes('email') || 
                    trimmedText.includes('continue with email') ||
                    trimmedText.includes('sign in with email')) {
                  console.log('Found email button: ' + trimmedText)
                  await button.click()
                  console.log('SUCCESS: Clicked email button')
                  
                  // Wait for email form to appear
                  await sleep(3000)
                  emailFormVisible = true
                  break
                }
              }
            } catch (e) {
              console.log('WARNING: Error checking button ' + (i + 1) + ':', e)
              continue
            }
          }
        } catch (e) {
          console.log('ERROR: Manual button search failed:', e)
        }
      }
      
      // Check if email input is now visible
      if (!emailFormVisible) {
        // Look for email input directly (might already be visible)
        const emailInputSelectors = [
          'input[type="email"]',
          'input[name*="email"]',
          'input[name*="username"]',
          'input[name="username"]',  // TradingView often uses this
          'input[placeholder*="email" i]',
          'input[placeholder*="username" i]'
        ]
        
        for (const selector of emailInputSelectors) {
          try {
            if (await isElementVisible(this.page, selector, 1000)) {
              console.log('SUCCESS: Email input already visible: ' + selector)
              emailFormVisible = true
              break
            }
          } catch (e) {
            continue
          }
        }
      }
      
      if (!emailFormVisible) {
        await this.takeDebugScreenshot('no_email_form')
        
        // Additional debugging: show what elements are available
        const availableElements = await this.page.evaluate(() => {
          const buttons = Array.from(document.querySelectorAll('button')).map(btn => btn.textContent?.trim()).filter(Boolean)
          const inputs = Array.from(document.querySelectorAll('input')).map(input => ({
            type: input.type,
            name: input.name,
            placeholder: input.placeholder
          }))
          const forms = Array.from(document.querySelectorAll('form')).length
          return { buttons, inputs, forms }
        })
        
        console.log('CHECKING: Available elements:', JSON.stringify(availableElements, null, 2))
        throw new Error('Could not find or activate email login form')
      }

      // Find and fill email field
      console.log('📧 Looking for email input field...')
      
      const emailSelectors = [
        'input[name="username"]',  // TradingView commonly uses this
        'input[type="email"]',
        'input[name="email"]', 
        'input[name="id_username"]',
        'input[placeholder*="email" i]',
        'input[placeholder*="username" i]',
        'input[autocomplete="username"]',
        'input[autocomplete="email"]',
        'form input[type="text"]:not([type="password"])'
      ]

      let emailInput = null
      for (const selector of emailSelectors) {
        try {
          console.log('CHECKING: Trying email selector: ' + selector)
          if (await isElementVisible(this.page, selector, 2000)) {
            emailInput = selector
            console.log('SUCCESS: Found email input: ' + selector)
            break
          }
        } catch (e) {
          continue
        }
      }

      if (!emailInput) {
        // Try manual search like the old code
        console.log('🔄 Selector approach failed, trying manual input search...')
        try {
          const inputs = await this.page.locator('input').all()
          console.log(`CHECKING: Found ${inputs.length} inputs to check`)
          
          for (let i = 0; i < inputs.length; i++) {
            try {
              const input = inputs[i]
              if (await input.isVisible({ timeout: 1000 })) {
                const type = await input.getAttribute('type') || ''
                const name = await input.getAttribute('name') || ''
                const placeholder = await input.getAttribute('placeholder') || ''
                
                console.log(`INFO: Input ${i + 1}: type="${type}" name="${name}" placeholder="${placeholder}"`)
                
                if (type === 'email' || 
                    name.toLowerCase().includes('email') || 
                    name.toLowerCase().includes('username') ||
                    placeholder.toLowerCase().includes('email') ||
                    placeholder.toLowerCase().includes('username')) {
                  console.log(`TARGET: Found email input manually: ${name || type || placeholder}`)
                  emailInput = `input:nth-of-type(${i + 1})`
                  break
                }
              }
            } catch (e) {
              console.log('WARNING: Error checking input ' + (i + 1) + ':', e)
              continue
            }
          }
        } catch (e) {
          console.log('ERROR: Manual input search failed:', e)
        }
      }

      if (!emailInput) {
        await this.takeDebugScreenshot('no_email_input')
        throw new Error('Could not find email input field')
      }

      // Fill email
      console.log('📧 Filling email field...')
      await this.page.fill(emailInput, email)
      console.log('SUCCESS: Email filled')

      // Find and fill password field
      console.log('🔑 Looking for password input field...')
      
      const passwordSelectors = [
        'input[type="password"]',
        'input[name="password"]',
        'input[name="id_password"]',
        'input[placeholder*="password" i]',
        'input[autocomplete="current-password"]'
      ]

      let passwordInput = null
      for (const selector of passwordSelectors) {
        try {
          console.log("CHECKING: Trying password selector: " + selector)
          if (await isElementVisible(this.page, selector, 2000)) {
            passwordInput = selector
            console.log("SUCCESS: Found password input: " + selector)
            break
          }
        } catch (e) {
          continue
        }
      }

      if (!passwordInput) {
        await this.takeDebugScreenshot('no_password_input')
        throw new Error('Could not find password input field')
      }

      // Fill password
      console.log('🔑 Filling password field...')
      await this.page.fill(passwordInput, password)
      console.log('SUCCESS: Password filled')

      // Handle potential captcha
      console.log('🤖 Checking for captcha...')
      try {
        // Look for different types of captcha and robot confirmation
        const captchaSelectors = [
          'iframe[src*="recaptcha"]',
          'iframe[src*="captcha"]',
          '.recaptcha-checkbox',
          '[data-testid="captcha"]',
          '.captcha-container',
          'text="Please confirm that you are not a robot"',
          'text="Are you human?"',
          'text="Please verify you are human"',
          'text="Security check"',
          '.tv-dialog__error:has-text("robot")',
          '.alert:has-text("robot")',
          '.error:has-text("robot")'
        ]
        
        let captchaFound = false
        let captchaType = ''
        for (const selector of captchaSelectors) {
          try {
            if (await isElementVisible(this.page, selector, 2000)) {
              console.log("🤖 Captcha/Robot check detected: " + selector)
              captchaFound = true
              captchaType = selector
              break
            }
          } catch (e) {
            continue
          }
        }
        
        if (captchaFound) {
          console.log('WARNING: CAPTCHA/Robot verification detected!')
          console.log('🚫 This indicates TradingView has flagged this as automated behavior.')
          console.log('� In a Docker environment, automated captcha solving is not feasible.')
          
          // Take a screenshot for debugging
          await this.takeDebugScreenshot('captcha_detected')
          
          // Instead of waiting, we should fail fast and suggest alternatives
          console.log('ERROR: Cannot proceed with automated login due to captcha protection.')
          console.log('🔧 Possible solutions:')
          console.log('   1. Use a different IP address or VPN')
          console.log('   2. Wait some time before retrying (rate limiting)')
          console.log('   3. Use session persistence from a manually authenticated browser')
          console.log('   4. Contact TradingView support if this persists')
          
          // Mark captcha detection for future reference
          await this.markCaptchaDetected()
          
          // Return false immediately instead of waiting
          throw new Error(`Captcha detected (${captchaType}) - automated login blocked`)
        }
        
      } catch (captchaError: any) {
        if (captchaError.message.includes('Captcha detected')) {
          throw captchaError
        }
        console.log('WARNING: Captcha check failed:', captchaError?.message)
      }

      // Find and click submit button
      console.log('🔘 Looking for submit button...')
      
      const submitSelectors = [
        'button[type="submit"]',
        'input[type="submit"]',
        'button:has-text("Sign in")',
        'button:has-text("Log in")',
        'button:has-text("Login")', 
        'button:has-text("Sign In")',
        '.tv-button--primary',
        'form button:not([type="button"])',
        '[data-testid="signin-button"]',
        '[data-testid="login-button"]'
      ]

      let submitButton = null
      for (const selector of submitSelectors) {
        try {
          console.log("CHECKING: Trying submit selector: " + selector)
          if (await isElementVisible(this.page, selector, 2000)) {
            submitButton = selector
            console.log("SUCCESS: Found submit button: " + selector)
            break
          }
        } catch (e) {
          continue
        }
      }

      if (!submitButton) {
        // Try manual search like the old code
        console.log('🔄 Selector approach failed, trying manual button search for submit...')
        try {
          const buttons = await this.page.locator('button').all()
          console.log(`CHECKING: Found ${buttons.length} buttons to check for submit`)
          
          for (let i = 0; i < buttons.length; i++) {
            try {
              const button = buttons[i]
              if (await button.isVisible({ timeout: 1000 })) {
                const text = (await button.textContent() || '').toLowerCase()
                const type = await button.getAttribute('type') || ''
                
                console.log(`INFO: Submit Button ${i + 1}: "${text}" type="${type}"`)
                
                if (type === 'submit' || 
                    text.includes('sign in') || 
                    text.includes('login') ||
                    text.includes('submit')) {
                  console.log(`TARGET: Found submit button manually: "${text}"`)
                  submitButton = `button:nth-of-type(${i + 1})`
                  break
                }
              }
            } catch (e) {
              continue
            }
          }
        } catch (e) {
          console.log('ERROR: Manual submit button search failed:', e)
        }
      }

      if (!submitButton) {
        await this.takeDebugScreenshot('no_submit_button')
        throw new Error('Could not find submit button')
      }

      // Click submit button
      console.log('🖱️ Clicking submit button...')
      await this.page.click(submitButton)
      console.log('SUCCESS: Submit button clicked')

      // Wait for login to complete
      console.log('⏳ Waiting for login to complete...')
      
      try {
        // Wait for login completion without using waitForFunction (CSP violation)
        // Instead, check URL and elements periodically
        let attempts = 0
        let maxAttempts = 15 // Reduced to 15 seconds with 1 second intervals
        let loginDetected = false
        
        while (attempts < maxAttempts && !loginDetected) {
          await sleep(1000) // Wait 1 second
          attempts++
          
          console.log('Login check attempt ' + attempts + '/' + maxAttempts)
          
          // Check if we navigated away from login page
          const currentUrl = await this.page.url()
          console.log("📍 Current URL: " + currentUrl)
          const notOnLoginPage = !currentUrl.includes('/accounts/signin') && !currentUrl.includes('/signin')
          
          // Check for user-specific elements
          let hasUserElements = false
          try {
            const userElement = await this.page.locator(
              '[data-name="watchlist-button"], .tv-header__user-menu-button:not(.tv-header__user-menu-button--anonymous), [data-name="user-menu"]'
            ).first()
            hasUserElements = await userElement.isVisible({ timeout: 500 })
            if (hasUserElements) {
              console.log('SUCCESS: Found user-specific elements')
            }
          } catch (e) {
            // Element not found, continue checking
          }
          
          // Check for error messages
          try {
            const errorSelectors = [
              '.tv-dialog__error',
              '.error-message',
              '[data-testid="error"]',
              '.alert-danger'
            ]
            
            for (const selector of errorSelectors) {
              if (await isElementVisible(this.page, selector, 500)) {
                const errorText = await this.page.locator(selector).textContent()
                console.log('ERROR: Login error detected: ' + errorText)
                throw new Error('Login failed: ' + errorText)
              }
            }
          } catch (e) {
            if (e instanceof Error && e.message.includes('Login failed:')) {
              throw e
            }
            // Continue if just element not found
          }
          
          if (notOnLoginPage || hasUserElements) {
            loginDetected = true
            console.log('🎉 Navigation/elements suggest login success!')
            break
          }
        }
        
        if (!loginDetected) {
          throw new Error('Login verification timeout - no success indicators found')
        }
        
        // Additional wait for page to fully load
        await sleep(5000)
        
        // Verify login status with enhanced detection
        const loginSuccessful = await this.checkLoginStatus()
        
        if (loginSuccessful) {
          console.log('SUCCESS: Login verified successful!')
          this.isAuthenticated = true
          
          // Save session for future use
          await this.saveSession()
          console.log('💾 Session saved for future use')
          
          return true
        } else {
          console.log('ERROR: Login verification failed')
          await this.takeDebugScreenshot('login_verification_failed')
          return false
        }
        
      } catch (error) {
        console.error('ERROR: Login completion timeout or error:', error)
        await this.takeDebugScreenshot('login_timeout')
        return false
      }

    } catch (error) {
      console.error('ERROR: Login failed:', error)
      await this.takeDebugScreenshot('login_error')
      return false
    }
  }

  /**
   * Smart login that prioritizes session persistence to avoid captchas
   */
  async smartLogin(credentials?: TradingViewCredentials): Promise<boolean> {
    if (!this.page) throw new Error('Page not initialized')

    try {
      console.log('CHECKING: Attempting smart login with session persistence...')
      
      // First check if already logged in
      const alreadyLoggedIn = await this.checkLoginStatus()
      if (alreadyLoggedIn) {
        console.log('🎉 Already logged in to TradingView! Skipping login process.')
        await this.saveSession() // Save current session
        return true
      }

      console.log('🔐 Not logged in, checking session persistence options...')
      
      // Before attempting login, check if we have any saved session data
      const sessionInfo = await this.testSessionPersistence()
      console.log('DATA: Session persistence check:', sessionInfo)
      
      if (sessionInfo.cookiesCount > 0) {
        console.log('🍪 Found saved session data, attempting to restore...')
        
        // Try navigating to TradingView with saved session first
        try {
          await this.page.goto('https://www.tradingview.com/', {
            waitUntil: 'domcontentloaded',
            timeout: 30000
          })
          
          // Restore session storage after navigation
          await this.restoreSessionStorage()
          
          // Wait for page to settle and check login status
          await sleep(5000)
          
          const nowLoggedIn = await this.checkLoginStatus()
          if (nowLoggedIn) {
            console.log('SUCCESS: Session restoration successful! Login confirmed.')
            this.isAuthenticated = true
            await this.saveSession()
            return true
          } else {
            console.log('WARNING: Session restoration failed, saved session may be expired')
          }
        } catch (e) {
          console.log('ERROR: Session restoration attempt failed:', e)
        }
      }
      
      // Only attempt automated login if we don't have valid session data
      console.log('🤖 Attempting automated login...')
      try {
        const autoLoginSuccess = await this.login(credentials)
        
        if (autoLoginSuccess) {
          console.log('SUCCESS: Automated login successful! Saving session for future use.')
          this.isAuthenticated = true
          await this.saveSession()
          return true
        }
      } catch (loginError: any) {
        if (loginError.message.includes('Captcha detected')) {
          console.log('🚫 Captcha protection encountered during automated login.')
          console.log('💡 To resolve this issue:')
          console.log('   1. Clear any existing session data: docker-compose exec app rm -f /app/session_*.json')
          console.log('   2. Wait 1-2 hours before retrying (to clear rate limiting)')
          console.log('   3. Consider using a different IP address or VPN')
          console.log('   4. Manually log in to TradingView in a browser to establish a valid session')
          return false
        }
        throw loginError
      }
      
      console.log('ERROR: Automated login failed, this is likely due to captcha protection.')
      console.log('WARNING: In Docker environment, manual login is not practical.')
      console.log('� Checking if we can proceed with session persistence...')
      
      // Try to check if there are any existing valid session cookies
      const fallbackSessionInfo = await this.testSessionPersistence()
      if (fallbackSessionInfo.isValid) {
        console.log('SUCCESS: Found valid session data, attempting to use it...')
        try {
          // Navigate to main TradingView page to test session
          await this.page.goto('https://www.tradingview.com/', {
            waitUntil: 'domcontentloaded',
            timeout: 30000
          })
          
          await sleep(5000)
          
          const nowLoggedIn = await this.checkLoginStatus()
          if (nowLoggedIn) {
            console.log('SUCCESS: Session persistence worked! Login successful.')
            this.isAuthenticated = true
            await this.saveSession()
            return true
          }
        } catch (e) {
          console.log('ERROR: Session persistence test failed:', e)
        }
      }
      
      console.log('ERROR: All login methods failed. This may require manual intervention.')
      console.log('💡 To fix: Log in manually in a browser with the same credentials and restart the application.')
      return false
      
    } catch (error) {
      console.error('ERROR: Smart login failed:', error)
      return false
    }
  }

  async navigateToChart(options: NavigationOptions = {}): Promise<boolean> {
    if (!this.page) throw new Error('Page not initialized')

    try {
      // Throttle requests to avoid suspicious patterns
      await this.throttleRequests()
      
      // Validate session integrity before proceeding
      const sessionValid = await this.validateSessionIntegrity()
      if (!sessionValid) {
        console.log('WARNING: Session integrity compromised, may require re-authentication')
      }

      const { symbol = 'SOLUSD', timeframe = '5', waitForChart = true } = options

      console.log('Navigating to chart page...')
      
      // Perform human-like interactions before navigation
      await this.performHumanLikeInteractions()
      
      // Generate session fingerprint for tracking
      await this.generateSessionFingerprint()
      
      // Navigate to chart page with more flexible waiting strategy
      try {
        await this.page.goto('https://www.tradingview.com/chart/', {
          waitUntil: 'domcontentloaded', // More lenient than 'networkidle'
          timeout: 45000 // Increased timeout
        })
      } catch (error) {
        console.log('Standard navigation failed, trying fallback...')
        // Fallback: navigate without waiting for full network idle
        await this.page.goto('https://www.tradingview.com/chart/', {
          waitUntil: 'load',
          timeout: 30000
        })
      }
      
      // Human-like delay after navigation
      await this.humanDelay(2000, 4000)

      // Wait for chart to load
      if (waitForChart) {
        console.log('Waiting for chart container...')
        try {
          await this.page.waitForSelector('.chart-container, #tv_chart_container, .tv-layout', { timeout: 30000 })
          console.log('Chart container found')
        } catch (error) {
          console.log('Chart container not found with standard selectors, trying alternatives...')
          // Try alternative selectors for chart elements
          const chartSelectors = [
            '.chart-widget',
            '.tradingview-widget-container', 
            '[data-name="chart"]',
            'canvas',
            '.tv-chart'
          ]
          
          let chartFound = false
          for (const selector of chartSelectors) {
            try {
              await this.page.waitForSelector(selector, { timeout: 5000 })
              console.log("Chart found with selector: " + selector)
              chartFound = true
              break
            } catch (e) {
              continue
            }
          }
          
          if (!chartFound) {
            console.log('No chart container found, proceeding anyway...')
          }
        }
        
        // Additional wait for chart initialization with human-like delay
        await this.humanDelay(3000, 6000)
      }

      // Change symbol if not BTC
      if (symbol !== 'BTCUSD') {
        console.log(`Changing symbol to ${symbol}...`)
        await this.changeSymbol(symbol)
      }

      // Change timeframe if specified
      if (timeframe) {
        console.log(`Setting timeframe to ${timeframe}...`)
        await this.changeTimeframe(timeframe)
      }

      console.log(`Successfully navigated to ${symbol} chart with ${timeframe} timeframe`)
      return true

    } catch (error) {
      console.error('Navigation to chart failed:', error)
      await this.takeDebugScreenshot('navigation_failed')
      return false
    }
  }

  private async changeSymbol(symbol: string): Promise<void> {
    if (!this.page) return

    try {
      // Try multiple selectors for the symbol searcher
      const symbolSelectors = [
        '.tv-symbol-header__short-title',
        '.js-symbol-title',
        '[data-name="legend-source-title"]',
        '.tv-symbol-header',
        '.tv-chart-header__symbol'
      ]

      let symbolElement = null
      for (const selector of symbolSelectors) {
        try {
          await this.page.waitForSelector(selector, { timeout: 3000 })
          symbolElement = selector
          break
        } catch (e) {
          console.log(`Symbol selector ${selector} not found, trying next...`)
        }
      }

      if (!symbolElement) {
        throw new Error('Could not find symbol selector')
      }

      await this.page.click(symbolElement)

      // Wait for search input
      const searchSelectors = [
        'input[data-role="search"]',
        '.tv-dialog__body input',
        '.tv-symbol-search-dialog__input input',
        'input[placeholder*="Search"]'
      ]

      let searchInput = null
      for (const selector of searchSelectors) {
        try {
          await this.page.waitForSelector(selector, { timeout: 3000 })
          searchInput = selector
          break
        } catch (e) {
          console.log(`Search input selector ${selector} not found, trying next...`)
        }
      }

      if (!searchInput) {
        throw new Error('Could not find search input')
      }
      
      // Clear and type new symbol
      await this.page.fill(searchInput, symbol)
      
      // Wait a bit for search results
      await sleep(2000)
      
      // Try to click first result or press Enter
      const resultSelectors = [
        '.tv-screener-table__row',
        '.js-searchbar-suggestion',
        '.tv-symbol-search-dialog__item',
        '.tv-symbol-search-dialog__symbol'
      ]

      let clicked = false
      for (const selector of resultSelectors) {
        try {
          const firstResult = this.page.locator(selector).first()
          if (await firstResult.isVisible({ timeout: 2000 })) {
            await firstResult.click()
            clicked = true
            break
          }
        } catch (e) {
          console.log(`Result selector ${selector} not found, trying next...`)
        }
      }

      if (!clicked) {
        console.log('No result found, pressing Enter...')
        await this.page.press(searchInput, 'Enter')
      }

      // Wait for symbol to change
      await sleep(3000)
      
    } catch (error) {
      console.error('Failed to change symbol:', error)
      await this.takeDebugScreenshot('symbol_change_failed')
    }
  }

  private async changeTimeframe(timeframe: string): Promise<void> {
    if (!this.page) return

    try {
      console.log("Attempting to change timeframe to: " + timeframe)
      
      // Wait for chart to be ready
      await sleep(3000)
      
      // Map common timeframe values to TradingView format
      // CRITICAL: For hours, always prioritize minute values to avoid confusion
      const timeframeMap: { [key: string]: string[] } = {
        '1': ['1', '1m', '1min'],
        '1m': ['1', '1m', '1min'],
        '5': ['5', '5m', '5min'],
        '5m': ['5', '5m', '5min'],
        '15': ['15', '15m', '15min'],
        '15m': ['15', '15m', '15min'],
        '30': ['30', '30m', '30min'],
        '30m': ['30', '30m', '30min'],
        // For 1 hour - prioritize minute values first to avoid confusion
        '60': ['60', '60m', '1h', '1H'],
        '1h': ['60', '60m', '1h', '1H'], 
        '1H': ['60', '60m', '1h', '1H'],
        // For 4 hours - CRITICAL: prioritize 240 minutes to avoid "4min" confusion
        '240': ['240', '240m', '4h', '4H'],
        '4h': ['240', '240m', '4h', '4H'], // Always try 240 minutes FIRST
        '4H': ['240', '240m', '4h', '4H'],
        // Add other common hour timeframes
        '2h': ['120', '120m', '2h', '2H'],
        '2H': ['120', '120m', '2h', '2H'],
        '6h': ['360', '360m', '6h', '6H'],
        '6H': ['360', '360m', '6h', '6H'],
        '12h': ['720', '720m', '12h', '12H'],
        '12H': ['720', '720m', '12h', '12H'],
        // Daily and weekly
        '1D': ['1D', 'D', 'daily', '1d'],
        '1d': ['1D', 'D', 'daily', '1d'],
        '1W': ['1W', 'W', 'weekly', '1w'],
        '1w': ['1W', 'W', 'weekly', '1w']
      }
      
      // Get possible timeframe values to try
      const timeframesToTry = timeframeMap[timeframe] || [timeframe]
      console.log(`🎯 TIMEFRAME MAPPING: "${timeframe}" -> [${timeframesToTry.join(', ')}]`)
      console.log("Will try these timeframe values in order: " + timeframesToTry.join(', '))
      
      let found = false
      
      // Take a screenshot to see current timeframe bar
      await this.takeDebugScreenshot('before_timeframe_change')
      
      // CRITICAL: Click the interval legend to open timeframe selector
      console.log('TARGET: Looking for interval legend to open timeframe selector...')
      const intervalLegendSelectors = [
        '[data-name="legend-source-interval"]',
        '.intervalTitle-l31H9iuA',
        '[title="Change interval"]',
        '.intervalTitle-l31H9iuA button',
        '[data-name="legend-source-interval"] button'
      ]
      
      let intervalLegendClicked = false
      for (const selector of intervalLegendSelectors) {
        try {
          console.log("Trying interval legend selector: " + selector)
          const element = this.page.locator(selector).first()
          if (await element.isVisible({ timeout: 3000 })) {
            console.log("SUCCESS: Found interval legend: " + selector)
            await element.click()
            await sleep(2000)
            console.log('🖱️ Clicked interval legend - timeframe selector should be open')
            intervalLegendClicked = true
            break
          }
        } catch (e) {
          console.log(`Interval legend selector ${selector} not found`)
        }
      }
      
      if (!intervalLegendClicked) {
        console.log('ERROR: Could not find interval legend to click')
        await this.takeDebugScreenshot('no_interval_legend')
        return
      }
      
      // Now look for timeframe options in the opened selector
      console.log('CHECKING: Looking for timeframe options in selector...')
      
      for (const tf of timeframesToTry) {
        const timeframeSelectors = [
          // After clicking interval legend, look for options
          `[data-value="${tf}"]`,
          `button:has-text("${tf}")`,
          `.tv-dropdown__item:has-text("${tf}")`,
          `.tv-interval-item:has-text("${tf}")`,
          `[title="${tf}"]`,
          `[aria-label*="${tf}"]`,
          
          // Look in the opened dropdown/menu
          `.tv-dropdown-behavior__body [data-value="${tf}"]`,
          `.tv-dropdown-behavior__body button:has-text("${tf}")`,
          
          // Look for list items or menu items
          `li:has-text("${tf}")`,
          `div[role="option"]:has-text("${tf}")`,
          `[role="menuitem"]:has-text("${tf}")`,
          
          // TradingView specific interval selectors
          `.tv-screener-table__row:has-text("${tf}")`,
          `.tv-interval-tabs button:has-text("${tf}")`,
          `.intervals-GwQQdU8S [data-value="${tf}"]`,
          
          // Generic selectors in visible containers
          `.tv-dialog [data-value="${tf}"]`,
          `.tv-dialog button:has-text("${tf}")`
        ]
        
        for (const selector of timeframeSelectors) {
          try {
            console.log("Trying timeframe option selector: " + selector)
            const element = this.page.locator(selector).first()
            
            // Check if element exists and is visible
            const isVisible = await element.isVisible({ timeout: 2000 })
            if (isVisible) {
              console.log("SUCCESS: Found timeframe option: " + selector)
              await element.click()
              await sleep(2000)
              console.log("🎉 Successfully clicked timeframe option for " + tf)
              found = true
              break
            }
          } catch (e) {
            console.log(`Timeframe option selector ${selector} not found or not clickable`)
          }
        }
        if (found) break
      }

      // Fallback: Try keyboard navigation (only for simple minute timeframes)
      if (!found) {
        console.log('🔄 Timeframe options not found, trying keyboard navigation...')
        
        // Try pressing specific keys for common timeframes (ONLY for minute-based)
        const keyMap: { [key: string]: string } = {
          '1': '1',
          '5': '5',
          '15': '1', // Sometimes 15min maps to '1'
          '30': '3', // Sometimes 30min maps to '3'
          '1D': 'D'
          // REMOVED: '240': '4' - this was causing 4h to be interpreted as 4min!
          // REMOVED: '60': '1' - this was causing 1h to be interpreted as 1min!
        }
        
        // Only use keyboard shortcuts for simple minute timeframes, not hour-based ones
        if (keyMap[timeframe] && !timeframe.includes('h') && !timeframe.includes('H')) {
          console.log("🎹 Trying keyboard shortcut: " + keyMap[timeframe])
          await this.page.keyboard.press(keyMap[timeframe])
          await sleep(1000)
          found = true
        }
      }
      
      // PRIORITY FALLBACK: Try custom interval input (for hour-based timeframes)
      if (!found) {
        console.log('🔢 Trying custom interval input for hour-based timeframes...')
        
        // Convert timeframe to minutes for custom input
        const minutesMap: { [key: string]: string } = {
          '4h': '240',
          '4H': '240',
          '240': '240',
          '2h': '120',
          '2H': '120',
          '120': '120',
          '6h': '360',
          '6H': '360',
          '360': '360',
          '12h': '720',
          '12H': '720',
          '720': '720',
          '1h': '60',
          '1H': '60',
          '60': '60'
        }
        
        const minutesValue = minutesMap[timeframe]
        if (minutesValue) {
          try {
            console.log(`🎯 PRIORITY: Entering ${minutesValue} minutes for ${timeframe} directly...`)
            
            // First, try to click the interval legend again to ensure dialog is open
            const intervalLegendSelectors = [
              '[data-name="legend-source-interval"]',
              '.intervalTitle-l31H9iuA',
              '[title="Change interval"]'
            ]
            
            for (const selector of intervalLegendSelectors) {
              try {
                const element = this.page.locator(selector).first()
                if (await element.isVisible({ timeout: 2000 })) {
                  await element.click()
                  await sleep(1000)
                  break
                }
              } catch (e) {
                // Continue to next selector
              }
            }
            
            // Look for the custom interval input field (more comprehensive selectors)
            const customInputSelectors = [
              // TradingView interval dialog input
              'input[data-name="text-input-field"]',
              'input[placeholder*="interval"]',
              'input[placeholder*="minutes"]',
              '.tv-dialog input[type="text"]',
              '.tv-dialog input[type="number"]',
              '.tv-text-input input',
              'input[type="text"]',
              'input[inputmode="numeric"]',
              // Look in any visible dialog
              '[role="dialog"] input',
              '.tv-dropdown-behavior__body input',
              // Generic text inputs that might be visible
              'input:visible'
            ]
            
            let inputFound = false
            for (const selector of customInputSelectors) {
              try {
                const input = this.page.locator(selector).first()
                if (await input.isVisible({ timeout: 1000 })) {
                  console.log(`📝 Found interval input field: ${selector}`)
                  
                  // Clear any existing value and enter the minutes value
                  await input.click()
                  await sleep(300)
                  
                  // Select all and delete
                  await this.page.keyboard.press('Control+a')
                  await sleep(100)
                  await this.page.keyboard.press('Delete')
                  await sleep(300)
                  
                  // Type the correct minutes value
                  await input.fill(minutesValue)
                  await sleep(500)
                  
                  // Press Enter to confirm
                  await this.page.keyboard.press('Enter')
                  await sleep(2000)
                  
                  console.log(`✅ Successfully entered ${minutesValue} minutes for ${timeframe}`)
                  found = true
                  inputFound = true
                  break
                }
              } catch (e) {
                console.log(`Custom input selector ${selector} not found or not accessible`)
              }
            }
            
            if (!inputFound) {
              console.log('❌ No custom interval input field found')
            }
            
          } catch (error) {
            console.log('❌ Error with custom interval input:', error)
          }
        } else {
          console.log(`ℹ️ No minutes mapping found for timeframe: ${timeframe}`)
        }
      }
      
      if (found) {
        console.log("SUCCESS: Successfully changed timeframe to " + timeframe)
        await this.takeDebugScreenshot('after_timeframe_change')
      } else {
        console.log(`ERROR: Could not change timeframe to ${timeframe} - timeframe options not found`)
        // Take a debug screenshot to see current state
        await this.takeDebugScreenshot('timeframe_change_failed')
        
        // Log all visible elements that might be timeframe related
        try {
          const visibleElements = await this.page.$$eval('[data-value], button, [role="option"], [role="menuitem"], li', (elements: Element[]) => 
            elements
              .filter((el: Element) => {
                const style = window.getComputedStyle(el)
                return style.display !== 'none' && style.visibility !== 'hidden'
              })
              .slice(0, 20)
              .map((el: Element) => ({
                tagName: el.tagName,
                text: el.textContent?.trim().substring(0, 20),
                className: el.className.substring(0, 50),
                dataValue: el.getAttribute('data-value'),
                role: el.getAttribute('role'),
                outerHTML: el.outerHTML.substring(0, 150)
              }))
          )
          console.log('Visible interactive elements:', JSON.stringify(visibleElements, null, 2))
        } catch (e) {
          console.log('Could not analyze visible elements')
        }
      }
      
    } catch (error) {
      console.error('Failed to change timeframe:', error)
      await this.takeDebugScreenshot('timeframe_change_error')
    }
  }

  /**
   * Switch between different TradingView layouts (AI, DIY Module, etc.)
   * Uses the keyboard shortcut '.' to open the layouts dialog, then clicks the specific layout
   */
  async switchLayout(layoutType: string): Promise<boolean> {
    if (!this.page) return false

    try {
      console.log(`🎛️ Switching to ${layoutType} layout using layouts dialog...`)
      
      // Take debug screenshot before switching
      await this.takeDebugScreenshot(`before_switch_to_${layoutType}`)
      
      // Map layout types to the EXACT text that appears in the layouts dialog
      const layoutMap: { [key: string]: string[] } = {
        'ai': ['ai'],                    // Exact text from dialog: "ai"
        'diy': ['Diy module'],          // Exact text from dialog: "Diy module"
        'default': ['Default'],
        'advanced': ['Advanced']
      }
      
      const searchTerms = layoutMap[layoutType.toLowerCase()] || [layoutType]
      
      // First, try the keyboard shortcut method to open layouts dialog
      console.log(`⌨️ Opening layouts dialog with '.' key...`)
      await this.page.keyboard.press('.')
      await sleep(2000) // Wait for dialog to appear
      
      // Take debug screenshot to see the layouts dialog
      await this.takeDebugScreenshot(`layouts_dialog_opened_for_${layoutType}`)
      
      // Look for the layouts dialog and the specific layout within it
      const layoutsDialogVisible = await this.page.locator('.tv-dialog, .tv-popup, .tv-dropdown-behavior').first().isVisible({ timeout: 3000 }).catch(() => false)
      
      if (layoutsDialogVisible) {
        console.log(`✅ Layouts dialog is open, checking current selection and navigating to ${layoutType} layout...`)
        
        // First, detect which layout is currently selected
        let currentSelectedIndex = -1
        let currentSelectedText = ''
        
        try {
          const selectedInfo = await this.page.evaluate(() => {
            // Find all layout items in the dialog
            const items = Array.from(document.querySelectorAll('.tv-dropdown-behavior__item, .tv-list__item'))
            let selectedIndex = -1
            let selectedText = ''
            
            items.forEach((item, index) => {
              const text = item.textContent?.trim() || ''
              const isSelected = item.classList.contains('tv-dropdown-behavior__item--selected') ||
                               item.classList.contains('tv-list__item--selected') ||
                               item.getAttribute('aria-selected') === 'true' ||
                               item.classList.contains('selected') ||
                               getComputedStyle(item).backgroundColor !== 'rgba(0, 0, 0, 0)'
              
              if (isSelected && text) {
                selectedIndex = index
                selectedText = text
              }
            })
            
            return { selectedIndex, selectedText, totalItems: items.length }
          })
          
          currentSelectedIndex = selectedInfo.selectedIndex
          currentSelectedText = selectedInfo.selectedText
          
          console.log(`📍 Current selection: "${currentSelectedText}" at index ${currentSelectedIndex}`)
          console.log(`📋 Total items in dialog: ${selectedInfo.totalItems}`)
          
        } catch (e) {
          console.log(`⚠️ Could not detect current selection, using default navigation`)
        }
        
        // Define the layout positions based on the dialog structure
        const layoutPositions: { [key: string]: number } = {
          'diy': 0,     // "Diy module" is first (index 0)
          'ai': 1,      // "ai" is second (index 1)
          'support': 2, // "support & resistance" would be third
          'pi': 3       // "pi cycle top" would be fourth
          // Add more as needed
        }
        
        const targetIndex = layoutPositions[layoutType.toLowerCase()]
        
        if (targetIndex !== undefined && currentSelectedIndex >= 0) {
          const stepsNeeded = targetIndex - currentSelectedIndex
          
          console.log(`🎯 Need to move from index ${currentSelectedIndex} to ${targetIndex} (${stepsNeeded} steps)`)
          
          if (stepsNeeded === 0) {
            console.log(`✅ Target layout "${layoutType}" is already selected, pressing Enter`)
            await this.page.keyboard.press('Enter')
          } else if (stepsNeeded > 0) {
            console.log(`🔽 Pressing ArrowDown ${stepsNeeded} times to reach "${layoutType}"`)
            for (let i = 0; i < stepsNeeded; i++) {
              await this.page.keyboard.press('ArrowDown')
              await sleep(200)
            }
            await this.page.keyboard.press('Enter')
          } else {
            console.log(`🔼 Pressing ArrowUp ${Math.abs(stepsNeeded)} times to reach "${layoutType}"`)
            for (let i = 0; i < Math.abs(stepsNeeded); i++) {
              await this.page.keyboard.press('ArrowUp')
              await sleep(200)
            }
            await this.page.keyboard.press('Enter')
          }
          
        } else {
          // Fallback: Search by text content
          console.log(`🔍 Using fallback search method for "${layoutType}"`)
          const searchTerms = layoutMap[layoutType.toLowerCase()] || [layoutType]
          
          let attempts = 0
          const maxAttempts = 10
          
          while (attempts < maxAttempts) {
            try {
              const currentText = await this.page.evaluate(() => {
                const selected = document.querySelector('.tv-dropdown-behavior__item--selected, .tv-list__item--selected, [aria-selected="true"]')
                return selected?.textContent?.trim() || ''
              })
              
              console.log(`  Checking item: "${currentText}"`)
              
              if (searchTerms.some(term => currentText.toLowerCase().includes(term.toLowerCase()))) {
                console.log(`🎯 Found matching layout: "${currentText}"`)
                await this.page.keyboard.press('Enter')
                break
              }
            } catch (e) {
              // Continue searching
            }
            
            await this.page.keyboard.press('ArrowDown')
            await sleep(300)
            attempts++
          }
          
          if (attempts >= maxAttempts) {
            console.log(`⚠️ Could not find ${layoutType} layout after ${maxAttempts} attempts`)
            await this.page.keyboard.press('Escape')
            await sleep(1000)
            return false
          }
        }
        
        // Wait for layout to switch
        await sleep(3000)
        
        // Take debug screenshot after selection
        await this.takeDebugScreenshot(`after_select_${layoutType}_layout`)
        
        console.log(`✅ Successfully selected ${layoutType} layout via keyboard navigation`)
        return true
        
      } else {
        console.log(`⚠️ Layouts dialog did not appear, trying fallback method...`)
      }
      
      // Fallback to the original click-based method if keyboard shortcut didn't work
      console.log(`🔄 Fallback: Trying direct UI element search for ${layoutType}...`)
      const fallbackSearchTerms = layoutMap[layoutType.toLowerCase()] || [layoutType]
      
      // Enhanced TradingView layout switcher selectors (2024 UI patterns)
      const layoutSwitcherSelectors = [
        // Look for the DIY module toggle specifically (visible in screenshot)
        'text=Diy module',
        'text=DIY module', 
        '[title="Diy module"]',
        '[title="DIY module"]',
        'button:has-text("Diy")',
        'button:has-text("DIY")',
        
        // TradingView specific layout/module selectors - more precise matching
        '[data-name="ai-panel"]',
        '[data-name="ai-layout"]',
        '[data-name="ai-module"]',
        '[data-name="diy-panel"]',
        '[data-name="diy-layout"]',
        '[data-name="diy-module"]',
        '[data-module-name="ai"]',
        '[data-module-name="diy"]',
        '[data-layout-name="ai"]',
        '[data-layout-name="diy"]',
        
        // Top toolbar and header elements with specific text content
        '.tv-header [role="button"]:has-text("AI")',
        '.tv-header [role="button"]:has-text("DIY")',
        '.tv-toolbar [role="button"]:has-text("AI")',
        '.tv-toolbar [role="button"]:has-text("DIY")',
        '.tv-chart-header [role="button"]:has-text("AI")',
        '.tv-chart-header [role="button"]:has-text("DIY")',
        
        // Module and tab selectors with text
        '.tv-module-tabs [role="tab"]:has-text("AI")',
        '.tv-module-tabs [role="tab"]:has-text("DIY")',
        '.tv-chart-tabs [role="tab"]:has-text("AI")',
        '.tv-chart-tabs [role="tab"]:has-text("DIY")',
        
        // Modern UI component selectors - exact matches
        '[data-testid="ai-layout"]',
        '[data-testid="diy-layout"]',
        '[data-testid="ai-module"]',
        '[data-testid="diy-module"]',
        '[data-widget-type="ai"]',
        '[data-widget-type="diy"]',
        
        // Button elements with exact title/aria-label matches
        'button[title="AI"]',
        'button[title="DIY"]',
        'button[title="AI Analysis"]',
        'button[title="DIY Module"]',
        'button[aria-label="AI"]',
        'button[aria-label="DIY"]',
        'button[aria-label="AI Analysis"]',
        'button[aria-label="DIY Module"]',
        
        // Generic selectors (last resort) - but we'll be more selective
        '[role="tab"]',
        '[role="button"]',
        'button'
      ]
      
      console.log(`🔍 Searching for ${layoutType} layout using ${fallbackSearchTerms.length} search terms and ${layoutSwitcherSelectors.length} selectors`)
      
      // Debug: Log all visible buttons/tabs for inspection
      if (process.env.NODE_ENV === 'development') {
        try {
          const allInteractiveElements = await this.page.locator('button, [role="button"], [role="tab"]').all()
          console.log(`🔍 Found ${allInteractiveElements.length} interactive elements on page`)
          
          for (const element of allInteractiveElements.slice(0, 20)) { // Limit to first 20 for readability
            const text = await element.textContent().catch(() => '')
            const title = await element.getAttribute('title').catch(() => '')
            const ariaLabel = await element.getAttribute('aria-label').catch(() => '')
            const dataName = await element.getAttribute('data-name').catch(() => '')
            
            if (text || title || ariaLabel || dataName) {
              console.log(`  📋 Element: text="${text}" title="${title}" aria-label="${ariaLabel}" data-name="${dataName}"`)
            }
          }
        } catch (e) {
          console.log('⚠️ Could not enumerate interactive elements for debugging')
        }
      }
      
      // First, try to find and click layout switcher elements
      for (const searchTerm of fallbackSearchTerms) {
        console.log(`🎯 Searching for layout elements containing: "${searchTerm}"`)
        
        for (const selector of layoutSwitcherSelectors) {
          try {
            // Look for elements containing the search term
            const elements = await this.page.locator(selector).all()
            
            for (const element of elements) {
              const text = await element.textContent().catch(() => '')
              const title = await element.getAttribute('title').catch(() => '')
              const ariaLabel = await element.getAttribute('aria-label').catch(() => '')
              const dataTooltip = await element.getAttribute('data-tooltip').catch(() => '')
              const dataName = await element.getAttribute('data-name').catch(() => '')
              
              const combinedText = `${text} ${title} ${ariaLabel} ${dataTooltip} ${dataName}`.toLowerCase()
              
              // More precise matching - avoid false positives
              let isMatch = false
              
              if (layoutType.toLowerCase() === 'ai') {
                // For AI, look for exact matches or clear AI-related terms
                isMatch = (
                  text?.trim().toLowerCase() === 'ai' ||
                  title?.toLowerCase() === 'ai' ||
                  ariaLabel?.toLowerCase() === 'ai' ||
                  text?.toLowerCase().includes('ai analysis') ||
                  text?.toLowerCase().includes('ai insights') ||
                  title?.toLowerCase().includes('ai analysis') ||
                  title?.toLowerCase().includes('ai insights') ||
                  dataName?.toLowerCase() === 'ai-panel' ||
                  dataName?.toLowerCase() === 'ai-module' ||
                  dataName?.toLowerCase() === 'ai-layout'
                )
              } else if (layoutType.toLowerCase() === 'diy') {
                // For DIY, look for exact matches or clear DIY-related terms  
                isMatch = (
                  text?.trim().toLowerCase() === 'diy' ||
                  title?.toLowerCase() === 'diy' ||
                  ariaLabel?.toLowerCase() === 'diy' ||
                  text?.toLowerCase().includes('diy module') ||
                  text?.toLowerCase().includes('diy builder') ||
                  title?.toLowerCase().includes('diy module') ||
                  title?.toLowerCase().includes('diy builder') ||
                  dataName?.toLowerCase() === 'diy-panel' ||
                  dataName?.toLowerCase() === 'diy-module' ||
                  dataName?.toLowerCase() === 'diy-layout'
                )
              } else {
                // For other layouts, use the original logic
                isMatch = combinedText.includes(searchTerm.toLowerCase())
              }
              
              if (isMatch) {
                console.log(`🎯 Found potential ${layoutType} layout element:`)
                console.log(`   Selector: ${selector}`)
                console.log(`   Text: "${text}"`)
                console.log(`   Title: "${title}"`)
                console.log(`   Aria-label: "${ariaLabel}"`)
                console.log(`   Data-name: "${dataName}"`)
                
                // Additional validation - skip if this looks like a false positive
                const skipPatterns = [
                  'details', 'metrics', 'search', 'symbol', 'chart-', 'interval',
                  'timeframe', 'indicator', 'alert', 'watchlist', 'compare'
                ]
                
                const shouldSkip = skipPatterns.some(pattern => 
                  dataName?.toLowerCase().includes(pattern) ||
                  title?.toLowerCase().includes(pattern) ||
                  ariaLabel?.toLowerCase().includes(pattern)
                )
                
                if (shouldSkip) {
                  console.log(`⚠️ Skipping element that looks like a false positive`)
                  continue
                }
                
                if (await element.isVisible({ timeout: 2000 })) {
                  console.log(`✅ Element is visible, attempting click...`)
                  await element.click()
                  await sleep(3000) // Wait longer for layout change
                  
                  // Take debug screenshot after clicking
                  await this.takeDebugScreenshot(`after_click_${layoutType}`)
                  
                  console.log(`✅ Successfully clicked ${layoutType} layout element`)
                  return true
                } else {
                  console.log(`⚠️ Element found but not visible`)
                }
              }
            }
          } catch (e: any) {
            // Continue to next selector
            console.log(`⚠️ Error with selector "${selector}": ${e?.message || e}`)
          }
        }
      }
      
      // Secondary approach: Try to find layout/module menus and click them
      console.log(`🔍 Trying to find ${layoutType} layout via menu navigation...`)
      
      const menuSelectors = [
        // Look for layout/view menus
        '[data-name="chart-layout-menu"]',
        '[data-name="view-menu"]',
        '[data-name="chart-menu"]',
        '.tv-menu-button',
        '.tv-dropdown-button',
        
        // Try toolbar dropdown menus
        '.tv-toolbar .tv-dropdown',
        '.tv-header .tv-dropdown',
        '.tv-chart-header .tv-dropdown',
        
        // Widget panel menus
        '.tv-widget-panel .tv-dropdown',
        '.tv-side-panel .tv-dropdown'
      ]
      
      for (const menuSelector of menuSelectors) {
        try {
          const menuButton = this.page.locator(menuSelector).first()
          if (await menuButton.isVisible({ timeout: 1000 })) {
            console.log(`🎯 Found potential layout menu: ${menuSelector}`)
            await menuButton.click()
            await sleep(1000)
            
            // Look for layout options in the opened menu
            for (const searchTerm of searchTerms) {
              const menuItems = await this.page.locator('.tv-dropdown-behavior__item, .tv-menu__item, .tv-popup__item').all()
              
              for (const item of menuItems) {
                const itemText = await item.textContent().catch(() => '')
                if (itemText && itemText.toLowerCase().includes(searchTerm.toLowerCase())) {
                  console.log(`🎯 Found ${layoutType} in menu: ${itemText}`)
                  await item.click()
                  await sleep(3000)
                  
                  // Take debug screenshot after menu selection
                  await this.takeDebugScreenshot(`after_menu_select_${layoutType}`)
                  
                  return true
                }
              }
            }
            
            // Close menu if we didn't find what we're looking for
            await this.page.keyboard.press('Escape')
            await sleep(500)
          }
        } catch (e: any) {
          // Continue to next menu selector
          console.log(`⚠️ Error with menu selector "${menuSelector}": ${e?.message || e}`)
        }
      }
      
      // Third approach: Try right-click context menu
      console.log(`🔍 Trying right-click context menu for ${layoutType} layout...`)
      try {
        // Right-click on chart area
        const chartContainer = this.page.locator('.tv-chart-container, .chart-container, .tv-chart').first()
        if (await chartContainer.isVisible({ timeout: 2000 })) {
          await chartContainer.click({ button: 'right' })
          await sleep(1000)
          
          // Look for layout options in context menu
          for (const searchTerm of searchTerms) {
            const contextMenuItems = await this.page.locator('.tv-context-menu__item, .tv-dropdown-behavior__item').all()
            
            for (const item of contextMenuItems) {
              const itemText = await item.textContent().catch(() => '')
              if (itemText && itemText.toLowerCase().includes(searchTerm.toLowerCase())) {
                console.log(`🎯 Found ${layoutType} in context menu: ${itemText}`)
                await item.click()
                await sleep(3000)
                
                // Take debug screenshot after context menu selection
                await this.takeDebugScreenshot(`after_context_menu_${layoutType}`)
                
                return true
              }
            }
          }
          
          // Close context menu
          await this.page.keyboard.press('Escape')
        }
      } catch (e: any) {
        console.log(`⚠️ Error with context menu: ${e?.message || e}`)
      }
      
      // Fallback: Try keyboard shortcuts
      const keyboardShortcuts: { [key: string]: string } = {
        'ai': 'Alt+A',
        'diy': 'Alt+D',
        'default': 'Alt+1',
        'advanced': 'Alt+2'
      }
      
      const shortcut = keyboardShortcuts[layoutType.toLowerCase()]
      if (shortcut) {
        console.log(`⌨️ Trying keyboard shortcut for ${layoutType}: ${shortcut}`)
        await this.page.keyboard.press(shortcut)
        await sleep(3000)
        
        // Take debug screenshot after keyboard shortcut
        await this.takeDebugScreenshot(`after_shortcut_${layoutType}`)
        
        console.log(`✅ Attempted ${layoutType} layout switch via keyboard shortcut`)
        return true
      }
      
      console.log(`❌ Could not find ${layoutType} layout switcher with any method`)
      
      // Take final debug screenshot
      await this.takeDebugScreenshot(`failed_switch_to_${layoutType}`)
      
      return false
      
    } catch (error) {
      console.error(`Error switching to ${layoutType} layout:`, error)
      return false
    }
  }

  /**
   * Wait for layout to fully load and verify the layout change occurred
   */
  async waitForLayoutLoad(layoutType: string): Promise<boolean> {
    if (!this.page) return false
    
    try {
      console.log(`⏳ Waiting for ${layoutType} layout to load...`)
      
      // Take debug screenshot to verify layout state
      await this.takeDebugScreenshot(`waiting_for_${layoutType}_load`)
      
      // Wait for layout-specific elements to appear
      const layoutIndicators: { [key: string]: string[] } = {
        'ai': [
          // AI-specific panels and widgets
          '[data-name="ai-panel"]',
          '[data-name*="ai"]',
          '.ai-analysis',
          '.ai-module',
          '.ai-widget',
          '.ai-insights',
          '[title*="AI"]',
          '[class*="ai"]',
          '[data-widget-type*="ai"]',
          // AI content indicators
          'text=AI Analysis',
          'text=AI Insights',
          'text=Smart Money',
          // TradingView AI specific
          '.tv-ai-panel',
          '.tv-ai-widget',
          '.tv-ai-analysis'
        ],
        'diy': [
          // DIY-specific panels and widgets
          '[data-name="diy-panel"]',
          '[data-name*="diy"]',
          '.diy-module',
          '.diy-builder',
          '.diy-widget',
          '[title*="DIY"]',
          '[class*="diy"]',
          '[data-widget-type*="diy"]',
          // DIY content indicators
          'text=DIY Builder',
          'text=DIY Module',
          'text=Custom Layout',
          // TradingView DIY specific
          '.tv-diy-panel',
          '.tv-diy-widget',
          '.tv-diy-builder'
        ],
        'default': [
          // Default layout indicators
          '.tv-chart-container',
          '.chart-container',
          '.tv-chart'
        ]
      }
      
      const indicators = layoutIndicators[layoutType.toLowerCase()] || []
      let layoutDetected = false
      
      console.log(`🔍 Checking ${indicators.length} layout indicators for ${layoutType}`)
      
      // Try each indicator with reasonable timeout
      for (const indicator of indicators) {
        try {
          console.log(`  🎯 Checking indicator: ${indicator}`)
          await this.page.locator(indicator).first().waitFor({ 
            state: 'visible',
            timeout: 3000 
          })
          console.log(`✅ ${layoutType} layout indicator found: ${indicator}`)
          layoutDetected = true
          break
        } catch (e) {
          // Continue to next indicator
          console.log(`  ⚠️ Indicator not found: ${indicator}`)
        }
      }
      
      if (layoutDetected) {
        // Take success screenshot
        await this.takeDebugScreenshot(`${layoutType}_layout_loaded`)
        
        // Additional wait for content to stabilize
        await sleep(2000)
        console.log(`✅ ${layoutType} layout loaded successfully`)
        return true
      }
      
      // If no specific indicators found, try generic layout change detection
      console.log(`🔍 No specific indicators found, checking for general layout changes...`)
      
      // Wait for any visual changes in common layout areas
      const layoutAreas = [
        '.tv-chart-container',
        '.tv-widget-panel',
        '.tv-side-panel',
        '.chart-container',
        '.tv-chart'
      ]
      
      for (const area of layoutAreas) {
        try {
          const areaElement = this.page.locator(area).first()
          if (await areaElement.isVisible({ timeout: 2000 })) {
            console.log(`✅ Layout area visible: ${area}`)
            layoutDetected = true
            break
          }
        } catch (e) {
          // Continue checking
        }
      }
      
      if (layoutDetected) {
        // Fallback: wait for general layout changes
        await sleep(3000)
        
        // Take fallback screenshot
        await this.takeDebugScreenshot(`${layoutType}_layout_fallback_loaded`)
        
        console.log(`⚠️ ${layoutType} layout load detection uncertain, but proceeding...`)
        return true
      }
      
      console.log(`❌ ${layoutType} layout load could not be verified`)
      
      // Take failure screenshot
      await this.takeDebugScreenshot(`${layoutType}_layout_load_failed`)
      
      return false
      
    } catch (error) {
      console.error(`Error waiting for ${layoutType} layout:`, error)
      
      // Take error screenshot
      await this.takeDebugScreenshot(`${layoutType}_layout_load_error`)
      
      return false
    }
  }

  /**
   * Check if user is logged in (alias for checkLoginStatus)
   */
  async isLoggedIn(): Promise<boolean> {
    return this.checkLoginStatus()
  }

  /**
   * Wait for chart data to load with enhanced detection
   */
  async waitForChartData(): Promise<boolean> {
    if (!this.page) return false

    try {
      console.log('Waiting for chart data to load...')
      
      // Wait for various chart loading indicators
      const chartLoadingSelectors = [
        'canvas',
        '.tv-lightweight-charts',
        '[data-name="chart"]',
        '.chart-container canvas',
        '.tv-chart canvas'
      ]
      
      // Wait for at least one chart element
      let chartFound = false
      for (const selector of chartLoadingSelectors) {
        try {
          await this.page.waitForSelector(selector, { timeout: 10000 })
          chartFound = true
          break
        } catch (e) {
          continue
        }
      }
      
      if (!chartFound) {
        console.log('WARNING: No chart elements found')
        return false
      }
      
      // Additional wait for chart data to load
      await this.humanDelay(3000, 6000)
      
      // Check if chart appears to have data (not just loading screen)
      const hasData = await this.page.evaluate(() => {
        const canvases = document.querySelectorAll('canvas')
        for (let i = 0; i < canvases.length; i++) {
          const canvas = canvases[i]
          const rect = canvas.getBoundingClientRect()
          if (rect.width > 100 && rect.height > 100) {
            return true
          }
        }
        return false
      })
      
      console.log('Chart data loaded successfully')
      return hasData
    } catch (error) {
      console.error('ERROR: Error waiting for chart data:', error)
      return false
    }
  }

  /**
   * Take screenshot with anti-detection measures
   */
  async takeScreenshot(filename: string): Promise<string> {
    if (!this.page) throw new Error('Page not initialized')

    try {
      const screenshotsDir = path.join(process.cwd(), 'screenshots')
      await fs.mkdir(screenshotsDir, { recursive: true })
      
      const filePath = path.join(screenshotsDir, filename)
      
      // Perform human-like interaction before screenshot
      await this.simulateHumanScrolling()
      await this.humanDelay(1000, 2000)
      
      console.log("Taking screenshot: " + filename)
      
      // Try to find and focus on the main chart area first
      const chartSelectors = [
        '#tv-chart-container',
        '.layout__area--center',
        '.chart-container-border',
        '.tv-chart-area-container',
        '.chart-area',
        '[data-name="chart-area"]',
        '.tv-chart-area'
      ]
      
      let chartElement = null
      for (const selector of chartSelectors) {
        try {
          chartElement = await this.page.locator(selector).first()
          if (await chartElement.isVisible({ timeout: 2000 })) {
            console.log(`📸 Found chart area with selector: ${selector}`)
            break
          }
        } catch (e) {
          // Continue to next selector
        }
      }
      
      if (chartElement && await chartElement.isVisible()) {
        // Take screenshot of the chart area specifically
        await chartElement.screenshot({
          path: filePath,
          type: 'png'
        })
        console.log("📸 Chart area screenshot saved: " + filename)
      } else {
        // Fallback to full page screenshot
        console.log("⚠️ Chart area not found, taking full page screenshot")
        await this.page.screenshot({
          path: filePath as `${string}.png`,
          fullPage: true,
          type: 'png'
        })
        console.log("📸 Full page screenshot saved: " + filename)
      }
      
      return filePath
    } catch (error) {
      console.error('ERROR: Error taking screenshot:', error)
      throw error
    }
  }

  /**
   * Take a debug screenshot for troubleshooting
   */
  private async takeDebugScreenshot(prefix: string): Promise<void> {
    if (!this.page) return
    
    try {
      const timestamp = Date.now()
      const filename = `debug_${prefix}_${timestamp}.png`
      const filePath = path.join(process.cwd(), 'screenshots', filename)
      
      // Ensure directory exists
      await fs.mkdir(path.dirname(filePath), { recursive: true })
      
      await this.page.screenshot({
        path: filePath as `${string}.png`,
        fullPage: true,
        type: 'png'
      })
      
      console.log("Screenshot saved: " + filename)
    } catch (error) {
      console.log('WARNING: Error taking debug screenshot:', error)
    }
  }

  /**
   * Get current URL
   */
  async getCurrentUrl(): Promise<string> {
    if (!this.page) return 'about:blank'
    return this.page.url()
  }

  /**
   * Enhanced cleanup method
   */
  async close(): Promise<void> {
    return this.forceCleanup()
  }

  /**
   * Force cleanup of browser resources
   */
  async forceCleanup(): Promise<void> {
    // Don't use operation lock here to avoid deadlocks during cleanup
    try {
      if (this.page) {
        try {
          await this.page.close()
        } catch (e) {
          console.log('WARNING: Error closing page:', e)
        }
        this.page = null
      }
      
      if (this.context) {
        try {
          await this.context.close()
        } catch (e) {
          console.log('WARNING: Error closing context:', e)
        }
        this.context = null
      }
      
      if (this.browser) {
        try {
          await this.browser.close()
        } catch (e) {
          console.log('WARNING: Error closing browser:', e)
        }
        this.browser = null
      }
      
      // Reset flags
      this.isAuthenticated = false
      this.operationLock = false
      this.initPromise = null
      
    } catch (error) {
      console.error('ERROR: Error during force cleanup:', error)
    }
  }

  /**
   * Reset the singleton instance (useful for testing or forcing recreation)
   */
  static resetInstance(): void {
    if (TradingViewAutomation.instance) {
      TradingViewAutomation.instance.forceCleanup().catch(console.error)
      TradingViewAutomation.instance = null
    }
  }

  /**
   * Load saved session data (cookies, localStorage, etc.)
   */
  private async loadSession(): Promise<void> {
    try {
      console.log('🔄 Loading saved session data...')
      
      // Load cookies
      if (await this.fileExists(COOKIES_FILE)) {
        const cookiesData = await fs.readFile(COOKIES_FILE, 'utf8')
        const cookies = JSON.parse(cookiesData)
        await this.context!.addCookies(cookies)
        console.log(`SUCCESS: Loaded ${cookies.length} cookies from saved session`)
      }
      
      // Note: Session storage will be loaded after page navigation
      
    } catch (error) {
      console.log('WARNING: Could not load session data (starting fresh):', error)
    }
  }

  /**
   * Save current session data for future use
   */
  private async saveSession(): Promise<void> {
    try {
      console.log('💾 Saving session data...')
      
      if (!this.context || !this.page) return
      
      // Save cookies
      const cookies = await this.context.cookies()
      await fs.writeFile(COOKIES_FILE, JSON.stringify(cookies, null, 2))
      console.log(`SUCCESS: Saved ${cookies.length} cookies`)
      
      // Save session storage and localStorage
      const sessionData = await this.page.evaluate(() => {
        const localStorage: { [key: string]: string | null } = {}
        const sessionStorage: { [key: string]: string | null } = {}
        
        // Extract localStorage
        for (let i = 0; i < window.localStorage.length; i++) {
          const key = window.localStorage.key(i)
          if (key) {
            localStorage[key] = window.localStorage.getItem(key)
          }
        }
        
        // Extract sessionStorage
        for (let i = 0; i < window.sessionStorage.length; i++) {
          const key = window.sessionStorage.key(i)
          if (key) {
            sessionStorage[key] = window.sessionStorage.getItem(key)
          }
        }
        
        return { localStorage, sessionStorage }
      })
      
      await fs.writeFile(SESSION_STORAGE_FILE, JSON.stringify(sessionData, null, 2))
      console.log('SUCCESS: Saved session storage and localStorage')
      
    } catch (error) {
      console.error('ERROR: Failed to save session data:', error)
    }
  }

  /**
   * Restore session storage and localStorage
   */
  private async restoreSessionStorage(): Promise<void> {
    try {
      if (!this.page || !await this.fileExists(SESSION_STORAGE_FILE)) return
      
      const sessionData = JSON.parse(await fs.readFile(SESSION_STORAGE_FILE, 'utf8'))
      
      await this.page.evaluate((data) => {
        // Restore localStorage
        if (data.localStorage) {
          for (const [key, value] of Object.entries(data.localStorage)) {
            try {
              window.localStorage.setItem(key, value as string)
            } catch (e) {
              console.log('Could not restore localStorage item:', key)
            }
          }
        }
        
        // Restore sessionStorage
        if (data.sessionStorage) {
          for (const [key, value] of Object.entries(data.sessionStorage)) {
            try {
              window.sessionStorage.setItem(key, value as string)
            } catch (e) {
              console.log('Could not restore sessionStorage item:', key)
            }
          }
        }
      }, sessionData)
      
      console.log('SUCCESS: Restored session storage and localStorage')
      
    } catch (error) {
      console.log('WARNING: Could not restore session storage:', error)
    }
  }

  /**
   * Refresh session to keep it alive
   */
  async refreshSession(): Promise<boolean> {
    if (!this.page || !this.isAuthenticated) return false

    try {
      console.log('🔄 Refreshing TradingView session...')
      
      // Just reload the current page to refresh session
      await this.page.reload({
        waitUntil: 'domcontentloaded',
        timeout: 30000
      })
      
      // Wait for page to settle
      await sleep(2000)
      
      // Verify still logged in
      const stillLoggedIn = await this.checkLoginStatus()
      if (stillLoggedIn) {
        console.log('SUCCESS: Session refreshed successfully')
        await this.saveSession() // Save refreshed session
        return true
      } else {
        console.log('ERROR: Session expired during refresh')
        this.isAuthenticated = false
        return false
      }
      
    } catch (error) {
      console.error('ERROR: Failed to refresh session:', error)
      return false
    }
  }

  /**
   * Clear all saved session data
   */
  async clearSession(): Promise<void> {
    try {
      console.log('🗑️ Clearing saved session data...')
      
      if (await this.fileExists(COOKIES_FILE)) {
        await fs.unlink(COOKIES_FILE)
        console.log('SUCCESS: Cleared cookies file')
      }
      
      if (await this.fileExists(SESSION_STORAGE_FILE)) {
        await fs.unlink(SESSION_STORAGE_FILE)
        console.log('SUCCESS: Cleared session storage file')
      }
      
      // Clear browser context storage if available
      if (this.context) {
        await this.context.clearCookies()
        console.log('SUCCESS: Cleared browser context cookies')
      }
      
      this.isAuthenticated = false
      console.log('SUCCESS: Session data cleared successfully')
      
    } catch (error) {
      console.error('ERROR: Failed to clear session data:', error)
    }
  }

  /**
   * Get session status information
   */
  async getSessionInfo(): Promise<{
    isAuthenticated: boolean
    hasSavedCookies: boolean
    hasSavedStorage: boolean
    cookiesCount: number
    currentUrl: string
  }> {
    const hasSavedCookies = await this.fileExists(COOKIES_FILE)
    const hasSavedStorage = await this.fileExists(SESSION_STORAGE_FILE)
    
    let cookiesCount = 0
    if (hasSavedCookies) {
      try {
        const cookiesData = await fs.readFile(COOKIES_FILE, 'utf8')
        const cookies = JSON.parse(cookiesData)
        cookiesCount = cookies.length
      } catch (e) {
        // Ignore error
      }
    }
    
    const currentUrl = this.page ? await this.page.url() : ''
    
    return {
      isAuthenticated: this.isAuthenticated,
      hasSavedCookies,
      hasSavedStorage,
      cookiesCount,
      currentUrl
    }
  }

  /**
   * Get lightweight session status without triggering navigation
   */
  async getQuickSessionStatus(): Promise<{
    isAuthenticated: boolean
    hasSavedCookies: boolean
    hasSavedStorage: boolean
    cookiesCount: number
    currentUrl: string
    browserActive: boolean
  }> {
    const sessionInfo = await this.getSessionInfo()
    
    return {
      ...sessionInfo,
      browserActive: !!(this.browser && this.page)
    }
  }

  /**
   * Add random delay to mimic human behavior
   */
  private async humanDelay(min: number = 500, max: number = 1500): Promise<void> {
    if (!this.humanBehaviorEnabled) return
    
    const delay = Math.floor(Math.random() * (max - min + 1)) + min
    // Add micro-pauses to make it even more realistic
    const microPauses = Math.floor(Math.random() * 3) + 1
    
    for (let i = 0; i < microPauses; i++) {
      await this.page?.waitForTimeout(delay / microPauses)
      if (i < microPauses - 1) {
        await this.page?.waitForTimeout(Math.floor(Math.random() * 100) + 50)
      }
    }
  }

  /**
   * Simulate human-like mouse movement before clicks
   */
  private async humanMouseMove(targetElement?: any): Promise<void> {
    if (!this.page || !this.humanBehaviorEnabled) return

    try {
      // Get current viewport size
      const viewport = this.page.viewportSize() || { width: 1920, height: 1080 }
      
      if (targetElement) {
        // Move to target element with slight randomization
        const boundingBox = await targetElement.boundingBox()
        if (boundingBox) {
          const targetX = boundingBox.x + boundingBox.width / 2 + (Math.random() - 0.5) * 20
          const targetY = boundingBox.y + boundingBox.height / 2 + (Math.random() - 0.5) * 20
          
          // Create intermediate points for more natural movement
          const steps = Math.floor(Math.random() * 3) + 2
          for (let i = 1; i <= steps; i++) {
            const progress = i / steps
            const currentX = this.lastMousePosition.x + (targetX - this.lastMousePosition.x) * progress
            const currentY = this.lastMousePosition.y + (targetY - this.lastMousePosition.y) * progress
            
            await this.page.mouse.move(currentX, currentY)
            await this.humanDelay(50, 150)
          }
          
          this.lastMousePosition = { x: targetX, y: targetY }
        }
      } else {
        // Random mouse movement within viewport
        const randomX = Math.floor(Math.random() * viewport.width)
        const randomY = Math.floor(Math.random() * viewport.height)
        
        await this.page.mouse.move(randomX, randomY)
        this.lastMousePosition = { x: randomX, y: randomY }
      }
    } catch (error) {
      console.log('WARNING: Mouse movement simulation failed:', error)
    }
  }

  /**
   * Human-like typing with realistic delays and occasional typos
   */
  private async humanType(selector: string, text: string): Promise<void> {
    if (!this.page || !this.humanBehaviorEnabled) {
      await this.page?.fill(selector, text)
      return
    }

    try {
      // Clear the field first
      await this.page.click(selector)
      await this.page.keyboard.press('Control+a')
      await this.humanDelay(100, 300)
      
      // Type character by character with realistic delays
      for (let i = 0; i < text.length; i++) {
        const char = text[i]
        
        // Simulate occasional brief pauses (thinking)
        if (Math.random() < 0.1) {
          await this.humanDelay(300, 800)
        }
        
        // Simulate occasional typos and corrections (5% chance)
        if (Math.random() < 0.05 && i > 0) {
          // Type wrong character
          const wrongChars = 'abcdefghijklmnopqrstuvwxyz'
          const wrongChar = wrongChars[Math.floor(Math.random() * wrongChars.length)]
          await this.page.keyboard.type(wrongChar)
          await this.humanDelay(200, 500)
          
          // Correct the typo
          await this.page.keyboard.press('Backspace')
          await this.humanDelay(100, 300)
        }
        
        // Type the actual character
        await this.page.keyboard.type(char)
        
        // Realistic typing speed variation
        const baseDelay = 80
        const variation = Math.random() * 120
        await sleep(baseDelay + variation)
      }
      
      // Brief pause after typing
      await this.humanDelay(200, 500)
    } catch (error) {
      console.log('WARNING: Human typing failed, falling back to fill:', error)
      await this.page.fill(selector, text)
    }
  }

  /**
   * Human-like clicking with mouse movement and realistic delays
   */
  private async humanClick(element: any): Promise<void> {
    if (!this.page || !this.humanBehaviorEnabled) {
      await element.click()
      return
    }

    try {
      // Move mouse to element first
      await this.humanMouseMove(element)
      await this.humanDelay(200, 500)
      
      // Hover for a brief moment
      await element.hover()
      await this.humanDelay(100, 300)
      
      // Click with slight randomization
      const boundingBox = await element.boundingBox()
      if (boundingBox) {
        const clickX = boundingBox.x + boundingBox.width / 2 + (Math.random() - 0.5) * 10
        const clickY = boundingBox.y + boundingBox.height / 2 + (Math.random() - 0.5) * 10
        
        await this.page.mouse.click(clickX, clickY)
      } else {
        await element.click()
      }
      
      // Brief pause after click
      await this.humanDelay(300, 700)
    } catch (error) {
      console.log('WARNING: Human clicking failed, falling back to normal click:', error)
      await element.click()
      await this.humanDelay(300, 700)
    }
  }

  /**
   * Simulate reading behavior with eye movement patterns
   */
  private async simulateReading(): Promise<void> {
    if (!this.page || !this.humanBehaviorEnabled) return

    try {
      // Simulate reading by scrolling and pausing
      const viewport = this.page.viewportSize() || { width: 1920, height: 1080 }
      
      // Random scroll amount
      const scrollAmount = Math.floor(Math.random() * 200) + 100
      await this.page.mouse.wheel(0, scrollAmount)
      await this.humanDelay(800, 1500)
      
      // Scroll back
      await this.page.mouse.wheel(0, -scrollAmount)
      await this.humanDelay(500, 1000)
    } catch (error) {
      console.log('WARNING: Reading simulation failed:', error)
    }
  }

  /**
   * Enhanced anti-detection measures
   */
  private async applyAdvancedStealth(): Promise<void> {
    if (!this.page) return

    try {
      await this.page.addInitScript(() => {
        // Advanced fingerprint resistance
        
        // Override canvas fingerprinting
        const originalToDataURL = HTMLCanvasElement.prototype.toDataURL;
        HTMLCanvasElement.prototype.toDataURL = function(...args) {
          const context = this.getContext('2d');
          if (context) {
            // Add noise to canvas
            const imageData = context.getImageData(0, 0, this.width, this.height);
            for (let i = 0; i < imageData.data.length; i += 4) {
              if (Math.random() < 0.01) {
                imageData.data[i] = Math.floor(Math.random() * 256);
                imageData.data[i + 1] = Math.floor(Math.random() * 256);
                imageData.data[i + 2] = Math.floor(Math.random() * 256);
              }
            }
            context.putImageData(imageData, 0, 0);
          }
          return originalToDataURL.apply(this, args);
        };

        // Override audio fingerprinting
        const audioContext = window.AudioContext || (window as any).webkitAudioContext;
        if (audioContext) {
          const originalCreateAnalyser = audioContext.prototype.createAnalyser;
          audioContext.prototype.createAnalyser = function() {
            const analyser = originalCreateAnalyser.call(this);
            const originalGetFloatFrequencyData = analyser.getFloatFrequencyData;
            analyser.getFloatFrequencyData = function(array: Float32Array) {
              originalGetFloatFrequencyData.call(this, array);
              // Add subtle noise
              for (let i = 0; i < array.length; i++) {
                array[i] += (Math.random() - 0.5) * 0.001;
              }
            };
            return analyser;
          };
        }

        // Override timezone detection
        Object.defineProperty(Intl.DateTimeFormat.prototype, 'resolvedOptions', {
          value: function() {
            return {
              ...Intl.DateTimeFormat.prototype.resolvedOptions.call(this),
              timeZone: 'America/New_York'
            };
          }
        });

        // Randomize performance.now() slightly
        const originalNow = performance.now;
        performance.now = function() {
          return originalNow.call(this) + Math.random() * 0.1;
        };

        // Mock more realistic viewport
        Object.defineProperty(window, 'outerWidth', {
          get: () => 1920 + Math.floor(Math.random() * 100)
        });
        Object.defineProperty(window, 'outerHeight', {
          get: () => 1080 + Math.floor(Math.random() * 100)
        });
      });
    } catch (error) {
      console.log('WARNING: Advanced stealth measures failed:', error)
    }
  }

  /**
   * Check if file exists
   */
  private async fileExists(filePath: string): Promise<boolean> {
    try {
      await fs.access(filePath)
      return true
    } catch {
      return false
    }
  }

  /**
   * Mark CAPTCHA as detected (stub)
   */
  private async markCaptchaDetected(): Promise<void> {
    console.log('🤖 CAPTCHA detected')
  }

  /**
   * Throttle requests (stub)
   */
  private async throttleRequests(): Promise<void> {
    // Rate limiting logic could go here
    await new Promise(resolve => setTimeout(resolve, 100))
  }

  /**
   * Validate session integrity (stub)
   */
  private async validateSessionIntegrity(): Promise<boolean> {
    return true // Simplified implementation
  }

  /**
   * Perform human-like interactions (stub)
   */
  private async performHumanLikeInteractions(): Promise<void> {
    // Human-like behavior could go here
  }

  /**
   * Generate session fingerprint (stub)
   */
  private async generateSessionFingerprint(): Promise<void> {
    this.sessionFingerprint = `fp_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`
  }

  /**
   * Simulate human scrolling (stub)
   */
  private async simulateHumanScrolling(): Promise<void> {
    if (!this.page) return
    
    // Simple scroll simulation
    await this.page.mouse.wheel(0, 100)
    await sleep(500)
    await this.page.mouse.wheel(0, -50)
  }

  /**
   * Test session persistence and return session information
   */
  async testSessionPersistence(): Promise<{
    isValid: boolean
    cookiesCount: number
    hasStorage: boolean
    details?: string
  }> {
    try {
      let cookiesCount = 0
      let hasStorage = false
      let details = ''

      // Check if session files exist
      if (await this.fileExists(COOKIES_FILE)) {
        const cookiesData = await fs.readFile(COOKIES_FILE, 'utf-8')
        const cookies = JSON.parse(cookiesData)
        cookiesCount = cookies.length || 0
      }

      if (await this.fileExists(SESSION_STORAGE_FILE)) {
        const storageData = await fs.readFile(SESSION_STORAGE_FILE, 'utf-8')
        const storage = JSON.parse(storageData)
        hasStorage = Object.keys(storage).length > 0
      }

      const isValid = cookiesCount > 0 && hasStorage
      
      details = `Cookies: ${cookiesCount}, Storage: ${hasStorage ? 'Yes' : 'No'}`

      return {
        isValid,
        cookiesCount,
        hasStorage,
        details
      }
    } catch (error) {
      console.error('Error testing session persistence:', error)
      return {
        isValid: false,
        cookiesCount: 0,
        hasStorage: false,
        details: 'Session test failed'
      }
    }
  }
}

/**
 * Add process cleanup handlers to ensure browser instances are properly cleaned up
 */
process.on('SIGTERM', async () => {
  console.log('🔄 SIGTERM received, cleaning up browser...')
  await TradingViewAutomation.getInstance().forceCleanup()
  process.exit(0)
})

process.on('SIGINT', async () => {
  console.log('🔄 SIGINT received, cleaning up browser...')
  await TradingViewAutomation.getInstance().forceCleanup()
  process.exit(0)
})

process.on('uncaughtException', async (error) => {
  console.error('💥 Uncaught exception, cleaning up browser:', error)
  await TradingViewAutomation.getInstance().forceCleanup()
  process.exit(1)
})

process.on('unhandledRejection', async (reason, promise) => {
  console.error('💥 Unhandled rejection, cleaning up browser:', reason)
  await TradingViewAutomation.getInstance().forceCleanup()
  process.exit(1)
})

export const tradingViewAutomation = TradingViewAutomation.getInstance()
