export type FeaturePolicyValueType = 'boolean' | 'number' | 'string' | 'multi_boolean'

export interface FeaturePolicyFieldDef {
  key: string
  labelKey: string
  type: FeaturePolicyValueType
  placeholderKey?: string
  min?: number
  max?: number
  unitKey?: string
  dependsOnKey?: string
}

export interface FeaturePolicySectionDef {
  key: string
  titleKey: string
  fields: FeaturePolicyFieldDef[]
}

export interface FeaturePolicyCustomItem {
  key: string
  label: string
  type: 'boolean' | 'number' | 'string'
  value: boolean | number | string
}

export interface FeaturePolicyModel {
  features: Record<string, boolean>
  quotas: Record<string, number | undefined>
  extra: {
    customItems: FeaturePolicyCustomItem[]
    unknown?: Record<string, unknown>
  }
}

export interface FeaturePolicyPreset {
  key: string
  labelKey: string
  value: Partial<FeaturePolicyModel>
}

export const FeaturePolicySections: FeaturePolicySectionDef[] = [
  {
    key: 'features',
    titleKey: 'tenantPackage.featurePolicy.sections.features',
    fields: [
      {
        key: 'features.reportsExport',
        labelKey: 'tenantPackage.featurePolicy.features.reportsExport',
        type: 'boolean'
      },
      {
        key: 'features.printing',
        labelKey: 'tenantPackage.featurePolicy.features.printing',
        type: 'boolean'
      },
      {
        key: 'features.apiAccess',
        labelKey: 'tenantPackage.featurePolicy.features.apiAccess',
        type: 'boolean'
      },
      {
        key: 'features.marketingCoupon',
        labelKey: 'tenantPackage.featurePolicy.features.coupon',
        type: 'boolean'
      },
      {
        key: 'features.marketingFullReduction',
        labelKey: 'tenantPackage.featurePolicy.features.fullReduction',
        type: 'boolean'
      },
      {
        key: 'features.marketingMember',
        labelKey: 'tenantPackage.featurePolicy.features.member',
        type: 'boolean'
      },
      {
        key: 'features.marketingPoints',
        labelKey: 'tenantPackage.featurePolicy.features.points',
        type: 'boolean'
      }
    ]
  },
  {
    key: 'quotas',
    titleKey: 'tenantPackage.featurePolicy.sections.quotas',
    fields: [
      {
        key: 'quotas.maxProducts',
        labelKey: 'tenantPackage.featurePolicy.quotas.maxProducts',
        type: 'number',
        min: 0,
        placeholderKey: 'tenantPackage.featurePolicy.quotas.unlimitedPlaceholder'
      },
      {
        key: 'quotas.maxMenus',
        labelKey: 'tenantPackage.featurePolicy.quotas.maxMenus',
        type: 'number',
        min: 0,
        placeholderKey: 'tenantPackage.featurePolicy.quotas.unlimitedPlaceholder'
      },
      {
        key: 'quotas.maxApiCallsPerDay',
        labelKey: 'tenantPackage.featurePolicy.quotas.maxApiCallsPerDay',
        type: 'number',
        min: 0,
        placeholderKey: 'tenantPackage.featurePolicy.quotas.unlimitedPlaceholder',
        dependsOnKey: 'features.apiAccess'
      }
    ]
  }
]

export const FeaturePolicyPresets: FeaturePolicyPreset[] = [
  {
    key: 'blank',
    labelKey: 'tenantPackage.featurePolicy.presets.blank',
    value: {
      features: {},
      quotas: {}
    }
  },
  {
    key: 'standard',
    labelKey: 'tenantPackage.featurePolicy.presets.standard',
    value: {
      features: {
        reportsExport: true,
        printing: true,
        apiAccess: false,
        marketingCoupon: false,
        marketingFullReduction: true,
        marketingMember: false,
        marketingPoints: false
      },
      quotas: {
        maxProducts: 500,
        maxMenus: 30
      }
    }
  },
  {
    key: 'pro',
    labelKey: 'tenantPackage.featurePolicy.presets.pro',
    value: {
      features: {
        reportsExport: true,
        printing: true,
        apiAccess: true,
        marketingCoupon: true,
        marketingFullReduction: true,
        marketingMember: true,
        marketingPoints: true
      },
      quotas: {
        maxProducts: 3000,
        maxMenus: 200,
        maxApiCallsPerDay: 20000
      }
    }
  }
]

