import Electrobun, { BrowserWindow, Updater, Utils } from 'electrobun/bun'
import { applyApplicationMenu, setupApplicationMenuEvents } from './menu.js'
import {
  applyMacOSWindowEffects,
  MAC_TRAFFIC_LIGHT_OFFSET
} from './macos-window-effects.js'
import { sendToRenderer, setMessageSender } from './notify.js'
import {
  clampWindowToWorkArea,
  isWindowWorkAreaMaximized
} from './windows-work-area.js'

const isMacOS = process.platform === 'darwin'
const isWindows = process.platform === 'win32'
const WINDOWS_STARTUP_MAXIMIZE_DELAY_MS = 1500

const DEV_SERVER_PORT = 5780
const DEV_SERVER_URL = `http://localhost:${DEV_SERVER_PORT}`
import { findProtocolUrl, setSingleInstanceHandlers } from './single-instance.js'

let mainWindow = null
let webviewDomReady = false
const pendingNavigations = []

export function getMainWindow() {
  return mainWindow
}

export function showMainWindow() {
  if (!mainWindow) return

  if (mainWindow.isMinimized?.()) {
    mainWindow.restore?.()
  }

  mainWindow.show?.()
  mainWindow.activate?.()
}

export async function getMainViewUrl() {
  const channel = await Updater.localInfo.channel()
  if (channel === 'dev' || process.env.NODE_ENV === 'development') {
    try {
      await fetch(DEV_SERVER_URL, { method: 'HEAD' })
      console.log(`Using Vite dev server at ${DEV_SERVER_URL}`)
      return DEV_SERVER_URL
    } catch {
      console.warn(
        'Vite dev server not running. Start it with `bun run dev:renderer`.'
      )
    }
  }

  return 'views://mainview/index.html'
}

function deliverNavigation(redirectPath) {
  sendToRenderer('navigate', redirectPath)

  if (!mainWindow) return

  if (mainWindow.isMinimized?.()) {
    mainWindow.restore?.()
  }

  mainWindow.show?.()
  mainWindow.activate?.()
}

function flushPendingNavigations() {
  if (!mainWindow || !webviewDomReady) {
    return
  }

  while (pendingNavigations.length > 0) {
    const redirectPath = pendingNavigations.shift()
    setTimeout(() => deliverNavigation(redirectPath), 100)
  }
}

function sendNavigateToRenderer(redirectPath) {
  if (!redirectPath || typeof redirectPath !== 'string') {
    return
  }

  if (!mainWindow || !webviewDomReady) {
    pendingNavigations.push(redirectPath)
    return
  }

  setTimeout(() => deliverNavigation(redirectPath), 100)
}

export function openInternalUrl(url) {
  sendNavigateToRenderer(url)
  return true
}

const PROTOCOL_PREFIX = 'farmcontrol://'

function parseDeepLinkPath(url) {
  if (!url || typeof url !== 'string') {
    return null
  }

  if (url.startsWith('/')) {
    return url
  }

  if (!url.startsWith(`${PROTOCOL_PREFIX}app`)) {
    return null
  }

  const redirectPath = url.slice(`${PROTOCOL_PREFIX}app`.length) || '/'
  const normalizedPath = redirectPath.startsWith('/')
    ? redirectPath
    : `/${redirectPath}`

  try {
    return decodeURI(normalizedPath)
  } catch {
    return normalizedPath
  }
}

export function handleDeepLink(url) {
  const path = parseDeepLinkPath(url)
  if (!path) {
    showMainWindow()
    return false
  }

  sendNavigateToRenderer(path)
  return true
}

export function handleDeepLinkFromArgv(launchUrl) {
  if (process.platform === 'darwin') return

  const url = launchUrl || findProtocolUrl(process.argv)
  if (url) {
    handleDeepLink(url)
  }
}

function broadcastWindowState() {
  sendToRenderer('windowState', getWindowState())
}

function applyStartupWindowState(window) {
  if (!window) return

  if (isMacOS) {
    window.maximize?.()
  }

  setTimeout(broadcastWindowState, 100)
}

function syncWindowsWebviewLayout(window) {
  if (!window?.getSize || !window?.setSize) {
    return
  }

  const { width, height } = window.getSize()
  if (!width || !height) {
    return
  }

  // Re-apply the current size so the native webview relayouts to the window.
  window.setSize(width, height)
}

function handleWindowsWindowChange(window) {
  if (clampWindowToWorkArea(window)) {
    syncWindowsWebviewLayout(window)
  }

  broadcastWindowState()
}

function applyWindowsStartupWindowState(window) {
  if (!window) return

  setTimeout(() => {
    window.maximize?.()
    // maximize() settles asynchronously; sync state after the frame updates.
    setTimeout(() => handleWindowsWindowChange(window), 100)
  }, WINDOWS_STARTUP_MAXIMIZE_DELAY_MS)
}

function setupWindowEvents(window) {
  // Electrobun emits resize/focus, not Electron's maximize/fullscreen events.
  const onWindowChange = isWindows
    ? () => handleWindowsWindowChange(window)
    : broadcastWindowState

  window.on?.('resize', onWindowChange)
  window.on?.('focus', broadcastWindowState)
  window.on?.('move', onWindowChange)
}

