import { chromium } from 'playwright'

import { runtimeConfig } from '../../config/runtime.js'
import {
  buildChromiumLaunchOptions as buildChromiumLaunchOptionsBase,
  resolveBrowserLaunchOptions as resolveBrowserLaunchOptionsBase,
} from './session-browser-config.js'
import { CHROME_PATH } from './session-service-config.js'

type BrowserLike = {
  on: (event: 'disconnected', listener: () => void) => void
  close: () => Promise<void>
  newContext: (options?: Record<string, unknown>) => Promise<any>
}

type SessionServiceErrorOptions = {
  statusCode?: number
  errorCode?: string
}

let browserPromise: Promise<BrowserLike> | null = null

export class SessionServiceError extends Error {
  statusCode: number
  errorCode: string

  constructor(message: string, { statusCode = 500, errorCode = 'session_error' }: SessionServiceErrorOptions = {}) {
    super(message)
    this.name = 'SessionServiceError'
    this.statusCode = statusCode
    this.errorCode = errorCode
  }
}

export async function warmupTencentBrowser() {
  const browserLaunch = resolveBrowserLaunchOptions()

  if (!browserLaunch.prewarm) {
    return {
      warmed: false,
      reason: 'disabled',
      browserLaunch,
    }
  }

  await ensureBrowser()

  return {
    warmed: true,
    reason: 'ready',
    browserLaunch,
  }
}

export async function ensureBrowser(): Promise<BrowserLike> {
  if (!browserPromise) {
    const launchOptions = resolveBrowserLaunchOptions()
    browserPromise = (chromium.launch(buildChromiumLaunchOptions(launchOptions)) as Promise<BrowserLike>)
      .then((browser) => {
        browser.on('disconnected', () => {
          browserPromise = null
        })
        return browser
      })
      .catch((error) => {
        browserPromise = null
        throw error
      })
  }

  return browserPromise
}

export function resolveBrowserLaunchOptions() {
  return resolveBrowserLaunchOptionsBase({
    browserConfig: runtimeConfig.browser,
    chromePath: CHROME_PATH,
  })
}

export async function closeBrowserIfIdle({ activeSessionCount = 0 }: { activeSessionCount?: number } = {}): Promise<void> {
  if (activeSessionCount > 0 || !browserPromise) {
    return
  }

  if (resolveBrowserLaunchOptions().keepAlive) {
    return
  }

  try {
    const browser = await browserPromise
    await browser.close()
  } catch {
    // ignore browser close failures
  } finally {
    browserPromise = null
  }
}

function buildChromiumLaunchOptions(launchOptions: ReturnType<typeof resolveBrowserLaunchOptionsBase>) {
  return buildChromiumLaunchOptionsBase(launchOptions, { chromePath: CHROME_PATH })
}