export function createDefaultFeaturePolicy(): FeaturePolicyModel {
  return {
    features: {},
    quotas: {},
    extra: {
      customItems: [],
      unknown: {}
    }
  }
}

export function parseFeaturePolicyJson(value: string): {
  model: FeaturePolicyModel
  isValidJson: boolean
} {
  // 1. 空值视为默认
  if (!value?.trim()) return { model: createDefaultFeaturePolicy(), isValidJson: true }

  // 2. JSON 解析失败则回退默认，但标记无效
  let raw: unknown
  try {
    raw = JSON.parse(value)
  } catch {
    return { model: createDefaultFeaturePolicy(), isValidJson: false }
  }

  if (!raw || typeof raw !== 'object') {
    return { model: createDefaultFeaturePolicy(), isValidJson: false }
  }

  // 3. 兼容旧结构：limits/api/reports/printing/marketing
  const rawObj = raw as Record<string, unknown>
  const model = createDefaultFeaturePolicy()

  // 3.1 映射 features
  const features: Record<string, boolean> = {}
  const rawReports = toRecord(rawObj.reports)
  const rawPrinting = toRecord(rawObj.printing)
  const rawApi = toRecord(rawObj.api)
  const rawMarketing = toRecord(rawObj.marketing)

  if (typeof rawReports.exportEnabled === 'boolean')
    features.reportsExport = rawReports.exportEnabled
  if (typeof rawPrinting.enabled === 'boolean') features.printing = rawPrinting.enabled
  if (typeof rawApi.enabled === 'boolean') features.apiAccess = rawApi.enabled

  if (typeof rawMarketing.coupon === 'boolean') features.marketingCoupon = rawMarketing.coupon
  if (typeof rawMarketing.fullReduction === 'boolean')
    features.marketingFullReduction = rawMarketing.fullReduction
  if (typeof rawMarketing.member === 'boolean') features.marketingMember = rawMarketing.member
  if (typeof rawMarketing.points === 'boolean') features.marketingPoints = rawMarketing.points

  model.features = features

  // 3.2 映射 quotas
  const quotas: Record<string, number | undefined> = {}
  const rawLimits = toRecord(rawObj.limits)
  quotas.maxProducts = normalizeNumber(rawLimits.maxProducts)
  quotas.maxMenus = normalizeNumber(rawLimits.maxMenus)
  quotas.maxApiCallsPerDay = normalizeNumber(rawApi.maxCallsPerDay)
  model.quotas = quotas

  // 3.3 合并 extra：优先保留 raw.extra，再叠加未知顶层字段
  const rawExtra = toRecord(rawObj.extra)
  const unknown: Record<string, unknown> = {}
  const knownKeys = new Set(['limits', 'api', 'reports', 'printing', 'marketing', 'extra'])
  Object.keys(rawObj).forEach((k) => {
    if (!knownKeys.has(k)) unknown[k] = rawObj[k]
  })

  model.extra = {
    customItems: normalizeCustomItems(rawExtra.customItems),
    unknown: { ...unknown, ...rawExtra }
  }

  return { model, isValidJson: true }
}