export function setupMainWindowMessaging(window = mainWindow) {
  if (!window) {
    return
  }

  setMessageSender((channel, data) => {
    try {
      const webview = window.webview
      const channelLiteral = JSON.stringify(channel)
      const payloadLiteral = JSON.stringify(data ?? null)

      // Prefer direct JS dispatch — WebSocket RPC pushes can be deferred by
      // WKWebView until the next interaction during native window transitions.
      if (webview?.executeJavascript) {
        webview.executeJavascript(
          `window.__farmcontrolDispatchRpcMessage?.(${channelLiteral}, ${payloadLiteral})`
        )
        return true
      }

      const send = webview?.rpc?.send
      if (!send) {
        console.warn(
          `No RPC sender available for channel: ${channel}. Is the window ready?`
        )
        return false
      }

      send[channel](data)
      return true
    } catch (error) {
      console.warn(`Failed to send RPC message on channel: ${channel}`, error)
      return false
    }
  })
}

export async function createMainWindow(rpc) {
  const url = await getMainViewUrl()

  mainWindow = new BrowserWindow({
    title: 'Farm Control',
    url,
    rpc,
    titleBarStyle: 'hiddenInset',
    ...(isMacOS
      ? { transparent: true, trafficLightOffset: MAC_TRAFFIC_LIGHT_OFFSET }
      : {}),
    frame: {
      width: 1200,
      height: 800,
      x: 100,
      y: 100
    }
  })

  if (isMacOS) {
    applyMacOSWindowEffects(mainWindow)
  }

  setupMainWindowMessaging(mainWindow)

  if (isMacOS) {
    applyApplicationMenu()
    setupApplicationMenuEvents({
      onNavigate: sendNavigateToRenderer,
      onToggleDevTools: () => {
        mainWindow?.webview?.toggleDevTools?.()
      },
      onCheckForUpdates: () => {
        sendToRenderer('checkForUpdates')
      }
    })
  }

  setupWindowEvents(mainWindow)
  applyStartupWindowState(mainWindow)

  Electrobun.events.on('open-url', (event) => {
    const url = event?.data?.url
    if (url) {
      handleDeepLink(url)
    }
  })

  return new Promise((resolve) => {
    mainWindow.webview.on('dom-ready', () => {
      webviewDomReady = true

      if (isWindows) {
        applyWindowsStartupWindowState(mainWindow)
      }

      flushPendingNavigations()
      resolve(mainWindow)
    })
  })
}

export async function setupDevAuthServer() {
  const env = (process.env.NODE_ENV || 'development').trim()
  if (env !== 'development') return

  const express = (await import('express')).default
  const app = express()
  const port = 3500

  app.use((req, res) => {
    const redirectPath = req.originalUrl
    res.send(`Open Farmcontrol to continue... (Redirect path: ${redirectPath})`)
    sendNavigateToRenderer(redirectPath)
  })

  app.listen(port, () => {})
}

export function setupWindowsDeepLinkHandling() {
  if (process.platform === 'darwin') {
    return
  }

  setSingleInstanceHandlers({
    onDeepLink: handleDeepLink,
    onFocus: showMainWindow
  })
}

export function getWindowState() {
  if (!mainWindow) {
    return { isFullScreen: false, isMaximized: false }
  }

  return {
    isFullScreen: mainWindow.isFullScreen?.() ?? false,
    isMaximized: isWindows
      ? isWindowWorkAreaMaximized(mainWindow)
      : (mainWindow.isMaximized?.() ?? false)
  }
}

export function handleWindowControl(action) {
  if (!mainWindow && action !== 'quit') return

  switch (action) {
    case 'minimize':
      mainWindow.minimize?.()
      break
    case 'maximize': {
      const currentlyMaximized = isWindows
        ? isWindowWorkAreaMaximized(mainWindow)
        : (mainWindow.isMaximized?.() ?? false)

      if (currentlyMaximized) {
        mainWindow.unmaximize?.()
      } else {
        mainWindow.maximize?.()
      }

      if (isWindows) {
        // Frame updates after maximize/unmaximize are async on Windows.
        setTimeout(() => handleWindowsWindowChange(mainWindow), 100)
      }
      break
    }
    case 'fullscreen':
      if (mainWindow.isFullScreen?.()) {
        mainWindow.setFullScreen?.(false)
      } else {
        mainWindow.setFullScreen?.(true)
      }
      setTimeout(broadcastWindowState, 100)
      break
    case 'close':
      mainWindow.close?.()
      break
    case 'quit':
      Utils.quit()
      break
    case 'toggle-devtools':
      mainWindow?.webview?.toggleDevTools?.()
      break
    default:
      break
  }
}

export function sendNavigationGesture(direction) {
  sendToRenderer('navigationGesture', direction)
}

export function setupNavigationGestures(window) {
  if (!window) return

  if (process.platform === 'darwin') {
    window.on?.('swipe', (_event, direction) => {
      if (direction === 'left') {
        sendNavigationGesture('back')
      } else if (direction === 'right') {
        sendNavigationGesture('forward')
      }
    })
  }

  window.on?.('app-command', (_event, command) => {
    if (command === 'browser-backward') {
      sendNavigationGesture('back')
    } else if (command === 'browser-forward') {
      sendNavigationGesture('forward')
    }
  })
}

export function openExternalUrl(url) {
  console.log('openExternalUrl', url)
  Utils.openExternal(url)
}
