import { invoke } from '@tauri-apps/api/core';
import { ApiResponse } from './types';
import { isTauri } from '../utils/env';

// 优先读取 Vite 环境变量，未配置时兜底为本地 Docker API 地址（与 docker-compose 端口一致）
export let PYTHON_API_BASE_URL =
  import.meta.env.VITE_API_BASE_URL || 'http://localhost:8000';

/**
 * 动态设置 API Base URL（用于环境切换）
 */
export function setApiBaseUrl(url: string): void {
  PYTHON_API_BASE_URL = url;
}

// 简单的内存缓存
type AuthData = { accessToken?: string; refreshToken?: string } | null;
let cachedAuth: AuthData = null;

// Token 刷新回调（供 authStore 注册，用于持久化新 Token）
type TokenRefreshedCallback = (tokens: { accessToken: string; refreshToken: string }) => void;
let tokenRefreshedCallback: TokenRefreshedCallback | null = null;

// 认证失效回调（设备被踢、Refresh Token 失效等）
type AuthFailedCallback = () => void;
let authFailedCallback: AuthFailedCallback | null = null;

/**
 * 注册 Token 刷新回调
 * 当 doRefreshToken 成功换取新 Token 时，会触发此回调
 */
export function setOnTokenRefreshed(cb: TokenRefreshedCallback): void {
  tokenRefreshedCallback = cb;
}

/**
 * 注册认证失效回调
 * 当 Refresh Token 失效（401 设备已失效）时触发，供上层执行登出
 */
export function setOnAuthFailed(cb: AuthFailedCallback): void {
  authFailedCallback = cb;
}

/**
 * 从存储加载 token（Tauri 文件存储优先，浏览器回退 localStorage）
 */
async function loadAuthToken(): Promise<string | null> {
  // 如果内存中有缓存，直接返回
  if (cachedAuth?.accessToken) {
    return cachedAuth.accessToken;
  }

  try {
    if (isTauri()) {
      const result = await invoke<{ code: number; data?: { accessToken?: string; refreshToken?: string } }>('load_auth_state');
      if (result.code === 200 && result.data?.accessToken) {
        cachedAuth = result.data;
        return result.data.accessToken;
      }
    } else {
      // 浏览器环境：从 localStorage 读取
      const legacy = localStorage.getItem('ai-video-auth');
      if (legacy) {
        try {
          const parsed = JSON.parse(legacy);
          const state = parsed?.state;
          // 兼容旧格式 { token } → 新格式 { accessToken, refreshToken }
          const accessToken = state?.accessToken || state?.token;
          const refreshToken = state?.refreshToken;
          if (accessToken) {
            cachedAuth = { accessToken, refreshToken };
            return accessToken;
          }
        } catch {
          // 忽略解析错误
        }
      }
    }
  } catch (e) {
    console.error('[client] 加载认证 token 失败:', e);
  }

  return null;
}

/**
 * 清除 token 缓存（用于登出）
 */
export function clearAuthCache(): void {
  cachedAuth = null;
}

// ═══════════════════════════════════════════════════════════════
// Token 自动刷新
// ═══════════════════════════════════════════════════════════════

let isRefreshing = false;
let refreshSubscribers: Array<(accessToken: string) => void> = [];

function onRefreshed(accessToken: string) {
  refreshSubscribers.forEach(cb => cb(accessToken));
  refreshSubscribers = [];
}

function addRefreshSubscriber(cb: (accessToken: string) => void) {
  refreshSubscribers.push(cb);
}

/**
 * 检查 JWT 是否即将过期
 * @param token JWT Token
 * @param bufferSeconds 提前量（默认 300 秒 = 5 分钟）
 */
function isTokenExpired(token: string, bufferSeconds = 300): boolean {
  try {
    const payload = JSON.parse(atob(token.split('.')[1]));
    if (!payload.exp) {return true;}
    return payload.exp * 1000 < Date.now() + bufferSeconds * 1000;
  } catch {
    return true;
  }
}

