import { app, shell, BrowserWindow, ipcMain, nativeImage, clipboard, Menu, Tray } from 'electron'
import { join } from 'path'
import { electronApp, optimizer, is } from '@electron-toolkit/utils'
import icon from '../../resources/icon.png?asset'
import { WechatDb } from './wechat-db'
import { VoiceService } from './voice-service'
import { StickerService } from './sticker-service'
import { parseMessageContent } from './message-parser'
import { ImageDecryptService } from './image-decrypt-service'
import { exportGroupReport } from './group-report-service'
import { GroupReportExportRequest } from '../shared/group-report'
import { DatabaseKeyStore } from './database-key-store'
import { KeyServiceMac } from './key-service-mac'
import * as chat from './services/chat-service'
import {
  apiServer
} from './http-server'
import {
  loadSettings,
  saveSettings,
  getSettingsPath,
  AppSettings
} from './services/settings-store'
import { installSafeConsole } from './safe-log'

// electron-vite can close the child's stdout/stderr after spawning Electron.
// Plain console.error then throws EPIPE on a closed pipe and crashes the IPC
// handler. Wrap console.* before any other module logs anything.
installSafeConsole()

let voiceService: VoiceService | null = null
let imageDecryptService: ImageDecryptService | null = null
let stickerService: StickerService | null = null
const databaseKeyStore = new DatabaseKeyStore()
const keyServiceMac = new KeyServiceMac()
let tray: Tray | null = null
const BUILD_MARK = 'wechat4-local-http-api-2026-07-03'
const TRAY_MODE =
  process.argv.includes('--tray') || (process.env['WXE_TRAY'] || '').toString() === '1'

// WechatExplorer's WCDB native library runs InitProtection before wcdb_init.
// In dev, matching the host app name avoids failing the native protection gate.
app.setName('WechatExplorer')

function createWindow(): void {
  // 创建浏览器窗口
  const mainWindow = new BrowserWindow({
    width: 1400,
    height: 800,
    show: false,
    autoHideMenuBar: true,
    ...(process.platform === 'linux' ? { icon } : {}),
    webPreferences: {
      preload: join(__dirname, '../preload/index.js'),
      sandbox: false
    }
  })

  mainWindow.on('ready-to-show', () => {
    mainWindow.show()
  })

  mainWindow.webContents.setWindowOpenHandler((details) => {
    shell.openExternal(details.url)
    return { action: 'deny' }
  })

  // 基于 electron-vite cli 的渲染器 HMR
  // 加载开发环境的远程 URL 或生产环境的本地 html 文件
  if (is.dev && process.env['ELECTRON_RENDERER_URL']) {
    mainWindow.loadURL(process.env['ELECTRON_RENDERER_URL'])
  } else {
    mainWindow.loadFile(join(__dirname, '../renderer/index.html'))
  }
}

