/**
 * 应用锁与安全（plan.md「0.4 应用锁与安全」）。
 * 参考 BeeCount 的 AppLockService（生物识别/PIN）。
 *
 * 实际的生物识别调用 expo-local-authentication（原生），
 * 本模块定义认证逻辑抽象，使可单测。
 */

import { logger } from '../../utils/logger';

export interface AuthProvider {
  /** 是否有生物识别硬件。 */
  hasHardware(): Promise<boolean>;
  /** 是否已录入生物识别。 */
  isEnrolled(): Promise<boolean>;
  /** 触发认证，返回是否成功。 */
  authenticate(promptMessage: string): Promise<boolean>;
}

/** expo-local-authentication 动态 require 的最小接口。 */
interface LocalAuthModule {
  hasHardwareAsync(): Promise<boolean>;
  isEnrolledAsync(): Promise<boolean>;
  authenticateAsync(opts: { promptMessage: string; cancelLabel: string }): Promise<{ success: boolean }>;
}

/**
 * 动态获取 expo-local-authentication 模块（避免测试环境 import 失败）。
 * 缓存结果，避免每次调用都重复 require。
 */
let _localAuth: LocalAuthModule | null = null;
let _localAuthTried = false;
function getLocalAuth(): LocalAuthModule | null {
  if (!_localAuthTried) {
    _localAuthTried = true;
    try {
      _localAuth = require('expo-local-authentication');
    } catch {
      _localAuth = null;
    }
  }
  return _localAuth;
}

/**
 * 生产环境 AuthProvider：用 expo-local-authentication 调用真实生物识别。
 */
export class RealAuthProvider implements AuthProvider {
  async hasHardware(): Promise<boolean> {
    const la = getLocalAuth();
    if (!la) return false;
    return await la.hasHardwareAsync();
  }

  async isEnrolled(): Promise<boolean> {
    const la = getLocalAuth();
    if (!la) return false;
    return await la.isEnrolledAsync();
  }

  async authenticate(promptMessage: string): Promise<boolean> {
    const la = getLocalAuth();
    if (!la) return false;
    const result = await la.authenticateAsync({
      promptMessage,
      cancelLabel: 'Cancel',
    });
    return result.success;
  }
}

/** 认证结果。 */
export interface AuthResult {
  success: boolean;
  reason: 'authenticated' | 'no-hardware' | 'not-enrolled' | 'cancelled' | 'failed';
}

/** 执行认证流程（带硬件/录入检查）。 */
export async function authenticate(auth: AuthProvider): Promise<AuthResult> {
  const hasHardware = await auth.hasHardware();
  if (!hasHardware) return { success: true, reason: 'no-hardware' };  // 无硬件直接放行

  const isEnrolled = await auth.isEnrolled();
  if (!isEnrolled) return { success: true, reason: 'not-enrolled' };

  const ok = await auth.authenticate('解锁记账应用');
  return ok
    ? { success: true, reason: 'authenticated' }
    : { success: false, reason: 'cancelled' };
}

/** 生成随机 salt（16 字节 hex）。始终使用密码学安全的随机源。 */
function generateSalt(): string {
  const bytes = new Uint8Array(16);
  if (typeof crypto !== 'undefined' && crypto.getRandomValues) {
    crypto.getRandomValues(bytes);
  } else {
    // 降级：使用多源混合提高随机性（仍非密码学安全，但优于纯 Math.random）
    const now = Date.now();
    for (let i = 0; i < 16; i++) {
      bytes[i] = (Math.floor(Math.random() * 256) ^ ((now >> (i % 4) * 8) & 0xff) ^ (i * 37)) & 0xff;
    }
    logger.warn('security', 'crypto.getRandomValues 不可用，salt 生成使用降级方案');
  }
  return Array.from(bytes).map(b => b.toString(16).padStart(2, '0')).join('');
}

/** PIN 哈希结果（含 salt）。 */
export interface PinHashResult {
  hash: string;
  salt: string;
}

/**
 * PIN 哈希（带盐 SHA-256）。
 * @param existingSalt 若提供则复用已有 salt（用于迁移旧数据）。
 */
export async function hashPin(pin: string, existingSalt?: string): Promise<PinHashResult> {
  const salt = existingSalt ?? generateSalt();
  const salted = salt + pin;
  if (typeof crypto !== 'undefined' && crypto.subtle) {
    const data = new TextEncoder().encode(salted);
    const buf = await crypto.subtle.digest('SHA-256', data);
    const hash = [...new Uint8Array(buf)].map(b => b.toString(16).padStart(2, '0')).join('');
    return { hash, salt };
  }
  // 降级：迭代 FNV-1a（1000 轮）提高暴力破解成本（生产环境应始终走 SHA-256）
  let h = 2166136261;
  for (let round = 0; round < 1000; round++) {
    for (const c of salted) h = Math.imul(h ^ c.charCodeAt(0), 16777619);
    h = Math.imul(h ^ (round & 0xff), 16777619);
  }
  logger.warn('security', 'crypto.subtle 不可用，PIN 哈希使用降级方案');
  return { hash: (h >>> 0).toString(16), salt };
}

/** 验证 PIN（支持旧版无 salt 哈希的迁移）。 */
export async function verifyPin(pin: string, storedHash: string, salt?: string): Promise<boolean> {
  if (salt) {
    const { hash } = await hashPin(pin, salt);
    return hash === storedHash;
  }
  // 旧版无 salt 哈希兼容：先尝试无 salt 验证（传入空字符串盐，保持 legacyHash 计算时不生成新盐）
  const { hash: legacyHash } = await hashPin(pin, '');
  return legacyHash === storedHash;
}

/** 是否应在恢复时锁屏（基于超时配置）。 */
export function shouldLockOnResume(
  lastBackgroundTime: number | null,
  timeoutSeconds: number,
  now: number = Date.now(),
): boolean {
  if (lastBackgroundTime === null) return false;
  return (now - lastBackgroundTime) / 1000 >= timeoutSeconds;
}
