import {
  getIndustryTypeValue,
  getMajorDictEntries,
  getServiceDictEntries,
  isIndustryEnabledByType,
  isMajorIdInIndustryScope
} from '@/sql'

interface KvStoreLike {
  setItem: <T = unknown>(keyRaw: string | number, value: T) => Promise<void>
}

type DictItemLite = {
  code?: string
  name?: string
  defCoe?: number | null
  notshowByzxflxs?: boolean
  hasCost?: boolean | null
  hasArea?: boolean | null
}

type FactorPersistRow = {
  id: string
  code: string
  name: string
  standardFactor: number | null
  budgetValue: number | null
  remark: string
  path: string[]
}

type FactorPersistState = {
  detailRows: FactorPersistRow[]
}

type ScalePersistRow = {
  id: string
  groupCode: string
  groupName: string
  majorCode: string
  majorName: string
  hasCost: boolean
  hasArea: boolean
  amount: number | null
  landArea: number | null
  path: string[]
  hide?: boolean
  isGroupRow?: boolean
}

type ScalePersistState = {
  detailRows: ScalePersistRow[]
  totalAmount: number | null
  roughCalcEnabled: boolean
}

type ScalePersistStateSource = {
  detailRows?: unknown[]
  totalAmount?: unknown
  roughCalcEnabled?: unknown
}

type ScaleDictLeaf = {
  id: string
  code: string
  name: string
  hasCost: boolean
  hasArea: boolean
}

type ScaleDictGroup = {
  id: string
  code: string
  name: string
  children: ScaleDictLeaf[]
}

const buildCodePath = (code: string, selfId: string, codeIdMap: Map<string, string>) => {
  const parts = code.split('-').filter(Boolean)
  if (!parts.length) return [selfId]

  const path: string[] = []
  let currentCode = parts[0]
  const firstId = codeIdMap.get(currentCode)
  if (firstId) path.push(firstId)

  for (let index = 1; index < parts.length; index += 1) {
    currentCode = `${currentCode}-${parts[index]}`
    const id = codeIdMap.get(currentCode)
    if (id) path.push(id)
  }

  if (!path.length || path[path.length - 1] !== selfId) path.push(selfId)
  return path
}

const buildFactorRowsFromEntries = (entries: Array<{ id: string; item: DictItemLite }>): FactorPersistRow[] => {
  const codeIdMap = new Map<string, string>()
  for (const entry of entries) {
    const code = String(entry.item?.code || '').trim()
    if (!code) continue
    codeIdMap.set(code, entry.id)
  }

  return entries
    .map(entry => {
      const code = String(entry.item?.code || '').trim()
      const name = String(entry.item?.name || '').trim()
      if (!code || !name) return null
      const standardFactor =
        typeof entry.item?.defCoe === 'number' && Number.isFinite(entry.item.defCoe)
          ? entry.item.defCoe
          : null
      return {
        id: entry.id,
        code,
        name,
        standardFactor,
        budgetValue: standardFactor,
        remark: '',
        path: buildCodePath(code, entry.id, codeIdMap)
      }
    })
    .filter((item): item is FactorPersistRow => Boolean(item))
}

const buildScaleGroupsFromEntries = (entries: Array<{ id: string; item: DictItemLite }>) => {
  const groupMap = new Map<string, ScaleDictGroup>()
  const groupOrder: string[] = []
  const codeLookup = new Map(entries.map(entry => [String(entry.item?.code || '').trim(), entry]))

  for (const entry of entries) {
    const code = String(entry.item?.code || '').trim()
    const name = String(entry.item?.name || '').trim()
    if (!code || !name) continue

    if (!code.includes('-')) {
      if (!groupMap.has(code)) groupOrder.push(code)
      groupMap.set(code, {
        id: entry.id,
        code,
        name,
        children: []
      })
      continue
    }

    const parentCode = code.split('-')[0]
    if (!groupMap.has(parentCode)) {
      const parent = codeLookup.get(parentCode)
      if (!groupOrder.includes(parentCode)) groupOrder.push(parentCode)
      groupMap.set(parentCode, {
        id: parent?.id || `group-${parentCode}`,
        code: parentCode,
        name: String(parent?.item?.name || parentCode).trim(),
        children: []
      })
    }

    groupMap.get(parentCode)?.children.push({
      id: entry.id,
      code,
      name,
      hasCost: entry.item?.hasCost !== false,
      hasArea: entry.item?.hasArea !== false
    })
  }

  return groupOrder
    .map(code => groupMap.get(code))
    .filter((item): item is ScaleDictGroup => Boolean(item))
}

