// ============================================================
// NetRequest — Electron net.request 的 axios 风格封装
//
// 用法：
//   import { request, get, post } from './net-request';
//   const { data } = await get<UpdateCheckResult>('/api/v1/update/check', {
//     baseURL: 'https://www.heixiu.com',
//     params: { platform: 'win32', version: '1.0.0' },
//   });
//
// 设计：
//   - 底层使用 Electron net.request（不引入额外依赖）
//   - Promise 封装，支持 async/await
//   - JSON 自动解析、查询参数自动拼接
//   - 流式下载场景仍直接用 net.request（本模块不封装 stream）
// ============================================================

import { net } from 'electron';
import { logger } from './logger';

// ---------- 类型 ----------

export interface RequestConfig {
   /** 请求方法（默认 GET） */
   method?: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';
   /** 基础 URL（可选，方便切换环境） */
   baseURL?: string;
   /** 查询参数（自动拼接 ?key=value&...） */
   params?: Record<string, string | number | undefined>;
   /** 请求头 */
   headers?: Record<string, string>;
   /** 请求体（对象自动 JSON.stringify，字符串原样发送） */
   data?: unknown;
   /** 响应类型（默认 json，设置 text 跳过解析） */
   responseType?: 'json' | 'text';
   /** 超时时间（毫秒，默认 30s） */
   timeout?: number;
}

export interface RequestResponse<T = unknown> {
   /** HTTP 状态码 */
   status: number;
   /** 状态文本（如 "OK"） */
   statusText: string;
   /** 响应头 */
   headers: Record<string, string | string[]>;
   /** 响应体 */
   data: T;
}

/** 网络请求错误 */
export class RequestError extends Error {
   status?: number;
   constructor(message: string, status?: number) {
      super(message);
      this.name = 'NetRequestError';
      this.status = status;
   }
}

// ---------- 工具 ----------

const DEFAULT_TIMEOUT = 30_000;

/** 拼接 URL（baseURL + path + query） */
function buildUrl(url: string, config: RequestConfig): string {
   // 如果没有 baseURL 且 url 不是绝对地址，直接报错（便于定位问题）
   if (!config.baseURL && !/^https?:\/\//i.test(url)) {
      throw new RequestError(
         `URL 缺少 baseURL，无法构造绝对地址。url="${url}", config=${JSON.stringify({ ...config, params: config.params ? '[object]' : undefined })}`,
      );
   }

   const base = (config.baseURL || '').trim().replace(/\/+$/, '');
   const path = url.startsWith('/') ? url : `/${url}`;
   let fullUrl = base ? `${base}${path}` : url;

   if (config.params) {
      const search = new URLSearchParams();
      for (const [key, value] of Object.entries(config.params)) {
         if (value !== undefined && value !== null) {
            search.append(key, String(value));
         }
      }
      const qs = search.toString();
      if (qs) {
         fullUrl += (fullUrl.includes('?') ? '&' : '?') + qs;
      }
   }

   // 调试：在 net.request 调用之前验证 URL 合法性
   try {
      void new URL(fullUrl);
   } catch {
      throw new RequestError(
         `构造的 URL 无法通过 Node.js 校验: "${fullUrl}"`,
      );
   }

   console.log('[net-request] buildUrl:', fullUrl);
   return fullUrl;
}

/** 序列化请求体 */
function serializeBody(data: unknown): { body: string; contentType: string } {
   if (typeof data === 'string') {
      return { body: data, contentType: 'text/plain' };
   }
   return { body: JSON.stringify(data), contentType: 'application/json' };
}

// ---------- 核心 ----------

/**
 * 发起请求（axios 风格 API）
 *
 * 流式下载场景请直接使用 Electron net.request（本模块不封装）
 */
export async function request<T = unknown>(
   url: string,
   config: RequestConfig = {},
): Promise<RequestResponse<T>> {
   const fullUrl = buildUrl(url, config);
   const method = (config.method || 'GET').toUpperCase();
   const timeout = config.timeout ?? DEFAULT_TIMEOUT;

   return new Promise((resolve, reject) => {
      // 调试：验证 URL 是否合法
      try {
         void new URL(fullUrl);
      } catch (urlErr) {
         logger.warn('request', `非法 URL: ${fullUrl}`, urlErr instanceof Error ? urlErr : undefined);
         reject(new RequestError(`非法 URL: ${(urlErr as Error).message}`));
         return;
      }

      const req = net.request({
         method,
         url: fullUrl,
      });

      // 超时处理
      let timedOut = false;
      const timer = setTimeout(() => {
         timedOut = true;
         req.abort();
         reject(new RequestError(`请求超时 (${timeout}ms)`));
      }, timeout);

      // 请求头
      if (config.headers) {
         for (const [key, value] of Object.entries(config.headers)) {
            req.setHeader(key, value);
         }
      }

      // 请求体
      if (config.data !== undefined) {
         const { body, contentType } = serializeBody(config.data);
         req.setHeader('Content-Type', contentType);
         req.write(body);
      }

      req.on('response', (response) => {
         clearTimeout(timer);

         const status = response.statusCode;
         const statusText = response.statusMessage ?? '';

         // 读取响应头
         const headers: Record<string, string | string[]> = {};
         // Electron IncomingMessage.headers 是 Record<string, string[]>
         const rawHeaders = response.headers;
         if (rawHeaders) {
            for (const [key, value] of Object.entries(rawHeaders)) {
               headers[key] = value.length === 1 ? value[0] : value;
            }
         }

         // 读取响应体
         let body = '';
         response.on('data', (chunk: Buffer) => (body += chunk.toString()));
         response.on('end', () => {
            try {
               const asJson = config.responseType !== 'text';
               const data = asJson ? JSON.parse(body) : body;
               resolve({ status, statusText, headers, data: data as T });
            } catch {
               // JSON 解析失败 → 返回原始文本
               resolve({ status, statusText, headers, data: body as T });
            }
         });

         response.on('error', (err) => {
            clearTimeout(timer);
            reject(new RequestError(`响应读取失败: ${err.message}`));
         });
      });

      req.on('error', (err) => {
         clearTimeout(timer);
         if (timedOut) return;
         reject(new RequestError(`网络请求失败: ${err.message}`));
      });

      req.end();
   });
}

// ---------- 默认导出：类 axios 命名空间 ----------

export const netRequest = {
   request,

   get<T>(url: string, config?: Omit<RequestConfig, 'method' | 'data'>) {
      return request<T>(url, { ...config, method: 'GET' });
   },

   post<T>(url: string, data?: unknown, config?: Omit<RequestConfig, 'method' | 'data'>) {
      return request<T>(url, { ...config, method: 'POST', data });
   },

   put<T>(url: string, data?: unknown, config?: Omit<RequestConfig, 'method' | 'data'>) {
      return request<T>(url, { ...config, method: 'PUT', data });
   },

   del<T>(url: string, config?: Omit<RequestConfig, 'method' | 'data'>) {
      return request<T>(url, { ...config, method: 'DELETE' });
   },
};
