import { chromium } from 'playwright'
import type { ErpConfig, ErpSession } from '../../types/erp.types'
import { createLogger } from '../logger'
import { capturePageContext } from './erp-error-context'
import { attachPageDiagnostics, attachContextDiagnostics } from './page-diagnostics'

const log = createLogger('ErpAuthService')

// Timeout constants
const PAGE_LOAD_TIMEOUT = 10000
const LOGIN_RESULT_TIMEOUT = 15000
const FORCE_LOGIN_TIMEOUT = 5000

/**
 * ERP Authentication Service
 * Manages login session and browser lifecycle
 */
export class ErpAuthService {
  private config: ErpConfig
  private session: ErpSession | null = null

  constructor(config: ErpConfig) {
    this.config = config
  }

  /**
   * Login to ERP system and establish session
   */
  async login(): Promise<ErpSession> {
    if (this.session?.isLoggedIn) {
      return this.session
    }

    log.info('开始ERP登录', { url: this.config.url })

    // Launch browser with SSL certificate errors ignored
    const browser = await chromium.launch({
      headless: this.config.headless ?? false, // Use config or default to false
      slowMo: 100, // Slow down for debugging
      args: [
        '--ignore-certificate-errors',
        '--ignore-ssl-errors',
        '--ignore-certificate-errors-spki-list',
        '--disable-web-security' // Disable web security for internal VPN
      ]
    })

    log.debug('浏览器已启动', { headless: this.config.headless ?? false })

    const contextOptions: Parameters<typeof browser.newContext>[0] = {
      acceptDownloads: true,
      viewport: { width: 1920, height: 1080 },
      ignoreHTTPSErrors: true, // Ignore SSL certificate errors
      // Disable web security for internal VPN
      javaScriptEnabled: true
    }

    if (this.config.recordVideo && this.config.videoDir) {
      contextOptions.recordVideo = {
        dir: this.config.videoDir,
        size: { width: 1920, height: 1080 }
      }
      log.info('Video recording enabled', { dir: this.config.videoDir })
    }

    const context = await browser.newContext(contextOptions)

    const page = await context.newPage()
    attachPageDiagnostics(page)
    attachContextDiagnostics(context)

    // Navigate to login page (use actual login URL from Python code)
    const loginUrl = `${this.config.url}/yonbip/resources/uap/rbac/login/main/index.html`
    await page.goto(loginUrl)

    log.debug('已导航到登录页面')

    // Wait for page to load
    await page.waitForLoadState('domcontentloaded', { timeout: PAGE_LOAD_TIMEOUT })

    // Wait for iframe to be present
    await page.waitForSelector('#forwardFrame', {
      state: 'attached',
      timeout: LOGIN_RESULT_TIMEOUT
    })

    // Extract forwardFrame (Python: main_frame = page.locator("#forwardFrame").content_frame)
    // This is the main working frame for all subsequent operations
    const frameLocator = page.locator('#forwardFrame')
    const contentFrame = await frameLocator.contentFrame()
    log.debug('已获取 forwardFrame')

    if (!contentFrame) {
      log.error('Failed to access forwardFrame content frame', {
        ...(await capturePageContext(
          page,
          undefined,
          'auth.forwardFrame',
          undefined,
          undefined,
          'auth_forward_frame'
        ))
      })
      throw new Error('Failed to access forwardFrame content frame')
    }

    // Store reference to main frame for later use (Python returns this as main_frame)
    const mainFrame = contentFrame

    // Fill username using role-based locator (Python: get_by_role("textbox", name="用户名"))
    try {
      await contentFrame.getByRole('textbox', { name: '用户名' }).fill(this.config.username)
    } catch (e) {
      log.error('Failed to find username input', {
        error: e instanceof Error ? e.message : String(e),
        ...(await capturePageContext(
          page,
          undefined,
          'login.username',
          undefined,
          undefined,
          'login_username'
        ))
      })
      throw new Error(`Failed to find username input: ${e}`)
    }

    // Fill password using role-based locator (Python: get_by_role("textbox", name="密码"))
    try {
      await contentFrame.getByRole('textbox', { name: '密码' }).fill(this.config.password)
    } catch (e) {
      log.error('Failed to find password input', {
        error: e instanceof Error ? e.message : String(e),
        ...(await capturePageContext(
          page,
          undefined,
          'login.password',
          undefined,
          undefined,
          'login_password'
        ))
      })
      throw new Error(`Failed to find password input: ${e}`)
    }

    // Click login button using role-based locator (Python: get_by_role("button", name="登录"))
    try {
      await contentFrame.getByRole('button', { name: '登录' }).click()
    } catch (e) {
      log.error('Failed to click login button', {
        error: e instanceof Error ? e.message : String(e),
        ...(await capturePageContext(
          page,
          undefined,
          'login.button',
          undefined,
          undefined,
          'login_button'
        ))
      })
      throw new Error(`Failed to click login button: ${e}`)
    }

    await page.waitForLoadState('domcontentloaded', { timeout: PAGE_LOAD_TIMEOUT }).catch(() => {
      log.warn('Page load state check timed out, continuing')
    })

    await this.waitForLoginResult(mainFrame as unknown as import('playwright').Frame)

    // Create session with mainFrame (Python returns main_frame as part of login result)
    this.session = {
      browser,
      context,
      page,
      mainFrame: mainFrame as any, // Store forwardFrame content frame for subsequent operations
      isLoggedIn: true
    }

    log.info('ERP会话已建立')

    return this.session
  }

  /**
   * Wait for login result: success, failure, or force login confirmation
   */
  private async waitForLoginResult(mainFrame: import('playwright').Frame): Promise<void> {
    const successLocator = mainFrame.locator('.nc-workbench-icon')
    const errorLocator = mainFrame.getByText('名称或密码错误')
    const forceLoginButton = mainFrame.getByRole('button', { name: '确定' })

    try {
      await Promise.race([
        successLocator.waitFor({ state: 'visible', timeout: LOGIN_RESULT_TIMEOUT }),
        errorLocator.waitFor({ state: 'visible', timeout: LOGIN_RESULT_TIMEOUT }),
        forceLoginButton
          .waitFor({ state: 'visible', timeout: FORCE_LOGIN_TIMEOUT })
          .then(async () => {
            log.info('Force login dialog detected, clicking confirm')
            await forceLoginButton.click()
            await this.waitForLoginResult(mainFrame)
          })
      ])

      const hasError = await errorLocator.isVisible()
      if (hasError) {
        log.error('ERP login failed: incorrect username or password')
        throw new Error('ERP 登录失败：名称或密码错误')
      }

      log.info('Login successful')
    } catch (error) {
      if (error instanceof Error && error.message.includes('名称或密码错误')) {
        throw error
      }

      const hasError = await errorLocator.isVisible().catch(() => false)
      if (hasError) {
        log.error('ERP login failed: incorrect username or password (retry check)')
        throw new Error('ERP 登录失败：名称或密码错误')
      }

      log.info('Login successful')
    }
  }

  /**
   * Close browser and cleanup session
   */
  async close(): Promise<void> {
    if (this.session) {
      log.info('正在关闭ERP会话')
      await this.session.context.close()
      await this.session.browser.close()
      this.session = null
    }
  }

  /**
   * Get current session (must be logged in first)
   */
  getSession(): ErpSession {
    if (!this.session?.isLoggedIn) {
      log.error('getSession called without active session')
      throw new Error('Not logged in. Call login() first.')
    }
    return this.session
  }

  /**
   * Check if session is active
   */
  isActive(): boolean {
    return this.session?.isLoggedIn ?? false
  }
}