const buildScaleLeafRows = (groups: ScaleDictGroup[]): ScalePersistRow[] => (
  groups.flatMap(group =>
    group.children.map(child => ({
      id: child.id,
      groupCode: group.code,
      groupName: group.name,
      majorCode: child.code,
      majorName: child.name,
      hasCost: child.hasCost,
      hasArea: child.hasArea,
      amount: null,
      landArea: null,
      path: [`${group.code} ${group.name}`, `${child.code} ${child.name}`]
    }))
  )
)

const mergeScaleLeafRows = (defaultRows: ScalePersistRow[], rowsFromDb: ScalePersistRow[] | undefined) => {
  const dbValueMap = new Map<string, ScalePersistRow>()
  for (const row of rowsFromDb || []) {
    if (!row || row.isGroupRow === true) continue
    dbValueMap.set(String(row.id || ''), row)
  }

  return defaultRows.map(row => {
    const fromDb = dbValueMap.get(row.id)
    if (!fromDb) return row
    return {
      ...row,
      hide: fromDb.hide,
      amount: row.hasCost && typeof fromDb.amount === 'number' && Number.isFinite(fromDb.amount) ? fromDb.amount : null,
      landArea: row.hasArea && typeof fromDb.landArea === 'number' && Number.isFinite(fromDb.landArea) ? fromDb.landArea : null
    }
  })
}

const buildScaleGroupRows = (groups: ScaleDictGroup[], rows: ScalePersistRow[]): ScalePersistRow[] => {
  const rowById = new Map(rows.map(row => [String(row.id || ''), row] as const))

  return groups.map(group => {
    let amountTotal = 0
    let hasAmount = false
    let landAreaTotal = 0
    let hasLandArea = false

    for (const child of group.children) {
      const leaf = rowById.get(String(child.id || ''))
      const amount = leaf?.amount
      if (typeof amount === 'number' && Number.isFinite(amount)) {
        amountTotal += amount
        hasAmount = true
      }
      const landArea = leaf?.landArea
      if (typeof landArea === 'number' && Number.isFinite(landArea)) {
        landAreaTotal += landArea
        hasLandArea = true
      }
    }

    return {
      id: group.id,
      groupCode: group.code,
      groupName: group.name,
      majorCode: group.code,
      majorName: group.name,
      hasCost: true,
      hasArea: true,
      amount: hasAmount ? amountTotal : null,
      landArea: hasLandArea ? landAreaTotal : null,
      path: [`${group.code} ${group.name}`],
      hide: false,
      isGroupRow: true
    }
  })
}

export const buildDefaultProjectScaleState = (industry: string, source?: ScalePersistStateSource | null): ScalePersistState => {
  const majorEntries = getMajorDictEntries()
    .map(({ id, item }) => ({ id, item: item as DictItemLite }))
    .filter(({ id, item }) => item.notshowByzxflxs !== true && isMajorIdInIndustryScope(id, industry))

  const groups = buildScaleGroupsFromEntries(majorEntries)
  const defaultLeafRows = buildScaleLeafRows(groups)
  const detailRows = mergeScaleLeafRows(
    defaultLeafRows,
    Array.isArray(source?.detailRows) ? source.detailRows as ScalePersistRow[] : undefined
  )

  return {
    detailRows: [...detailRows, ...buildScaleGroupRows(groups, detailRows)],
    totalAmount:
      typeof source?.totalAmount === 'number' && Number.isFinite(source.totalAmount)
        ? source.totalAmount
        : null,
    roughCalcEnabled: source?.roughCalcEnabled === true
  }
}

export const initializeProjectFactorStates = async (
  kvStore: KvStoreLike,
  industry: string,
  consultCategoryFactorKey: string,
  majorFactorKey: string
) => {
  const industryType = getIndustryTypeValue(industry)
  const consultEntries = getServiceDictEntries()
    .map(({ id, item }) => ({ id, item: item as DictItemLite }))
    .filter(({ item }) => {
      if (item.notshowByzxflxs === true) return false
      return isIndustryEnabledByType(item as Record<string, unknown>, industryType)
    })

  const majorEntries = getMajorDictEntries()
    .map(({ id, item }) => ({ id, item: item as DictItemLite }))
    .filter(({ id, item }) => item.notshowByzxflxs !== true && isMajorIdInIndustryScope(id, industry))

  const consultPayload: FactorPersistState = {
    detailRows: buildFactorRowsFromEntries(consultEntries)
  }
  const majorPayload: FactorPersistState = {
    detailRows: buildFactorRowsFromEntries(majorEntries)
  }

  await Promise.all([
    kvStore.setItem(consultCategoryFactorKey, consultPayload),
    kvStore.setItem(majorFactorKey, majorPayload)
  ])
}

export const initializeProjectScaleState = async (
  kvStore: KvStoreLike,
  industry: string,
  projectScaleKey: string
) => {
  await kvStore.setItem(projectScaleKey, buildDefaultProjectScaleState(industry))
}
