import puppeteer, { Browser, Page } from 'puppeteer'
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
  }
}

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 acquireOperationLock(): void {
    if (this.operationLock) {
      throw new Error('Another operation is already in progress. Please wait.')
    }
    this.operationLock = true
  }

  private releaseOperationLock(): void {
    this.operationLock = false
  }

  // Singleton pattern
  static getInstance(): TradingViewAutomation {
    if (!TradingViewAutomation.instance) {
      TradingViewAutomation.instance = new TradingViewAutomation()
    }
    return TradingViewAutomation.instance
  }

  async init(forceCleanup: boolean = false): Promise<void> {
    this.acquireOperationLock()
    try {
      if (this.initPromise) {
        console.log('🔄 Initialization already in progress, waiting...')
        await this.initPromise
        return
      }

      if (forceCleanup && this.browser) {
        console.log('🧹 Force cleanup requested')
        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 })
    
    try {
      this.browser = await puppeteer.launch({
        headless: true,
        executablePath: process.env.PUPPETEER_EXECUTABLE_PATH || process.env.CHROME_PATH || '/usr/bin/chromium',
        timeout: 60000,
        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',
          '--window-size=1920,1080',
          '--user-agent=Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36'
        ]
      })

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

      // Load saved session if available
      await this.loadSession()

      console.log('✅ Browser initialized successfully')
    } catch (error) {
      console.error('❌ Failed to initialize browser:', error)
      await this.forceCleanup()
      throw error
    }
  }

  async forceCleanup(): Promise<void> {
    console.log('🧹 Force cleanup: Closing browser and resetting state...')
    try {
      if (this.browser) {
        await this.browser.close()
      }
    } catch (e) {
      console.log('WARNING: Error during browser cleanup:', e)
    }
    
    this.browser = null
    this.page = null
    this.isAuthenticated = false
    console.log('✅ Cleanup completed')
  }

  private async loadSession(): Promise<void> {
    if (!this.page) return
    
    try {
      // Load cookies
      if (await fs.access(COOKIES_FILE).then(() => true).catch(() => false)) {
        const cookiesData = await fs.readFile(COOKIES_FILE, 'utf8')
        const cookies = JSON.parse(cookiesData)
        await this.page.setCookie(...cookies)
        console.log('✅ Loaded saved cookies')
      }
    } catch (e) {
      console.log('WARNING: Could not load session:', e)
    }
  }

  private async saveSession(): Promise<void> {
    if (!this.page) return
    
    try {
      // Save cookies
      const cookies = await this.page.cookies()
      await fs.writeFile(COOKIES_FILE, JSON.stringify(cookies, null, 2))
      console.log('✅ Session saved')
    } catch (e) {
      console.log('WARNING: Could not save session:', e)
    }
  }

  async checkLoginStatus(): Promise<boolean> {
    if (!this.page) throw new Error('Page not initialized')
    
    console.log('CHECKING: Login status with 5 detection strategies...')
    
    try {
      // Strategy 1: Check for user account indicators (positive indicators)
      console.log('CHECKING: Strategy 1: Checking for user account indicators...')
      await this.takeDebugScreenshot('login_status_check')
      
      const userIndicators = [
        '.js-header-user-menu-button', // TradingView's main user button
        '[data-name="header-user-menu"]',
        '.tv-header__user-menu-button:not(.tv-header__user-menu-button--anonymous)',
        '.tv-header__user-menu-wrap'
      ]
      
      for (const selector of userIndicators) {
        try {
          const element = await this.page.$(selector)
          if (element) {
            const isVisible = await element.boundingBox()
            if (isVisible) {
              console.log('SUCCESS: Found user account element: ' + selector)
              return true
            }
          }
        } catch (e) {
          continue
        }
      }
      
      // Strategy 2: Check for anonymous/sign-in indicators (negative indicators)
      console.log('CHECKING: Strategy 2: Checking for anonymous/sign-in indicators...')
      const anonymousIndicators = [
        '.tv-header__user-menu-button--anonymous',
        '[data-name="header-user-menu-sign-in"]',
        'button:contains("Sign in")',
        'a:contains("Sign in")'
      ]
      
      for (const selector of anonymousIndicators) {
        try {
          const element = await this.page.$(selector)
          if (element) {
            const isVisible = await element.boundingBox()
            if (isVisible) {
              console.log('ERROR: Found anonymous indicator: ' + selector + ' - not logged in')
              return false
            }
          }
        } catch (e) {
          continue
        }
      }
      
      // Strategy 3: Check URL patterns
      console.log('CHECKING: Strategy 3: Checking URL patterns...')
      const currentUrl = this.page.url()
      if (currentUrl.includes('/signin') || currentUrl.includes('/login')) {
        console.log('ERROR: On login page - not logged in')
        return false
      }
      
      // Strategy 4: Check authentication cookies
      console.log('CHECKING: Strategy 4: Checking authentication cookies...')
      const cookies = await this.page.cookies()
      const authCookies = cookies.filter(cookie => 
        cookie.name.includes('auth') || 
        cookie.name.includes('session') ||
        cookie.name.includes('token')
      )
      if (authCookies.length === 0) {
        console.log('WARNING: No authentication cookies found')
      }
      
      // Strategy 5: Check for personal content
      console.log('CHECKING: Strategy 5: Checking for personal content...')
      const personalContentSelectors = [
        '[data-name="watchlist"]',
        '.tv-header__watchlist',
        '.js-backtesting-head'
      ]
      
      for (const selector of personalContentSelectors) {
        try {
          const element = await this.page.$(selector)
          if (element) {
            console.log('SUCCESS: Found personal content: ' + selector)
            return true
          }
        } catch (e) {
          continue
        }
      }
      
      // If we can't determine status clearly, assume not logged in to be safe
      console.log('WARNING: Could not determine login status clearly, assuming not logged in')
      return false
      
    } catch (e) {
      console.log('ERROR: Error checking login status:', e)
      return false
    }
  }

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

    const email = credentials?.email || TRADINGVIEW_EMAIL
    const password = credentials?.password || TRADINGVIEW_PASSWORD

    if (!email || !password) {
      throw new Error('TradingView credentials not provided')
    }

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

      console.log('🔐 Starting login process...')
      
      // Navigate to login page
      console.log('📄 Navigating to TradingView login page...')
      await this.page.goto('https://www.tradingview.com/accounts/signin/', {
        waitUntil: 'domcontentloaded',
        timeout: 30000
      })
      
      await sleep(3000)
      await this.takeDebugScreenshot('login_page_loaded')

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

      // Look for email login option
      console.log('CHECKING: Looking for Email login option...')
      
      const emailTriggers = [
        'button[data-overflow-tooltip-text="Email"]',
        'button:contains("Email")',
        'button:contains("email")',
        '[data-name="email"]'
      ]
      
      let emailFormVisible = false
      for (const trigger of emailTriggers) {
        try {
          const element = await this.page.$(trigger)
          if (element) {
            const isVisible = await element.boundingBox()
            if (isVisible) {
              console.log("TARGET: Found email trigger: " + trigger)
              await element.click()
              console.log('SUCCESS: Clicked email trigger')
              await sleep(3000)
              emailFormVisible = true
              break
            }
          }
        } catch (e) {
          continue
        }
      }

      // Fill email
      const emailInputSelectors = [
        'input[type="email"]',
        'input[name*="email"]',
        'input[name="username"]',
        'input[placeholder*="email" i]'
      ]
      
      let emailInput = null
      for (const selector of emailInputSelectors) {
        try {
          emailInput = await this.page.$(selector)
          if (emailInput) {
            const isVisible = await emailInput.boundingBox()
            if (isVisible) {
              console.log('SUCCESS: Found email input: ' + selector)
              break
            }
          }
        } catch (e) {
          continue
        }
      }
      
      if (!emailInput) {
        throw new Error('Could not find email input field')
      }
      
      await emailInput.click()
      await emailInput.type(email)
      console.log('✅ Filled email field')

      // Fill password
      const passwordInputSelectors = [
        'input[type="password"]',
        'input[name*="password"]'
      ]
      
      let passwordInput = null
      for (const selector of passwordInputSelectors) {
        try {
          passwordInput = await this.page.$(selector)
          if (passwordInput) {
            const isVisible = await passwordInput.boundingBox()
            if (isVisible) {
              console.log('SUCCESS: Found password input: ' + selector)
              break
            }
          }
        } catch (e) {
          continue
        }
      }
      
      if (!passwordInput) {
        throw new Error('Could not find password input field')
      }
      
      await passwordInput.click()
      await passwordInput.type(password)
      console.log('✅ Filled password field')

      // Submit form
      const submitSelectors = [
        'button[type="submit"]',
        'button:contains("Sign in")',
        'button:contains("Log in")',
        'button:contains("Login")'
      ]
      
      let submitted = false
      for (const selector of submitSelectors) {
        try {
          const button = await this.page.$(selector)
          if (button) {
            const isVisible = await button.boundingBox()
            if (isVisible) {
              console.log('SUCCESS: Found submit button: ' + selector)
              await button.click()
              submitted = true
              break
            }
          }
        } catch (e) {
          continue
        }
      }
      
      if (!submitted) {
        // Try pressing Enter on password field
        await passwordInput.press('Enter')
        console.log('INFO: Pressed Enter on password field')
      }

      console.log('⏳ Waiting for login completion...')
      await sleep(5000)

      // Check for errors
      const errorSelectors = [
        '.tv-alert-dialog__text',
        '.tv-dialog__error',
        '[data-name="auth-error-message"]',
        '.error-message'
      ]
      
      for (const selector of errorSelectors) {
        try {
          const errorElement = await this.page.$(selector)
          if (errorElement) {
            const errorText = await this.page.evaluate(el => el.textContent, errorElement)
            if (errorText && errorText.trim()) {
              await this.takeDebugScreenshot('login_error')
              throw new Error('Login failed: ' + errorText.trim())
            }
          }
        } catch (e) {
          continue
        }
      }

      // Verify login success
      await sleep(3000)
      const loginSuccess = await this.checkLoginStatus()
      
      if (loginSuccess) {
        console.log('✅ Login successful!')
        this.isAuthenticated = true
        await this.saveSession()
        return true
      } else {
        await this.takeDebugScreenshot('login_verification_failed')
        throw new Error('Login verification failed - still appears not logged in')
      }

    } catch (error) {
      console.error('❌ Login failed:', error)
      await this.takeDebugScreenshot('login_error')
      throw error
    }
  }

  async takeDebugScreenshot(prefix: string = 'debug'): Promise<string> {
    if (!this.page) throw new Error('Page not initialized')
    
    try {
      const timestamp = Date.now()
      const filename = `${prefix}_${timestamp}.png`
      const filepath = path.join(process.cwd(), 'screenshots', filename)
      
      // Ensure screenshots 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}`)
      return filepath
    } catch (error) {
      console.error('Error taking screenshot:', error)
      throw error
    }
  }

  async navigateToSymbol(symbol: string, timeframe?: string): Promise<boolean> {
    if (!this.page) throw new Error('Page not initialized')
    
    try {
      console.log(`🎯 Navigating to symbol: ${symbol}`)
      
      // Construct TradingView URL
      const baseUrl = 'https://www.tradingview.com/chart/'
      const params = new URLSearchParams()
      params.set('symbol', symbol)
      if (timeframe) {
        params.set('interval', timeframe)
      }
      
      const url = `${baseUrl}?${params.toString()}`
      console.log(`📍 Navigating to: ${url}`)
      
      await this.page.goto(url, {
        waitUntil: 'domcontentloaded',
        timeout: 30000
      })
      
      // Wait for chart to load
      await sleep(5000)
      
      // Wait for chart container
      await this.page.waitForSelector('.chart-container, #chart-container, [data-name="chart"]', {
        timeout: 30000
      })
      
      console.log('✅ Chart loaded successfully')
      return true
      
    } catch (error) {
      console.error('❌ Failed to navigate to symbol:', error)
      throw error
    }
  }

  async takeScreenshot(options: { filename?: string, fullPage?: boolean } = {}): Promise<string> {
    if (!this.page) throw new Error('Page not initialized')
    
    try {
      const timestamp = Date.now()
      const filename = options.filename || `screenshot_${timestamp}.png`
      const filepath = path.join(process.cwd(), 'screenshots', filename)
      
      // Ensure screenshots directory exists
      await fs.mkdir(path.dirname(filepath), { recursive: true })
      
      await this.page.screenshot({ 
        path: filepath as `${string}.png`, 
        fullPage: options.fullPage || false,
        type: 'png'
      })
      
      console.log(`📸 Screenshot saved: ${filename}`)
      return filepath
    } catch (error) {
      console.error('Error taking screenshot:', error)
      throw error
    }
  }
}

// Export default instance
export default TradingViewAutomation.getInstance()