// 当 Electron 完成初始化并准备好创建浏览器窗口时，将调用此方法
// 某些 API 只能在此事件发生后使用
app.whenReady().then(async () => {
  console.log(`WechatExplorer main build: ${BUILD_MARK}`)
  // 为窗口设置应用程序用户模型 ID
  electronApp.setAppUserModelId('com.electron')

  // 在开发环境中默认按 F12 打开或关闭 DevTools
  // 在生产环境中忽略 CommandOrControl + R
  // 参见 https://github.com/alex8088/electron-toolkit/tree/master/packages/utils
  app.on('browser-window-created', (_, window) => {
    optimizer.watchWindowShortcuts(window)
  })

  // IPC test
  ipcMain.on('ping', () => console.log('pong'))

  ipcMain.handle('db:init', (_, key: string) => {
    try {
      const trimmedKey = String(key || '').trim()
      console.log(`db:init build=${BUILD_MARK} keyLength=${trimmedKey.length}`)
      const nextWechatDb = new WechatDb(key)
      chat.setChatDb(nextWechatDb)
      const wcdb4Client = nextWechatDb.getWcdb4Client()
      voiceService = new VoiceService(wcdb4Client)
      stickerService = new StickerService(wcdb4Client)
      const monitoring = wcdb4Client.startMonitor((type, json) => {
        for (const window of BrowserWindow.getAllWindows()) {
          if (!window.isDestroyed()) window.webContents.send('wcdb-change', { type, json })
        }
      })
      imageDecryptService = null
      return { success: true, monitoring }
    } catch (error) {
      console.error('Failed to init DB:', error)
      return { success: false, error: error instanceof Error ? error.message : String(error) }
    }
  })

  ipcMain.handle('key:getSavedDbKey', async () => databaseKeyStore.load())

  ipcMain.handle('key:pasteAndSaveDbKey', async () => {
    const clipboardKey = clipboard.readText().trim()
    return databaseKeyStore.save(clipboardKey)
  })

  ipcMain.handle('key:saveDbKey', async (_, key: string) => databaseKeyStore.save(String(key || '')))

  ipcMain.handle('key:clearSavedDbKey', async () => databaseKeyStore.clear())

  ipcMain.handle('key:autoGetDbKey', async (event) => {
    const result = await keyServiceMac.autoGetDbKey((message) => {
      if (!event.sender.isDestroyed()) event.sender.send('key:dbKeyStatus', { message })
    })
    if (!result.success || !result.key) return result

    const saved = await databaseKeyStore.save(result.key)
    return {
      ...result,
      saved: saved.success,
      warning: saved.success ? undefined : saved.error
    }
  })

  ipcMain.handle('db:getContacts', (_, filter?: string) => chat.listContacts(filter))

  ipcMain.handle('db:getMessages', (_, userMd5: string, startTime?: number, endTime?: number) =>
    chat.listMessages(userMd5, startTime, endTime)
  )

  ipcMain.handle('db:getGroupSnapshot', (_, userMd5: string) => chat.getGroupSnapshot(userMd5))

  ipcMain.handle('db:search', (_, keyword: string) => chat.searchMessages(keyword))

  ipcMain.handle(
    'ai:chat',
    async (
      _,
      messages: { role: string; content: string }[],
      options?: { apiKey?: string; model?: string; baseURL?: string }
    ) => {
      // @ts-ignore: vite env
      const apiKey = options?.apiKey || import.meta.env.VITE_DEEPSEEK_API_KEY
      const model = options?.model || import.meta.env.VITE_AI_MODEL || 'deepseek-chat'
      const baseURL =
        options?.baseURL || import.meta.env.VITE_AI_BASE_URL || 'https://api.deepseek.com'

      if (!apiKey) {
        return { success: false, error: '未配置 API Key' }
      }

      // 动态导入以避免如果未安装或初始类型缺失的问题
      const { OpenAI } = await import('openai')

      const openai = new OpenAI({
        baseURL,
        apiKey
      })
      try {
        const completion = await openai.chat.completions.create({
          // eslint-disable-next-line @typescript-eslint/no-explicit-any
          messages: messages as any,
          model: model
        })
        return { success: true, data: completion.choices[0].message.content }
      } catch (error: unknown) {
        console.error('AI API Error:', error)
        const errorMessage = error instanceof Error ? error.message : 'Unknown error'
        return { success: false, error: errorMessage }
      }
    }
  )

  ipcMain.handle('copy-image', async (_, base64String) => {
    try {
      const image = nativeImage.createFromDataURL(base64String)
      clipboard.writeImage(image)
      return { success: true }
    } catch (error: unknown) {
      return { success: false, error: error instanceof Error ? error.message : 'Unknown error' }
    }
  })

  ipcMain.handle('report:export', async (_, request: GroupReportExportRequest) => {
    return exportGroupReport(request)
  })

  ipcMain.handle('report:reveal', async (_, filePath: string) => {
    try {
      shell.showItemInFolder(filePath)
      return { success: true }
    } catch (error) {
      return { success: false, error: error instanceof Error ? error.message : String(error) }
    }
  })

  ipcMain.handle(
    'db:getVoiceData',
    async (_, sessionId: string, localId: number, createTime: number, svrId?: string | number) => {
      if (!voiceService) {
        return { success: false, error: 'VoiceService 未初始化' }
      }
      return voiceService.resolveVoice(sessionId, localId, createTime, svrId)
    }
  )

  ipcMain.handle('db:parseMessage', async (_, content: string, messageType: number) => {
    return parseMessageContent(content, messageType)
  })

  ipcMain.handle(
    'db:getImage',
    async (_, imageMd5?: string, imageDatNameOrThumb?: string | boolean, _sessionId?: string) => {
      void _sessionId
      if (!imageDecryptService) {
        // 从环境变量获取密钥
        const xorKey = import.meta.env.VITE_IMAGE_XOR_KEY || '0x40'
        const aesKey = import.meta.env.VITE_IMAGE_AES_KEY || ''
        if (!aesKey) {
          return { success: false, error: '未配置图片解密密钥' }
        }
        imageDecryptService = new ImageDecryptService(xorKey, aesKey, chat.getChatDb()?.getWcdb4Client())
      }

      const imageDatName = typeof imageDatNameOrThumb === 'string' ? imageDatNameOrThumb : undefined
      const filePath = imageDecryptService.findImageFile(imageMd5, imageDatName)
      if (!filePath) {
        return { success: false, error: '未找到图片文件' }
      }

      const base64 = imageDecryptService.decryptImageToBase64(filePath)
      if (!base64) {
        return { success: false, error: '图片解密失败' }
      }

      return { success: true, data: base64 }
    }
  )

  ipcMain.handle('db:getSticker', async (_, cdnUrl?: string, md5?: string) => {
    if (!stickerService) {
      stickerService = new StickerService(chat.getChatDb()?.getWcdb4Client())
    }
    return stickerService.resolveSticker(cdnUrl, md5)
  })

  // -------- Settings & API service --------

  ipcMain.handle('settings:get', () => ({
    settings: loadSettings(),
    settingsPath: getSettingsPath()
  }))

  ipcMain.handle('settings:set', (_, patch: Partial<AppSettings>) => {
    const merged = saveSettings({ ...loadSettings(), ...patch })
    return { settings: merged, settingsPath: getSettingsPath() }
  })

  ipcMain.handle('settings:getSelf', () => {
    const info = chat.getSelfAccountInfo()
    if (!info) return { ready: false }
    return { ready: true, info }
  })

  ipcMain.handle('db:testConnection', (_, key: string, accountRoot?: string) => {
    return chat.testConnection(key, accountRoot)
  })

  ipcMain.handle('db:reopenWithRoot', (_, accountRoot: string) => {
    const ok = chat.reopenWithRoot(accountRoot)
    if (!ok) return { success: false, error: '数据库未初始化或重新打开失败' }
    const info = chat.getSelfAccountInfo()
    return { success: true, info }
  })

  ipcMain.handle('api:getStatus', () => apiServer.getState())

  ipcMain.handle('api:start', async (_, host?: string, port?: number) => {
    const settings = loadSettings()
    const target = {
      host: host || settings.apiHost,
      port: port || settings.apiPort
    }
    if (host || port) saveSettings({ ...settings, ...target })
    return apiServer.start(target.host, target.port)
  })

  ipcMain.handle('api:stop', async () => apiServer.stop())

  ipcMain.handle('api:toggle', async (_, enabled: boolean) => {
    const settings = saveSettings({ ...loadSettings(), apiEnabled: enabled })
    if (enabled) {
      return apiServer.start(settings.apiHost, settings.apiPort)
    }
    return apiServer.stop()
  })

  createWindow()

  // 启动本地 HTTP API(根据 settings.apiEnabled 控制)
  const settings = loadSettings()
  if (settings.apiEnabled) {
    await apiServer.start(settings.apiHost, settings.apiPort)
  }

  if (TRAY_MODE) {
    app.dock?.hide()
    setupTray()
  }

  app.on('activate', function () {
    // 在 macOS 上，当点击 dock 图标且没有其他窗口打开时，
    // 通常会在应用程序中重新创建一个窗口。
    if (BrowserWindow.getAllWindows().length === 0) createWindow()
  })
})

