import "server-only"

import { lt } from "drizzle-orm"

import { db } from "@/shared/db"
import { auditLogs, loginLogs, dataChangeLogs } from "@/shared/db/schema"
import {
  getSystemSetting,
  upsertSystemSetting,
} from "@/modules/settings/data-access-system-settings"
import {
  DEFAULT_RETENTION_DAYS,
  DEFAULT_LOGIN_LOG_RETENTION_DAYS,
  MIN_RETENTION_DAYS,
  MAX_RETENTION_DAYS,
} from "./types"
import type { AuditRetentionConfig, PurgeResult } from "./types"

// 保留天数常量从 types.ts 重新导出，保持现有 import 路径兼容
export {
  DEFAULT_RETENTION_DAYS,
  DEFAULT_LOGIN_LOG_RETENTION_DAYS,
  MIN_RETENTION_DAYS,
  MAX_RETENTION_DAYS,
}

/**
 * 计算保留阈值日期（早于此日期的日志将被清理）
 *
 * 纯函数，便于单测。
 */
export function calculateRetentionThreshold(retentionDays: number, now: Date = new Date()): Date {
  const threshold = new Date(now)
  threshold.setDate(threshold.getDate() - retentionDays)
  return threshold
}

/**
 * 校验保留天数是否在合法范围内
 *
 * 纯函数，便于单测。
 */
export function isValidRetentionDays(days: number): boolean {
  return Number.isInteger(days) && days >= MIN_RETENTION_DAYS && days <= MAX_RETENTION_DAYS
}

/**
 * 获取审计日志保留策略配置
 *
 * 从 system_settings 表读取（category = "audit_retention"）。
 * 若未配置则返回默认值。
 *
 * audit-P2-5: 新增 loginLogRetentionDays 配置项（默认 365 天）。
 */
export async function getAuditRetentionConfig(): Promise<AuditRetentionConfig> {
  try {
    const [retentionDaysRow, loginLogRetentionDaysRow, autoCleanupRow] = await Promise.all([
      getSystemSetting("audit_retention", "retentionDays"),
      getSystemSetting("audit_retention", "loginLogRetentionDays"),
      getSystemSetting("audit_retention", "autoCleanupEnabled"),
    ])

    const retentionDays = retentionDaysRow
      ? Number(retentionDaysRow.value)
      : DEFAULT_RETENTION_DAYS

    const loginLogRetentionDays = loginLogRetentionDaysRow
      ? Number(loginLogRetentionDaysRow.value)
      : DEFAULT_LOGIN_LOG_RETENTION_DAYS

    const autoCleanupEnabled = autoCleanupRow
      ? autoCleanupRow.value === "true"
      : false

    return {
      retentionDays: isValidRetentionDays(retentionDays) ? retentionDays : DEFAULT_RETENTION_DAYS,
      loginLogRetentionDays: isValidRetentionDays(loginLogRetentionDays)
        ? loginLogRetentionDays
        : DEFAULT_LOGIN_LOG_RETENTION_DAYS,
      autoCleanupEnabled,
    }
  } catch (error) {
    console.error("getAuditRetentionConfig failed:", error)
    throw error
  }
}

/**
 * 保存审计日志保留策略配置
 *
 * audit-P2-5: 新增 loginLogRetentionDays 持久化。
 */
export async function saveAuditRetentionConfig(
  config: AuditRetentionConfig,
  updatedBy?: string,
): Promise<void> {
  if (!isValidRetentionDays(config.retentionDays)) {
    throw new Error(
      `Invalid retentionDays: must be integer between ${MIN_RETENTION_DAYS} and ${MAX_RETENTION_DAYS}`,
    )
  }
  if (!isValidRetentionDays(config.loginLogRetentionDays)) {
    throw new Error(
      `Invalid loginLogRetentionDays: must be integer between ${MIN_RETENTION_DAYS} and ${MAX_RETENTION_DAYS}`,
    )
  }

  try {
    await Promise.all([
      upsertSystemSetting({
        category: "audit_retention",
        key: "retentionDays",
        value: String(config.retentionDays),
        valueType: "number",
        updatedBy,
      }),
      upsertSystemSetting({
        category: "audit_retention",
        key: "loginLogRetentionDays",
        value: String(config.loginLogRetentionDays),
        valueType: "number",
        updatedBy,
      }),
      upsertSystemSetting({
        category: "audit_retention",
        key: "autoCleanupEnabled",
        value: String(config.autoCleanupEnabled),
        valueType: "boolean",
        updatedBy,
      }),
    ])
  } catch (error) {
    console.error("saveAuditRetentionConfig failed:", error)
    throw error
  }
}

/**
 * 清理过期的审计日志
 *
 * 删除三张日志表中早于保留阈值的所有记录。
 * 返回各表删除的行数。
 *
 * audit-P2-5: login_logs 可使用更长的保留期（loginLogRetentionDays），
 * 默认 365 天以支持安全取证；audit_logs 与 data_change_logs 使用 retentionDays。
 */
export async function purgeExpiredAuditLogs(
  retentionDays: number,
  loginLogRetentionDays?: number,
): Promise<PurgeResult> {
  if (!isValidRetentionDays(retentionDays)) {
    throw new Error(
      `Invalid retentionDays: must be integer between ${MIN_RETENTION_DAYS} and ${MAX_RETENTION_DAYS}`,
    )
  }

  const effectiveLoginRetention = loginLogRetentionDays ?? retentionDays
  if (!isValidRetentionDays(effectiveLoginRetention)) {
    throw new Error(
      `Invalid loginLogRetentionDays: must be integer between ${MIN_RETENTION_DAYS} and ${MAX_RETENTION_DAYS}`,
    )
  }

  const auditThreshold = calculateRetentionThreshold(retentionDays)
  const loginThreshold = calculateRetentionThreshold(effectiveLoginRetention)

  try {
    const [auditResult, loginResult, dataChangeResult] = await Promise.all([
      db.delete(auditLogs).where(lt(auditLogs.createdAt, auditThreshold)),
      db.delete(loginLogs).where(lt(loginLogs.createdAt, loginThreshold)),
      db.delete(dataChangeLogs).where(lt(dataChangeLogs.createdAt, auditThreshold)),
    ])

    return {
      auditLogsDeleted: auditResult[0].affectedRows,
      loginLogsDeleted: loginResult[0].affectedRows,
      dataChangeLogsDeleted: dataChangeResult[0].affectedRows,
    }
  } catch (error) {
    console.error("purgeExpiredAuditLogs failed:", error)
    throw error
  }
}
