import { existsSync, statSync } from 'node:fs';
import { join, normalize } from 'node:path';
import { pathToFileURL } from 'node:url';

import { app, net, protocol } from 'electron';

import { API_UPSTREAM, APP_SCHEME, IM_UPSTREAM } from './config';

/**
 * app:// 自定义协议：桌面端的「本地 nginx」
 * 1. 静态文件服务：把 web-antd 生产 dist 按 URL 路径映射到本地文件，带 SPA fallback
 *    （等价 nginx 的 try_files $uri /index.html），因此无需把前端路由改成 hash 模式
 * 2. 反向代理：/api/admin/* 与 /im-api/* 用 net.fetch 转发到线上上游 ——
 *    渲染层看到的是同源请求，天然规避 CORS，后端与 nginx 都不需要任何改动
 */

/** Electron net.fetch 支持但标准 RequestInit 类型缺失的扩展字段 */
type ElectronFetchInit = RequestInit & {
  /** 转发出去的请求不再进入自定义协议处理器，防止死循环 */
  bypassCustomProtocolHandlers?: boolean;
  /** 携带流式 body 时 fetch 规范要求显式声明半双工 */
  duplex?: 'half';
};

/**
 * 注册特权协议，必须在 app ready 之前调用
 * standard/secure 让 app:// 成为「安全上下文」，navigator.clipboard、getUserMedia、
 * IndexedDB、crypto.subtle 等浏览器能力才可用；stream 支持流式响应（音频等大文件）
 */
export function registerSchemePrivileges(): void {
  protocol.registerSchemesAsPrivileged([
    {
      scheme: APP_SCHEME,
      privileges: {
        standard: true,
        secure: true,
        supportFetchAPI: true,
        stream: true,
        corsEnabled: true,
      },
    },
  ]);
}

/**
 * 解析 web dist 根目录
 * 打包后位于 resources/web（electron-builder extraResources 拷入，不进 asar）；
 * 未打包（本地验证生产包行为）时兜底读仓库内 web-antd/dist
 */
export function getWebRoot(): string {
  return app.isPackaged
    ? join(process.resourcesPath, 'web')
    : join(__dirname, '../../../web-antd/dist');
}

/**
 * 把渲染层的同源请求转发到远端上游（等价 nginx proxy_pass）
 * 保留原请求的方法 / 头 / 流式请求体，Bearer token 请求头原样透传
 */
function proxyRequest(target: string, request: Request): Promise<Response> {
  const init: ElectronFetchInit = {
    method: request.method,
    headers: request.headers,
    body: request.body,
    bypassCustomProtocolHandlers: true,
  };
  if (request.body) init.duplex = 'half';
  return net.fetch(target, init);
}

/**
 * 静态文件解析：带路径穿越防护 + SPA fallback
 * 命中不了真实文件的路径（前端路由地址、不存在的资源）统一回 index.html 交给前端路由
 */
function resolveStaticFile(webRoot: string, pathname: string): string {
  const indexHtml = join(webRoot, 'index.html');
  try {
    const filePath = normalize(join(webRoot, decodeURIComponent(pathname)));
    // 防路径穿越：解析结果必须仍在 webRoot 目录内
    if (!filePath.startsWith(normalize(webRoot))) return indexHtml;
    if (existsSync(filePath) && statSync(filePath).isFile()) return filePath;
  } catch {
    // decode 失败等异常场景直接回 index.html，不让协议处理器抛错
  }
  return indexHtml;
}

/**
 * 注册 app:// 协议处理器，在 app ready 之后调用
 */
export function registerAppProtocol(): void {
  const webRoot = getWebRoot();
  protocol.handle(APP_SCHEME, (request) => {
    const url = new URL(request.url);
    // 反代 /api/admin/*：生产构建的 VITE_GLOB_API_URL=/api/admin，路径直接透传
    if (url.pathname.startsWith('/api/')) {
      return proxyRequest(`${API_UPSTREAM}${url.pathname}${url.search}`, request);
    }
    // 反代 /im-api/*：对齐开发代理规则，剥掉 /im-api 前缀后拼到上游 /api/*
    if (url.pathname.startsWith('/im-api/')) {
      const stripped = url.pathname.slice('/im-api'.length);
      return proxyRequest(`${IM_UPSTREAM}/api${stripped}${url.search}`, request);
    }
    // 其余全部按静态资源处理（net.fetch file:// 会按扩展名自动带上正确的 Content-Type）
    const fileUrl = pathToFileURL(resolveStaticFile(webRoot, url.pathname)).toString();
    return net.fetch(fileUrl, { bypassCustomProtocolHandlers: true } as ElectronFetchInit);
  });
}
