import { join } from 'node:path';

import { app, BrowserWindow, Notification, session, shell, systemPreferences } from 'electron';
import log from 'electron-log/main';

import { APP_INDEX_URL, APP_ORIGIN, DEV_SERVER_URL, getResourcePath } from './config';

/**
 * 主窗口管理
 * - 加载目标：开发模式加载 web-antd 的 vite dev server（未起服务时自动重试）；打包模式加载 app://local/
 * - 安全基线：contextIsolation + sandbox 开启，webSecurity 不关闭；window.open 一律交给系统浏览器
 * - 关闭行为：双平台都是「关窗隐藏、应用常驻」（Windows 走托盘、mac 走 Dock），真正退出走托盘菜单/Cmd+Q
 * - 权限控制：只放行业务需要的媒体（IM 音视频）/ 通知 / 剪贴板 / 全屏，其余默认拒绝
 */

let mainWindow: BrowserWindow | null = null;
/** 标记「真正退出」：常驻模式下 close 只是隐藏，只有 quit 流程才放行关闭 */
let isQuitting = false;
/** Windows 首次关窗提示「已最小化到托盘」，只提示一次避免打扰 */
let hasShownHideTip = false;
/** 加载失败重试定时器，避免叠加多个重试 */
let retryTimer: NodeJS.Timeout | null = null;

/** 主窗口要加载的页面地址：打包后走自定义协议，开发走 dev server */
const PAGE_URL = app.isPackaged ? APP_INDEX_URL : DEV_SERVER_URL;

export function markQuitting(): void {
  isQuitting = true;
}

export function getMainWindow(): BrowserWindow | null {
  return mainWindow;
}

/** 判断 URL 是否属于信任的页面来源（本地协议或开发服务器），IPC 与权限校验共用 */
export function isTrustedPageUrl(url: string): boolean {
  return url.startsWith(APP_ORIGIN) || url.startsWith(DEV_SERVER_URL);
}

/**
 * 会话级权限控制：白名单内且来源可信才放行
 * media 供 IM 音视频通话；notifications 供 HTML5 Notification（业务主要走主进程通知，这里兜底）
 */
function setupPermissionHandlers(): void {
  const allowedPermissions = new Set([
    'media',
    'notifications',
    'clipboard-read',
    'clipboard-sanitized-write',
    'fullscreen',
  ]);

  session.defaultSession.setPermissionRequestHandler((webContents, permission, callback) => {
    const trusted = isTrustedPageUrl(webContents.getURL());
    if (!trusted || !allowedPermissions.has(permission)) {
      callback(false);
      return;
    }
    // mac 首次使用摄像头/麦克风必须先过系统 TCC 授权弹窗，否则页面侧拿不到设备
    if (permission === 'media' && process.platform === 'darwin') {
      Promise.all([
        systemPreferences.askForMediaAccess('microphone'),
        systemPreferences.askForMediaAccess('camera'),
      ])
        .then(() => callback(true))
        // TCC 弹窗异常时仍放行，让页面侧走自己的错误提示
        .catch(() => callback(true));
      return;
    }
    callback(true);
  });

  session.defaultSession.setPermissionCheckHandler((_webContents, permission, requestingOrigin) => {
    return allowedPermissions.has(permission) && isTrustedPageUrl(requestingOrigin);
  });
}

/** 加载失败时的内置错误页（数据 URL 形式，不依赖任何本地文件） */
function buildErrorPageUrl(reason: string): string {
  const html = `<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="utf-8" />
  <title>加载失败</title>
  <style>
    body { margin: 0; display: flex; align-items: center; justify-content: center; height: 100vh;
      font-family: "Microsoft YaHei", "PingFang SC", sans-serif; background: #f5f6f8; color: #333; }
    .box { text-align: center; }
    .title { font-size: 18px; font-weight: 600; margin-bottom: 8px; }
    .desc { font-size: 13px; color: #999; }
  </style>
</head>
<body>
  <div class="box">
    <div class="title">页面加载失败，正在自动重试…</div>
    <div class="desc">请检查网络连接（${reason}）</div>
  </div>
</body>
</html>`;
  return `data:text/html;charset=utf-8,${encodeURIComponent(html)}`;
}

/**
 * 创建主窗口
 * @param options.startHidden 开机自启（--hidden）场景下静默启动，不弹窗打扰用户
 */