/**
 * 用 Refresh Token 换取新的 Token 对
 * 直接用 fetch，不走 httpRequest 避免递归
 */
async function doRefreshToken(): Promise<string | null> {
  if (!cachedAuth?.refreshToken) {return null;}
  try {
    const response = await fetch(`${PYTHON_API_BASE_URL}/auth/refresh`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ refresh_token: cachedAuth.refreshToken }),
    });
    if (!response.ok) {
      // 401 说明设备已失效或被踢，清除缓存避免死循环，并通知上层登出
      if (response.status === 401) {
        cachedAuth = null;
        if (authFailedCallback) { authFailedCallback(); }
      }
      return null;
    }
    const raw = await response.json();
    const data = parseResponse<{ accessToken: string; refreshToken: string }>(raw);
    cachedAuth = { accessToken: data.accessToken, refreshToken: data.refreshToken };
    // 通知上层（authStore）持久化新 Token
    if (tokenRefreshedCallback) {
      tokenRefreshedCallback({ accessToken: data.accessToken, refreshToken: data.refreshToken });
    }
    return data.accessToken;
  } catch (e) {
    console.error('[client] Token 刷新失败:', e);
    return null;
  }
}

/**
 * 转换 camelCase 对象key为 snake_case（发送给后端）
 */
function camelToSnake(obj: unknown): unknown {
  if (obj === null || typeof obj !== 'object') {
    return obj;
  }
  if (Array.isArray(obj)) {
    return obj.map(item => camelToSnake(item));
  }
  const result: Record<string, unknown> = {};
  for (const [key, value] of Object.entries(obj)) {
    const snakeKey = key
      .replace(/([A-Z]+)([A-Z][a-z])/g, '$1_$2')
      .replace(/([a-z\d])([A-Z])/g, '$1_$2')
      .toLowerCase();
    result[snakeKey] = camelToSnake(value);
  }
  return result;
}

/**
 * 转换 snake_case 对象key为 camelCase（从后端接收）
 */
function snakeToCamel(obj: unknown): unknown {
  if (obj === null || typeof obj !== 'object') {
    return obj;
  }
  if (Array.isArray(obj)) {
    return obj.map(item => snakeToCamel(item));
  }
  const result: Record<string, unknown> = {};
  for (const [key, value] of Object.entries(obj)) {
    const camelKey = key.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase());
    result[camelKey] = snakeToCamel(value);
  }
  return result;
}

/**
 * HTTP 直连 Python 后端
 * 自动转换：前端 camelCase ↔ 后端 snake_case
 */