// 当所有窗口关闭时退出，除了 macOS。在那里，
// 应用程序及其菜单栏通常会保持活动状态，直到用户
// 显式使用 Cmd + Q 退出。
app.on('window-all-closed', () => {
  if (TRAY_MODE) return
  if (process.platform !== 'darwin') {
    app.quit()
  }
})

app.on('before-quit', async () => {
  chat.setChatDb(null)
  await apiServer.stop().catch(() => undefined)
  if (tray) {
    tray.destroy()
    tray = null
  }
})

function showMainWindow(): void {
  if (TRAY_MODE) app.dock?.show().catch(() => undefined)
  const wins = BrowserWindow.getAllWindows()
  if (wins.length === 0) {
    createWindow()
    return
  }
  const win = wins[0]
  if (win.isMinimized()) win.restore()
  win.show()
  win.focus()
}

function buildTrayMenu(): Menu {
  return Menu.buildFromTemplate([
    {
      label: '打开主窗口',
      click: () => showMainWindow()
    },
    {
      label: 'API 状态',
      click: () => showMainWindow()
    },
    { type: 'separator' },
    {
      label: '退出 WechatExplorer',
      click: () => {
        tray?.destroy()
        tray = null
        app.quit()
      }
    }
  ])
}

function setupTray(): void {
  if (tray) return
  try {
    const image = nativeImage.createFromPath(join(__dirname, '../../resources/icon.png'))
    tray = new Tray(image.isEmpty() ? nativeImage.createEmpty() : image)
    tray.setToolTip('WechatExplorer')
    tray.setContextMenu(buildTrayMenu())
    tray.on('click', () => showMainWindow())
  } catch (error) {
    console.warn('[Tray] Failed to create tray:', error)
  }
}
