import path from 'node:path'
import type { JsonObject } from '../../types/json.js'

import { APP_CONFIG_KEYS } from '../../config/app-config-keys.js'
import { PROJECT_ROOT } from '../../config/runtime.js'
import { readAppConfigEntry, saveAppConfigEntry } from '../config/app-config-store.js'

const SCHEDULED_JOBS_FILE_PATH = path.join(PROJECT_ROOT, 'data', 'scheduled-jobs.json')
const CLOUDTENTACLES_HEALTH_JOB_ID = 'cloudtentacles-health'
const WORK_ORDER_TIMEOUT_JOB_ID = 'work-order-timeout'
const DEPOSIT_UNFREEZE_JOB_ID = 'deposit-unfreeze'
const DATA_RETENTION_JOB_ID = 'data-retention'

export function getScheduledJobsFilePath() {
  return SCHEDULED_JOBS_FILE_PATH
}

export function getScheduledJobsConfig() {
  return readAppConfigEntry({
    configKey: APP_CONFIG_KEYS.scheduledJobs,
    legacyFilePath: SCHEDULED_JOBS_FILE_PATH,
    fallback: createDefaultScheduledJobsConfig,
    normalize: normalizeScheduledJobsConfig,
  })
}

export function saveScheduledJobsConfig(rawValue: unknown) {
  return saveAppConfigEntry({
    configKey: APP_CONFIG_KEYS.scheduledJobs,
    legacyFilePath: SCHEDULED_JOBS_FILE_PATH,
    value: rawValue,
    normalize: normalizeScheduledJobsConfig,
  })
}

export function getCloudtentaclesHealthJob(config: JsonObject = getScheduledJobsConfig()) {
  return (
    (Array.isArray(config.jobs) ? config.jobs : []).find(
      (item) => String(item.id || '').trim() === CLOUDTENTACLES_HEALTH_JOB_ID,
    ) || createDefaultCloudtentaclesHealthJob()
  )
}

export function getWorkOrderTimeoutJob(config: JsonObject = getScheduledJobsConfig()) {
  return (
    (Array.isArray(config.jobs) ? config.jobs : []).find(
      (item) => String(item.id || '').trim() === WORK_ORDER_TIMEOUT_JOB_ID,
    ) || createDefaultWorkOrderTimeoutJob()
  )
}

export function getDepositUnfreezeJob(config: JsonObject = getScheduledJobsConfig()) {
  return (
    (Array.isArray(config.jobs) ? config.jobs : []).find(
      (item) => String(item.id || '').trim() === DEPOSIT_UNFREEZE_JOB_ID,
    ) || createDefaultDepositUnfreezeJob()
  )
}

export function getScheduledJobById(jobId: unknown, config: JsonObject = getScheduledJobsConfig()) {
  return (
    (Array.isArray(config.jobs) ? config.jobs : []).find(
      (item) => String(item.id || '').trim() === String(jobId || '').trim(),
    ) || null
  )
}

export function normalizeScheduledJobsConfig(rawValue: unknown) {
  const source = isPlainObject(rawValue) ? rawValue : {}
  const rawJobs = Array.isArray(source.jobs) ? source.jobs : []
  const jobs = rawJobs
    .map((item) => normalizeScheduledJob(item))
    .filter((item): item is NonNullable<ReturnType<typeof normalizeScheduledJob>> => Boolean(item))
  const hasCloudtentaclesHealth = jobs.some((item) => item.id === CLOUDTENTACLES_HEALTH_JOB_ID)
  const hasWorkOrderTimeout = jobs.some((item) => item.id === WORK_ORDER_TIMEOUT_JOB_ID)
  const hasDepositUnfreeze = jobs.some((item) => item.id === DEPOSIT_UNFREEZE_JOB_ID)
  const hasDataRetention = jobs.some((item) => item.id === DATA_RETENTION_JOB_ID)

  if (!hasCloudtentaclesHealth) {
    jobs.push(createDefaultCloudtentaclesHealthJob())
  }
  if (!hasWorkOrderTimeout) {
    jobs.push(createDefaultWorkOrderTimeoutJob())
  }
  if (!hasDepositUnfreeze) {
    jobs.push(createDefaultDepositUnfreezeJob())
  }
  if (!hasDataRetention) {
    jobs.push(createDefaultDataRetentionJob())
  }

  return {
    enabled: typeof source.enabled === 'boolean' ? source.enabled : true,
    jobs,
  }
}

function normalizeScheduledJob(rawValue: unknown) {
  if (!isPlainObject(rawValue)) {
    return null
  }

  const type = String(rawValue.type || '').trim()
  if (type === 'cloudtentacles_health') {
    return normalizeCloudtentaclesHealthJob(rawValue)
  }
  if (type === 'work_order_timeout') {
    return normalizeWorkOrderTimeoutJob(rawValue)
  }
  if (type === 'deposit_unfreeze') {
    return normalizeDepositUnfreezeJob(rawValue)
  }
  if (type === 'data_retention') {
    return normalizeDataRetentionJob(rawValue)
  }

  return null
}

function normalizeDepositUnfreezeJob(rawValue: JsonObject) {
  const config = isPlainObject(rawValue.config) ? rawValue.config : {}

  return {
    id: DEPOSIT_UNFREEZE_JOB_ID,
    type: 'deposit_unfreeze',
    enabled: rawValue.enabled !== false,
    intervalSeconds: normalizeRangeInteger(rawValue.intervalSeconds, 60, 30, 3600),
    config: {
      scanLimit: normalizeRangeInteger(config.scanLimit, 100, 1, 2000),
    },
  }
}