export function serializeFeaturePolicy(model: FeaturePolicyModel): string {
  // 1. 兼容旧结构输出：limits/api/reports/printing/marketing + extra
  const features = model.features || {}
  const quotas = model.quotas || {}

  const payload: Record<string, unknown> = {
    limits: {
      maxProducts: normalizeNumber(quotas.maxProducts),
      maxMenus: normalizeNumber(quotas.maxMenus)
    },
    api: {
      enabled: !!features.apiAccess,
      maxCallsPerDay: features.apiAccess ? normalizeNumber(quotas.maxApiCallsPerDay) : undefined
    },
    reports: {
      exportEnabled: !!features.reportsExport
    },
    printing: {
      enabled: !!features.printing
    },
    marketing: {
      coupon: !!features.marketingCoupon,
      fullReduction: !!features.marketingFullReduction,
      member: !!features.marketingMember,
      points: !!features.marketingPoints
    }
  }

  // 2. 合并 extra（保留未知字段 + 自定义扩展项）
  const extra: Record<string, unknown> = { ...(model.extra?.unknown || {}) }
  extra.customItems = normalizeCustomItems(model.extra?.customItems)

  if (Object.keys(extra).length) {
    payload.extra = extra
  }

  return JSON.stringify(payload, null, 2)
}

export function validateFeaturePolicy(model: FeaturePolicyModel): string[] {
  const errors: string[] = []

  // 1. 数值校验：非负
  const quotas = model.quotas || {}
  const numberKeys: Array<[string, unknown]> = [
    ['quotas.maxProducts', quotas.maxProducts],
    ['quotas.maxMenus', quotas.maxMenus],
    ['quotas.maxApiCallsPerDay', quotas.maxApiCallsPerDay]
  ]
  numberKeys.forEach(([key, value]) => {
    if (value === null || value === undefined || value === '') return
    const num = Number(value)
    if (!Number.isFinite(num) || num < 0) errors.push(key)
  })

  // 2. 依赖校验：API 未开启则不允许填写 maxApiCallsPerDay
  if (
    !model.features?.apiAccess &&
    quotas.maxApiCallsPerDay !== undefined &&
    quotas.maxApiCallsPerDay !== null
  ) {
    errors.push('quotas.maxApiCallsPerDay')
  }

  // 3. 自定义扩展项 key 校验与去重
  const seen = new Set<string>()
  ;(model.extra?.customItems || []).forEach((it, idx) => {
    const key = (it?.key || '').trim()
    const label = (it?.label || '').trim()

    if (!key) errors.push(`extra.customItems[${idx}].key`)
    if (!label) errors.push(`extra.customItems[${idx}].label`)
    if (key && !/^[a-zA-Z][a-zA-Z0-9_]{0,63}$/.test(key))
      errors.push(`extra.customItems[${idx}].key`)
    if (key && seen.has(key)) errors.push(`extra.customItems[${idx}].key`)
    if (key) seen.add(key)
  })

  return errors
}

function toRecord(value: unknown): Record<string, any> {
  return value && typeof value === 'object' ? (value as Record<string, any>) : {}
}

function normalizeNumber(value: unknown): number | undefined {
  if (value === null || value === undefined || value === '') return undefined
  const num = Number(value)
  if (!Number.isFinite(num)) return undefined
  if (num < 0) return 0
  return Math.floor(num)
}

function normalizeCustomItems(value: unknown): FeaturePolicyCustomItem[] {
  if (!Array.isArray(value)) return []
  return value
    .filter((x) => x && typeof x === 'object')
    .map((x) => x as FeaturePolicyCustomItem)
    .map((x) => ({
      key: String(x.key ?? '').trim(),
      label: String(x.label ?? '').trim(),
      type: (x.type === 'boolean' || x.type === 'number' || x.type === 'string'
        ? x.type
        : 'string') as 'boolean' | 'number' | 'string',
      value:
        x.type === 'boolean'
          ? !!x.value
          : x.type === 'number'
            ? Number.isFinite(Number(x.value))
              ? Number(x.value)
              : 0
            : String(x.value ?? '')
    }))
}