async function httpRequest<T>(
  method: string,
  path: string,
  body?: unknown,
  options?: { headers?: Record<string, string>; isFormData?: boolean; cache?: RequestCache; _retry?: boolean }
): Promise<T> {
  const url = `${PYTHON_API_BASE_URL}${path}`;

  // 加载 token
  let token = await loadAuthToken();

  // ========== 主动刷新：token 快过期时先刷新 ==========
  if (token && isTokenExpired(token) && !options?._retry) {
    if (!isRefreshing) {
      isRefreshing = true;
      try {
        const newToken = await doRefreshToken();
        if (newToken) {
          token = newToken;
          onRefreshed(newToken);
        }
      } finally {
        isRefreshing = false;
      }
    } else {
      // 等待其他请求刷新完成
      token = await new Promise((resolve) => {
        addRefreshSubscriber((newToken) => resolve(newToken));
      });
    }
  }

  const fetchOptions: RequestInit = {
    method,
    headers: {
      ...(options?.isFormData ? {} : { 'Content-Type': 'application/json' }),
      ...(token ? { Authorization: `Bearer ${token}` } : {}),
      ...options?.headers,
    },
    cache: options?.cache,
  };

  if (body !== undefined && !options?.isFormData) {
    // 自动转换 camelCase → snake_case 发给后端
    fetchOptions.body = JSON.stringify(camelToSnake(body));
  } else if (body !== undefined) {
    fetchOptions.body = body as BodyInit;
  }

  // 发送请求，失败时输出详细诊断日志（跨平台网络问题排查用）
  let response: Response;
  try {
    response = await fetch(url, fetchOptions);
  } catch (networkErr) {
    const err = networkErr instanceof Error ? networkErr : new Error(String(networkErr));
    console.error('[client] 网络请求失败:', {
      method,
      url,
      platform: navigator.userAgent,
      error: err.message,
      stack: err.stack,
    });
    // 在错误消息中附加 URL，方便生产环境排查
    throw new Error(`${err.message} (URL: ${url})`);
  }

  // ========== 被动刷新：401 时尝试刷新并重试 ==========
  if (response.status === 401 && !options?._retry && cachedAuth?.refreshToken) {
    if (!isRefreshing) {
      isRefreshing = true;
      try {
        const newToken = await doRefreshToken();
        if (newToken) {
          onRefreshed(newToken);
          return httpRequest<T>(method, path, body, { ...options, _retry: true });
        }
      } finally {
        isRefreshing = false;
        // 无论成功失败，都唤醒等待中的 subscriber，避免请求永远挂死
        onRefreshed(cachedAuth?.accessToken || '');
      }
    } else {
      // 等待刷新完成后重试
      await new Promise<void>((resolve) => {
        addRefreshSubscriber(() => resolve());
      });
      return httpRequest<T>(method, path, body, { ...options, _retry: true });
    }
  }

  if (!response.ok) {
    const errorText = await response.text().catch(() => response.statusText);
    throw new Error(extractErrorMessage(errorText, `HTTP ${response.status}: ${response.statusText}`));
  }

  const result = await response.json();
  return parseResponse<T>(result);
}

/**
 * 解析后端响应并转换 camelCase
 */
function parseResponse<T>(raw: string | unknown): T {
  const result = typeof raw === 'string' ? JSON.parse(raw) : raw;
  const convertedResult = snakeToCamel(result) as typeof result;
  if (convertedResult && typeof convertedResult === 'object' && 'data' in convertedResult) {
    return (convertedResult as ApiResponse<T>).data as T;
  }
  return convertedResult as T;
}

/**
 * 从错误响应中提取可读消息
 *
 * 后端错误统一返回 ApiErrorResponse 格式：{ code, message, detail }
 */
function extractErrorMessage(responseText: string, fallbackStatus: string): string {
  try {
    const errorData = JSON.parse(responseText);
    if (typeof errorData.message === 'string') {
      return errorData.message;
    }
  } catch {
    // 解析失败，返回原始文本或默认状态信息
  }
  return responseText || fallbackStatus;
}

/**
 * HTTP API 客户端
 */
export const client = {
  async get<T>(path: string): Promise<T> {
    const cacheBuster = `_t=${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
    const pathWithCacheBuster = path.includes('?') ? `${path}&${cacheBuster}` : `${path}?${cacheBuster}`;
    return httpRequest<T>('GET', pathWithCacheBuster, undefined, {
      headers: {
        'Cache-Control': 'no-store, no-cache, must-revalidate',
        Pragma: 'no-cache',
      },
    });
  },

  async post<T>(path: string, data?: unknown): Promise<T> {
    return httpRequest<T>('POST', path, data);
  },

  async put<T>(path: string, data?: unknown): Promise<T> {
    return httpRequest<T>('PUT', path, data);
  },

  async patch<T>(path: string, data?: unknown): Promise<T> {
    return httpRequest<T>('PATCH', path, data);
  },

  async delete<T>(path: string): Promise<T> {
    return httpRequest<T>('DELETE', path);
  },

  async postForm<T>(path: string, formData: FormData): Promise<T> {
    return httpRequest<T>('POST', path, formData, { isFormData: true });
  },
};
