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'
import { normalizeCloudtentaclesMatchName } from './cloudtentacles-match-utils.js'

const CLOUDTENTACLES_OVERRIDE_RULE_FILE_PATH = path.join(
  PROJECT_ROOT,
  'data',
  'cloudtentacles-override-rules.json',
)

export type CloudtentaclesOverrideDeliveryItem = {
  cloudSkuId: number
  cloudSkuName: string
  quantity: number
}

export type CloudtentaclesOverrideRule = {
  id: string
  enabled: boolean
  productName: string
  normalizedProductName: string
  sourceKey: string
  deliveryItems: CloudtentaclesOverrideDeliveryItem[]
  notes: string
}

export type CloudtentaclesOverrideRuleConfig = {
  enabled: boolean
  rules: CloudtentaclesOverrideRule[]
}

export function getCloudtentaclesOverrideRuleFilePath() {
  return CLOUDTENTACLES_OVERRIDE_RULE_FILE_PATH
}

export function getCloudtentaclesOverrideRuleConfig(): CloudtentaclesOverrideRuleConfig {
  return readAppConfigEntry({
    configKey: APP_CONFIG_KEYS.cloudtentaclesOverrideRules,
    legacyFilePath: CLOUDTENTACLES_OVERRIDE_RULE_FILE_PATH,
    fallback: createDefaultCloudtentaclesOverrideRuleConfig,
    normalize: normalizeCloudtentaclesOverrideRuleConfig,
  })
}

export function saveCloudtentaclesOverrideRuleConfig(
  rawValue: unknown,
): Promise<CloudtentaclesOverrideRuleConfig> {
  return saveAppConfigEntry({
    configKey: APP_CONFIG_KEYS.cloudtentaclesOverrideRules,
    legacyFilePath: CLOUDTENTACLES_OVERRIDE_RULE_FILE_PATH,
    value: rawValue,
    normalize: normalizeCloudtentaclesOverrideRuleConfig,
  })
}

export function resolveCloudtentaclesOverrideRule(productName: unknown) {
  const config = getCloudtentaclesOverrideRuleConfig()
  if (config.enabled === false) {
    return null
  }

  const normalizedProductName = normalizeCloudtentaclesMatchName(productName)
  if (!normalizedProductName) {
    return null
  }

  return (
    config.rules.find(
      (rule) =>
        rule.enabled !== false &&
        rule.normalizedProductName === normalizedProductName &&
        rule.deliveryItems.length > 0,
    ) || null
  )
}

export function normalizeCloudtentaclesOverrideRuleConfig(
  rawValue: unknown,
): CloudtentaclesOverrideRuleConfig {
  const source = isPlainObject(rawValue) ? rawValue : {}
  const rules = Array.isArray(source.rules) ? source.rules : []

  return {
    enabled: source.enabled !== false,
    rules: rules
      .map((item: unknown) => normalizeCloudtentaclesOverrideRule(item))
      .filter((item): item is CloudtentaclesOverrideRule => Boolean(item)),
  }
}

function normalizeCloudtentaclesOverrideRule(rawValue: unknown): CloudtentaclesOverrideRule | null {
  if (!isPlainObject(rawValue)) {
    return null
  }

  const productName = String(rawValue.productName || rawValue.name || '').trim()
  const normalizedProductName = normalizeCloudtentaclesMatchName(productName)
  const deliveryItems = normalizeDeliveryItems(rawValue.deliveryItems)

  if (!productName || !normalizedProductName || deliveryItems.length === 0) {
    return null
  }

  return {
    id: String(rawValue.id || normalizedProductName).trim() || normalizedProductName,
    enabled: rawValue.enabled !== false,
    productName,
    normalizedProductName,
    sourceKey: String(rawValue.sourceKey || '').trim(),
    deliveryItems,
    notes: String(rawValue.notes || '').trim(),
  }
}

function normalizeDeliveryItems(value: unknown) {
  const rawItems = Array.isArray(value) ? value : []
  const items = rawItems
    .map((item) => normalizeDeliveryItem(item))
    .filter((item): item is CloudtentaclesOverrideDeliveryItem => Boolean(item))

  return mergeDeliveryItems(items)
}

function normalizeDeliveryItem(value: unknown): CloudtentaclesOverrideDeliveryItem | null {
  const source = isPlainObject(value) ? value : {}
  const cloudSkuId = normalizePositiveInteger(source.cloudSkuId || source.skuId, 0)
  const cloudSkuName = String(source.cloudSkuName || source.skuName || '').trim()
  const quantity = normalizePositiveInteger(source.quantity, 1)

  if (!cloudSkuId && !cloudSkuName) {
    return null
  }

  return {
    cloudSkuId,
    cloudSkuName,
    quantity,
  }
}

function mergeDeliveryItems(items: CloudtentaclesOverrideDeliveryItem[]) {
  const merged = new Map<string, CloudtentaclesOverrideDeliveryItem>()

  for (const item of items) {
    const key =
      item.cloudSkuId > 0
        ? `id:${item.cloudSkuId}`
        : `name:${normalizeCloudtentaclesMatchName(item.cloudSkuName)}`
    const existing = merged.get(key)
    if (existing) {
      existing.quantity += item.quantity
      existing.cloudSkuName = existing.cloudSkuName || item.cloudSkuName
      existing.cloudSkuId = existing.cloudSkuId || item.cloudSkuId
      continue
    }

    merged.set(key, { ...item })
  }

  return Array.from(merged.values())
}

function normalizePositiveInteger(value: unknown, fallback: number) {
  const parsed = Number(value)
  if (!Number.isInteger(parsed) || parsed <= 0) {
    return fallback
  }

  return parsed
}

function createDefaultCloudtentaclesOverrideRuleConfig(): CloudtentaclesOverrideRuleConfig {
  return {
    enabled: true,
    rules: [],
  }
}

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