/**
 * Real API client for connecting to the FastAPI backend.
 * Handles auth tokens, request/response encryption, snake_case→camelCase conversion.
 */

import { encrypt, decrypt, isCryptoAvailable } from './crypto';

const BASE_URL = import.meta.env.VITE_API_BASE || 'http://localhost:8000';
const USE_ENCRYPTION = !!import.meta.env.VITE_ENCRYPTION_KEY && isCryptoAvailable();

interface RequestOptions {
  method?: string;
  body?: unknown;
  auth?: boolean;
  encryptBody?: boolean;
}

/** Convert snake_case string to camelCase */
function toCamel(s: string): string {
  return s.replace(/_([a-z])/g, (_, c) => c.toUpperCase());
}

/** Recursively convert all snake_case keys in an object/array to camelCase */
function keysToCamel(obj: unknown): unknown {
  if (Array.isArray(obj)) return obj.map(keysToCamel);
  if (obj !== null && typeof obj === 'object') {
    return Object.fromEntries(
      Object.entries(obj as Record<string, unknown>).map(([k, v]) => [toCamel(k), keysToCamel(v)])
    );
  }
  return obj;
}

function getToken(): string | null {
  return localStorage.getItem('auth_token');
}

export function setToken(token: string): void {
  localStorage.setItem('auth_token', token);
}

export function clearToken(): void {
  localStorage.removeItem('auth_token');
}

/** Try to decrypt response data, return null if it fails */
async function tryDecrypt(data: string): Promise<string | null> {
  try {
    return await decrypt(data);
  } catch {
    return null;
  }
}

export async function apiRequest<T>(path: string, options: RequestOptions = {}): Promise<T> {
  const { method = 'GET', body, auth = true, encryptBody = USE_ENCRYPTION } = options;

  const headers: Record<string, string> = {
    'Content-Type': 'application/json',
  };

  if (auth) {
    const token = getToken();
    if (token) headers['Authorization'] = `Bearer ${token}`;
  }

  let bodyStr: string | undefined;
  if (body !== undefined) {
    const json = JSON.stringify(body);
    if (encryptBody) {
      headers['X-Encrypted'] = 'true';
      bodyStr = JSON.stringify({ data: await encrypt(json) });
    } else {
      bodyStr = json;
    }
  }

  const res = await fetch(`${BASE_URL}/api${path}`, {
    method,
    headers,
    body: bodyStr,
  });

  if (res.status === 204) return undefined as T;

  const text = await res.text();
  if (!text) return undefined as T;

  // Parse the response body
  let parsed: any;
  try {
    parsed = JSON.parse(text);
  } catch {
    throw new Error(`响应解析失败 (${res.status})`);
  }

  // If the response is encrypted, decrypt it
  const hasEncryptedData = parsed && typeof parsed.data === 'string';
  if (hasEncryptedData && encryptBody) {
    const decrypted = await tryDecrypt(parsed.data);
    if (decrypted !== null) {
      parsed = JSON.parse(decrypted);
    }
    // If decryption fails, fall through — parsed still has {data: "..."}
    // which means the response was NOT actually encrypted (just has a "data" field)
  }

  // Handle error responses
  if (!res.ok) {
    const msg = parsed?.detail || `请求失败 (${res.status})`;
    if (res.status === 401) {
      clearToken();
      // Only redirect to login if not already on login page
      const currentPath = window.location.pathname;
      if (currentPath !== '/login') {
        window.location.href = '/login';
      }
    }
    throw new Error(msg);
  }

  return keysToCamel(parsed) as T;
}

// Convenience methods
export const api = {
  get: <T>(path: string, auth = true) => apiRequest<T>(path, { auth }),
  post: <T>(path: string, body?: unknown, auth = true) => apiRequest<T>(path, { method: 'POST', body, auth }),
  put: <T>(path: string, body?: unknown, auth = true) => apiRequest<T>(path, { method: 'PUT', body, auth }),
  delete: <T>(path: string, auth = true) => apiRequest<T>(path, { method: 'DELETE', auth }),
};