function normalizeDataRetentionJob(rawValue: JsonObject) {
  return {
    id: DATA_RETENTION_JOB_ID,
    type: 'data_retention',
    enabled: rawValue.enabled !== false,
    intervalSeconds: normalizeRangeInteger(rawValue.intervalSeconds, 86400, 3600, 604800),
    config: {},
  }
}

function normalizeWorkOrderTimeoutJob(rawValue: JsonObject) {
  const config = isPlainObject(rawValue.config) ? rawValue.config : {}

  return {
    id: WORK_ORDER_TIMEOUT_JOB_ID,
    type: 'work_order_timeout',
    enabled: rawValue.enabled !== false,
    intervalSeconds: normalizeRangeInteger(rawValue.intervalSeconds, 60, 30, 3600),
    config: {
      scanLimit: normalizeRangeInteger(config.scanLimit, 50, 1, 1000),
    },
  }
}

function normalizeCloudtentaclesHealthJob(rawValue: JsonObject) {
  const config = isPlainObject(rawValue.config) ? rawValue.config : {}
  const defaultAssetThreshold = normalizeRangeInteger(config.assetThreshold, 500, 0, 999999)

  return {
    id: CLOUDTENTACLES_HEALTH_JOB_ID,
    type: 'cloudtentacles_health',
    enabled: rawValue.enabled === true,
    intervalSeconds: normalizeRangeInteger(rawValue.intervalSeconds, 300, 60, 86400),
    cooldownSeconds: normalizeRangeInteger(rawValue.cooldownSeconds, 1800, 60, 86400),
    config: {
      assetThreshold: defaultAssetThreshold,
      accounts: normalizeCloudtentaclesHealthAccounts(config, defaultAssetThreshold),
    },
  }
}

function normalizeCloudtentaclesHealthAccounts(config: JsonObject, defaultAssetThreshold: number) {
  const rawAccounts = Array.isArray(config.accounts)
    ? config.accounts
    : Array.isArray(config.sourceKeys)
      ? config.sourceKeys.map((sourceKey) => ({ sourceKey }))
      : []
  const accounts = rawAccounts
    .map((item) => normalizeCloudtentaclesHealthAccount(item, defaultAssetThreshold))
    .filter(Boolean) as ReturnType<typeof normalizeCloudtentaclesHealthAccount>[]

  if (accounts.length === 0) {
    accounts.push(
      normalizeCloudtentaclesHealthAccount(
        {
          sourceKey: config.sourceKey || 'default',
          assetThreshold: defaultAssetThreshold,
        },
        defaultAssetThreshold,
      ),
    )
  }

  const seen = new Set<string>()
  return accounts.filter((item) => {
    const sourceKey = String(item?.sourceKey || '').trim()
    if (!sourceKey || seen.has(sourceKey)) {
      return false
    }
    seen.add(sourceKey)
    return true
  })
}

function normalizeCloudtentaclesHealthAccount(rawValue: unknown, defaultAssetThreshold: number) {
  const source = isPlainObject(rawValue) ? rawValue : {}
  const sourceKey = String(source.sourceKey || source.key || '').trim() || 'default'

  return {
    sourceKey,
    label: String(source.label || '').trim(),
    enabled: source.enabled !== false,
    assetThreshold: normalizeRangeInteger(
      source.assetThreshold ?? source.threshold,
      defaultAssetThreshold,
      0,
      999999,
    ),
  }
}

function createDefaultScheduledJobsConfig() {
  return {
    enabled: true,
    jobs: [
      createDefaultCloudtentaclesHealthJob(),
      createDefaultWorkOrderTimeoutJob(),
      createDefaultDepositUnfreezeJob(),
      createDefaultDataRetentionJob(),
    ],
  }
}

function createDefaultDataRetentionJob() {
  return {
    id: DATA_RETENTION_JOB_ID,
    type: 'data_retention',
    enabled: true,
    intervalSeconds: 86400,
    config: {},
  }
}

function createDefaultDepositUnfreezeJob() {
  return {
    id: DEPOSIT_UNFREEZE_JOB_ID,
    type: 'deposit_unfreeze',
    enabled: true,
    intervalSeconds: 60,
    config: {
      scanLimit: 100,
    },
  }
}

function createDefaultWorkOrderTimeoutJob() {
  return {
    id: WORK_ORDER_TIMEOUT_JOB_ID,
    type: 'work_order_timeout',
    enabled: true,
    intervalSeconds: 60,
    config: {
      scanLimit: 50,
    },
  }
}

function createDefaultCloudtentaclesHealthJob() {
  return {
    id: CLOUDTENTACLES_HEALTH_JOB_ID,
    type: 'cloudtentacles_health',
    enabled: false,
    intervalSeconds: 300,
    cooldownSeconds: 1800,
    config: {
      assetThreshold: 500,
      accounts: [
        {
          sourceKey: 'default',
          label: '默认账号',
          enabled: true,
          assetThreshold: 500,
        },
      ],
    },
  }
}

function normalizeRangeInteger(value: unknown, fallback: number, min: number, max: number) {
  const parsed = Number(value)
  if (!Number.isInteger(parsed)) {
    return fallback
  }

  return Math.min(max, Math.max(min, parsed))
}

function isPlainObject(value: unknown): value is JsonObject {
  return Object.prototype.toString.call(value) === '[object Object]'
}