export function createMainWindow(options: { startHidden: boolean }): BrowserWindow {
  setupPermissionHandlers();

  mainWindow = new BrowserWindow({
    width: 1440,
    height: 900,
    minWidth: 1100,
    minHeight: 700,
    show: false,
    autoHideMenuBar: true,
    title: '萧康云医',
    // Windows/Linux 窗口与任务栏图标；mac 使用打包进 app 的 icns，无需在此指定
    icon: process.platform === 'darwin' ? undefined : getResourcePath('icon.png'),
    webPreferences: {
      preload: join(__dirname, '../preload/index.js'),
      contextIsolation: true,
      nodeIntegration: false,
      sandbox: true,
      spellcheck: false,
    },
  });

  // 首次渲染完成再显示，避免白屏闪烁；静默启动场景保持隐藏（用户从托盘/快捷键唤起）
  mainWindow.once('ready-to-show', () => {
    if (!options.startHidden) mainWindow?.show();
  });

  // window.open / target=_blank：外链一律交给系统默认浏览器，不在应用内开新窗（安全 + 体验）
  mainWindow.webContents.setWindowOpenHandler(({ url }) => {
    if (url.startsWith('http://') || url.startsWith('https://')) {
      shell.openExternal(url);
    }
    return { action: 'deny' };
  });

  // 页面内导航限制在信任来源内，跳外部地址时转交系统浏览器
  mainWindow.webContents.on('will-navigate', (event, url) => {
    if (isTrustedPageUrl(url)) return;
    event.preventDefault();
    if (url.startsWith('http://') || url.startsWith('https://')) {
      shell.openExternal(url);
    }
  });

  // 加载失败（dev server 未启动 / 断网等）时展示内置错误页并定时重试
  mainWindow.webContents.on('did-fail-load', (_event, errorCode, errorDesc, _url, isMainFrame) => {
    if (!isMainFrame) return;
    // ERR_ABORTED（-3）是导航被主动取消（如重复 loadURL），不算失败
    if (errorCode === -3) return;
    log.warn(`主窗口加载失败: ${errorCode} ${errorDesc}`);
    mainWindow?.loadURL(buildErrorPageUrl(errorDesc || String(errorCode)));
    if (retryTimer) clearTimeout(retryTimer);
    // 开发模式快速重试等 vite 起服务；打包模式放慢节奏等网络恢复
    retryTimer = setTimeout(() => mainWindow?.loadURL(PAGE_URL), app.isPackaged ? 5000 : 1500);
  });

  // 窗口聚焦即清除任务栏闪烁提醒（配合新消息 flashFrame）
  mainWindow.on('focus', () => mainWindow?.flashFrame(false));

  // F12 切换 DevTools：内部管理系统保留排障入口
  mainWindow.webContents.on('before-input-event', (_event, input) => {
    if (input.type === 'keyDown' && input.key === 'F12') {
      mainWindow?.webContents.toggleDevTools();
    }
  });

  // 关闭行为：双平台都拦截为隐藏（Windows 常驻托盘 / mac 常驻 Dock），退出流程放行
  mainWindow.on('close', (event) => {
    if (isQuitting) return;
    event.preventDefault();
    mainWindow?.hide();
    // Windows 首次隐藏时提示托盘位置，避免用户以为应用被关闭
    if (process.platform === 'win32' && !hasShownHideTip && Notification.isSupported()) {
      hasShownHideTip = true;
      new Notification({
        title: '萧康云医仍在运行',
        body: '应用已最小化到系统托盘，点击托盘图标可重新打开窗口',
      }).show();
    }
  });

  mainWindow.on('closed', () => {
    mainWindow = null;
  });

  mainWindow.loadURL(PAGE_URL);
  return mainWindow;
}

/** 唤起主窗口：还原最小化、显示并聚焦（通知点击 / 托盘 / 第二实例 / 快捷键共用） */
export function showMainWindow(): void {
  if (!mainWindow) return;
  if (mainWindow.isMinimized()) mainWindow.restore();
  mainWindow.show();
  mainWindow.focus();
}

/** 切换主窗口显隐（全局快捷键使用） */
export function toggleMainWindow(): void {
  if (!mainWindow) return;
  if (mainWindow.isVisible() && mainWindow.isFocused()) {
    mainWindow.hide();
  } else {
    showMainWindow();
  }
}
