export type PoolSupportState = "supported" | "unsupported"
export type RebalanceDisplayState =
  | "unsupported"
  | "idle"
  | "starting"
  | "running"
  | "stopping"
  | "completed"
  | "failed"
  | "stopped"
  | "unknown"
export type DecommissionDisplayState =
  | "unsupported"
  | "blocked-by-rebalance"
  | "ready"
  | "confirming"
  | "running"
  | "canceling"
  | "completed"
  | "failed"
  | "canceled"
  | "unknown"

export interface PoolUsageProgress {
  bytes: number
  objects: number
  versions: number
  eta: number
  elapsed: number
}

export interface RebalanceCleanupWarnings {
  count: number
  lastMessage?: string
  lastBucket?: string
  lastObject?: string
  lastAt?: string
  present?: boolean
}

export interface PoolDecommissionSummary {
  startTime?: string
  startSize: number
  totalSize: number
  currentSize: number
  complete: boolean
  failed: boolean
  canceled: boolean
  queued: boolean
  queuedBuckets: string[]
  decommissionedBuckets: string[]
  bucket: string
  prefix: string
  object: string
  stage: string
  objects: number
  objectsFailed: number
  bytes: number
  bytesFailed: number
  waitingReason?: string
}

export interface PoolSummary {
  id: string
  name: string
  total: number
  used: number
  available: number
  currentSize: number
  usagePercent: number
  lastUpdate?: string
  status: string
  decommissionStatus: string
  rebalanceStatus: string
  progress: PoolUsageProgress
  cleanupWarnings: RebalanceCleanupWarnings
  decommission: PoolDecommissionSummary
}

export interface PoolsOverview {
  pools: PoolSummary[]
  totalCapacity: number
  totalUsedCapacity: number
  totalAvailableCapacity: number
  poolCount: number
  supportState: PoolSupportState
}

export interface RebalanceStatus {
  id: string
  status: string
  startedAt?: string
  updatedAt?: string
  stoppedAt?: string
  finishedAt?: string
  message?: string
  progressPercent: number
  pools: PoolSummary[]
  totals: PoolUsageProgress
}

export interface DecommissionInfo {
  status: string
  poolStatus: string
  poolId: string
  complete: boolean
  failed: boolean
  canceled: boolean
  queued: boolean
  progressPercent: number
  objects: number
  objectsFailed: number
  versions: number
  bytes: number
  bytesFailed: number
  startSize: number
  totalSize: number
  currentSize: number
  bucket: string
  prefix: string
  object: string
  stage: string
  waitingReason?: string
  message?: string
  startedAt?: string
  updatedAt?: string
}

type JsonRecord = Record<string, unknown>

function asRecord(value: unknown): JsonRecord {
  return value && typeof value === "object" ? (value as JsonRecord) : {}
}

function asArray(value: unknown): unknown[] {
  return Array.isArray(value) ? value : []
}

function asStringArray(value: unknown): string[] {
  return asArray(value).map(String)
}

function asString(value: unknown): string {
  return typeof value === "string" ? value : ""
}

function asIdString(value: unknown): string {
  return value === undefined || value === null ? "" : String(value)
}

function asNumber(value: unknown): number {
  if (typeof value === "number" && Number.isFinite(value)) return value
  if (typeof value === "string" && value.trim() !== "") {
    const parsed = Number(value)
    return Number.isFinite(parsed) ? parsed : 0
  }
  return 0
}

function asBoolean(value: unknown): boolean {
  return typeof value === "boolean" ? value : false
}

function clampPercent(value: number): number {
  if (!Number.isFinite(value)) return 0
  return Math.max(0, Math.min(100, value))
}

function normalizeProgress(value: unknown): PoolUsageProgress {
  const record = asRecord(value)
  return {
    bytes: asNumber(record.bytes),
    objects: asNumber(record.objects),
    versions: asNumber(record.versions),
    eta: asNumber(record.eta),
    elapsed: asNumber(record.elapsed),
  }
}

function normalizeCleanupWarnings(value: unknown): RebalanceCleanupWarnings {
  const present = value !== undefined && value !== null && typeof value === "object"
  const record = asRecord(value)
  const entries = asArray(record.entries || record.Entries)
  const rawCount = record.count ?? record.Count
  return {
    count: rawCount === undefined || rawCount === null ? entries.length : asNumber(rawCount),
    lastMessage: asString(record.lastMsg || record.LastMsg || record.lastMessage || record.LastMessage) || undefined,
    lastBucket: asString(record.lastBucket || record.LastBucket) || undefined,
    lastObject: asString(record.lastObject || record.LastObject) || undefined,
    lastAt: asString(record.lastAt || record.LastAt) || undefined,
    present,
  }
}

function normalizeDecommissionSummary(value: unknown): PoolDecommissionSummary {
  const record = asRecord(value)
  return {
    startTime: asString(record.startTime || record.StartTime) || undefined,
    startSize: asNumber(record.startSize || record.StartSize),
    totalSize: asNumber(record.totalSize || record.TotalSize),
    currentSize: asNumber(record.currentSize || record.CurrentSize),
    complete: asBoolean(record.complete || record.Complete),
    failed: asBoolean(record.failed || record.Failed),
    canceled: asBoolean(record.canceled || record.Canceled),
    queued: asBoolean(record.queued || record.Queued),
    queuedBuckets: asStringArray(record.queuedBuckets || record.QueuedBuckets),
    decommissionedBuckets: asStringArray(record.decommissionedBuckets || record.DecommissionedBuckets),
    bucket: asString(record.bucket || record.Bucket),
    prefix: asString(record.prefix || record.Prefix),
    object: asString(record.object || record.Object),
    stage: asString(record.stage || record.Stage),
    objects: asNumber(record.objectsDecommissioned || record.ObjectsDecommissioned || record.objects || record.Objects),
    objectsFailed: asNumber(
      record.objectsDecommissionedFailed || record.ObjectsDecommissionedFailed || record.objectsFailed,
    ),
    bytes: asNumber(record.bytesDecommissioned || record.BytesDecommissioned || record.bytes || record.Bytes),
    bytesFailed: asNumber(record.bytesDecommissionedFailed || record.BytesDecommissionedFailed || record.bytesFailed),
    waitingReason: asString(record.waitingReason || record.WaitingReason) || undefined,
  }
}

function hasProgress(progress: PoolUsageProgress): boolean {
  return Boolean(progress.bytes || progress.objects || progress.versions || progress.eta || progress.elapsed)
}

function aggregatePoolProgress(pools: PoolSummary[]): PoolUsageProgress {
  return pools.reduce(
    (totals, pool) => ({
      bytes: totals.bytes + pool.progress.bytes,
      objects: totals.objects + pool.progress.objects,
      versions: totals.versions + pool.progress.versions,
      eta: Math.max(totals.eta, pool.progress.eta),
      elapsed: Math.max(totals.elapsed, pool.progress.elapsed),
    }),
    { bytes: 0, objects: 0, versions: 0, eta: 0, elapsed: 0 },
  )
}

function hasExplicitProgressPercent(record: JsonRecord): boolean {
  return (
    record.progressPercent !== undefined ||
    record.ProgressPercent !== undefined ||
    record.percent !== undefined ||
    record.Percent !== undefined
  )
}

function deriveRebalanceProgressPercent(status: string, pools: PoolSummary[]): number {
  const state = normalizeState(status)
  const rebalancePools = participatingRebalancePools(pools)
  if (
    ["completed", "complete", "success", "finished"].includes(state) &&
    rebalancePools.length > 0 &&
    rebalancePools.every(isCompletedRebalancePool)
  ) {
    return 100
  }
  if (rebalancePools.length === 0) return 0

  const finishedPools = rebalancePools.filter(isCompletedRebalancePool).length
  return Math.round((finishedPools / rebalancePools.length) * 100)
}

function pickStatus(record: JsonRecord): string {
  return (
    asString(record.status) ||
    asString(record.State) ||
    asString(record.state) ||
    asString(record.phase) ||
    asString(record.result)
  )
}

function pickFieldStatus(record: JsonRecord, camelKey: string, pascalKey: string): string {
  return asString(record[camelKey]) || asString(record[pascalKey])
}

function derivePoolLifecycleStatus(decommissionStatus: string): string {
  const state = normalizeState(decommissionStatus)
  if (["complete", "completed", "success", "finished"].includes(state)) return "decommissioned"
  if (["failed", "canceled", "cancelled"].includes(state)) return "blocked"
  if (["queued", "running", "starting", "started", "in_progress", "in-progress", "stopping"].includes(state)) {
    return "decommissioning"
  }
  return "active"
}

function normalizePool(value: unknown, index: number): PoolSummary {
  const record = asRecord(value)
  const decommissionInfo = asRecord(record.decommissionInfo || record.DecommissionInfo)
  const decommission = normalizeDecommissionSummary(decommissionInfo)
  const total = asNumber(
    record.total || record.capacity || record.totalSize || record.Total || record.TotalSize || decommission.totalSize,
  )
  const explicitUsed = asNumber(record.usedSize || record.UsedSize || record.usedCapacity || record.UsedCapacity)
  const rawUsed = asNumber(record.used || record.Used)
  const used =
    explicitUsed || (rawUsed > 0 && rawUsed <= 1 && total > 0 ? total * rawUsed : rawUsed) || decommission.currentSize
  const currentSize = asNumber(record.currentSize || record.CurrentSize || decommission.currentSize)
  const available =
    asNumber(record.available || record.availableCapacity || record.Available || record.AvailableCapacity) ||
    Math.max(total - used, 0)
  const rawUsagePercent = asNumber(record.used || record.Used || record.usagePercent || record.UsagePercent)
  const usagePercent = clampPercent(
    rawUsagePercent > 0 && rawUsagePercent <= 1 ? rawUsagePercent * 100 : rawUsagePercent,
  )
  const progress = normalizeProgress(record.progress || record.Progress)
  const cleanupWarnings = normalizeCleanupWarnings(record.cleanupWarnings || record.CleanupWarnings)
  const rawId =
    record.id ?? record.poolId ?? record.pool ?? record.index ?? record.ID ?? record.PoolID ?? record.poolIndex
  const decommissionStatus =
    pickFieldStatus(record, "decommissionStatus", "DecommissionStatus") || deriveDecommissionStatus(decommissionInfo)
  const rebalanceStatus = pickFieldStatus(record, "rebalanceStatus", "RebalanceStatus")
  const lifecycleStatus = pickStatus(record) || derivePoolLifecycleStatus(decommissionStatus)

  return {
    id: String(rawId ?? index),
    name: asString(record.name) || asString(record.cmdline) || `Pool ${String(rawId ?? index)}`,
    total,
    used,
    available,
    currentSize,
    usagePercent: usagePercent || (total > 0 ? clampPercent((used / total) * 100) : 0),
    lastUpdate: asString(record.lastUpdate || record.LastUpdate || record.updatedAt || record.UpdatedAt) || undefined,
    status: lifecycleStatus,
    decommissionStatus,
    rebalanceStatus,
    progress,
    cleanupWarnings,
    decommission,
  }
}

function computeOverview(pools: PoolSummary[]): PoolsOverview {
  const totalCapacity = pools.reduce((sum, pool) => sum + pool.total, 0)
  const totalUsedCapacity = pools.reduce((sum, pool) => sum + pool.used, 0)
  const totalAvailableCapacity = pools.reduce((sum, pool) => sum + pool.available, 0)
  return {
    pools,
    totalCapacity,
    totalUsedCapacity,
    totalAvailableCapacity,
    poolCount: pools.length,
    supportState: pools.length > 1 ? "supported" : "unsupported",
  }
}

export function normalizePoolsOverview(value: unknown): PoolsOverview {
  const record = asRecord(value)
  const poolsSource = asArray(record.pools).length
    ? asArray(record.pools)
    : asArray(record.Pools).length
      ? asArray(record.Pools)
      : asArray(value)
  const pools = poolsSource.map((pool, index) => normalizePool(pool, index))
  return computeOverview(pools)
}

export function normalizeRebalanceStatus(value: unknown): RebalanceStatus {
  const record = asRecord(value)
  const poolsSource = asArray(record.pools).length
    ? asArray(record.pools)
    : asArray(record.Pools).length
      ? asArray(record.Pools)
      : []
  const pools = poolsSource.map((pool, index) => {
    const normalized = normalizePool(pool, index)
    const poolRecord = asRecord(pool)
    return {
      ...normalized,
      rebalanceStatus: normalized.rebalanceStatus || pickStatus(poolRecord),
    }
  })
  const explicitTotals = normalizeProgress(record.progress || record.Progress || record.totals || record.Totals)
  const totalsAreExplicit = hasProgress(explicitTotals)
  const totals = totalsAreExplicit ? explicitTotals : aggregatePoolProgress(pools)
  const status = pickStatus(record) || deriveStatusFromPools(pools)
  const rawProgress = hasExplicitProgressPercent(record)
    ? asNumber(record.progressPercent || record.ProgressPercent || record.percent || record.Percent)
    : totalsAreExplicit && totals.bytes > 0 && pools.length > 0
      ? (pools.reduce((sum, pool) => sum + pool.progress.bytes, 0) / totals.bytes) * 100
      : deriveRebalanceProgressPercent(status, pools)

  return {
    id: asString(record.id) || asString(record.ID),
    status,
    startedAt: asString(record.startedAt || record.StartedAt) || undefined,
    updatedAt: asString(record.updatedAt || record.UpdatedAt) || undefined,
    stoppedAt: asString(record.stoppedAt || record.StoppedAt) || undefined,
    finishedAt: asString(record.finishedAt || record.FinishedAt) || undefined,
    message: asString(record.message || record.Message) || undefined,
    progressPercent: clampPercent(rawProgress),
    pools,
    totals,
  }
}

export function normalizeDecommissionInfo(value: unknown, fallbackPoolId = ""): DecommissionInfo {
  const record = asRecord(value)
  const info = asRecord(record.decommissionInfo || record.DecommissionInfo || value)
  const status = pickStatus(record) || pickStatus(info) || deriveDecommissionStatus(info)
  const poolStatus = pickFieldStatus(record, "poolStatus", "PoolStatus")
  const startSize = asNumber(info.startSize || info.StartSize)
  const totalSize =
    asNumber(info.totalSize || info.TotalSize) || asNumber(record.totalSize || record.TotalSize || record.capacity)
  const bytes = asNumber(info.bytesDecommissioned || info.BytesDecommissioned || info.bytes || info.Bytes)
  const progressBase = Math.max(totalSize - startSize, 0)
  const rawProgress =
    asNumber(info.progressPercent || info.ProgressPercent || info.percent || info.Percent) ||
    (asBoolean(info.complete || info.Complete) ? 100 : progressBase > 0 ? (bytes / progressBase) * 100 : 0)

  return {
    status,
    poolStatus,
    poolId:
      asIdString(
        record.id ??
          record.poolId ??
          record.pool ??
          record.ID ??
          record.PoolID ??
          info.poolId ??
          info.pool ??
          info.PoolID,
      ) || fallbackPoolId,
    complete: asBoolean(info.complete || info.Complete),
    failed: asBoolean(info.failed || info.Failed),
    canceled: asBoolean(info.canceled || info.Canceled),
    queued: asBoolean(info.queued || info.Queued),
    progressPercent: clampPercent(rawProgress),
    objects: asNumber(
      info.objectsDecommissioned || info.ObjectsDecommissioned || info.objects || info.Objects || info.objectCount,
    ),
    objectsFailed: asNumber(info.objectsDecommissionedFailed || info.ObjectsDecommissionedFailed || info.objectsFailed),
    versions: asNumber(info.versions || info.Versions || info.versionCount),
    bytes,
    bytesFailed: asNumber(info.bytesDecommissionedFailed || info.BytesDecommissionedFailed || info.bytesFailed),
    startSize,
    totalSize,
    currentSize: asNumber(info.currentSize || info.CurrentSize || info.size || info.Size),
    bucket: asString(info.bucket || info.Bucket),
    prefix: asString(info.prefix || info.Prefix),
    object: asString(info.object || info.Object),
    stage: asString(info.stage || info.Stage),
    waitingReason: asString(info.waitingReason || info.WaitingReason) || undefined,
    message: asString(info.message || info.Message) || undefined,
    startedAt: asString(info.startedAt || info.StartedAt || info.startTime || info.StartTime) || undefined,
    updatedAt: asString(info.updatedAt || info.UpdatedAt) || undefined,
  }
}

export function normalizeDecommissionStatus(value: unknown): DecommissionInfo[] {
  const record = asRecord(value)
  const source = asArray(record.pools).length
    ? asArray(record.pools)
    : asArray(record.Pools).length
      ? asArray(record.Pools)
      : []
  if (source.length === 0 && Object.keys(record).length > 0) {
    const poolId = asIdString(record.id ?? record.poolId ?? record.pool ?? record.ID ?? record.PoolID)
    return [normalizeDecommissionInfo(record, poolId)]
  }
  return source.map((item) => {
    const itemRecord = asRecord(item)
    const poolId = asIdString(
      itemRecord.id ?? itemRecord.poolId ?? itemRecord.pool ?? itemRecord.ID ?? itemRecord.PoolID,
    )
    return normalizeDecommissionInfo(item, poolId)
  })
}

function normalizeState(value: string): string {
  return value.trim().toLowerCase()
}

export function isRebalanceNotStartedError(error: unknown): boolean {
  return error instanceof Error && normalizeState(error.message) === "pool rebalance is not started"
}

function deriveDecommissionStatus(info: JsonRecord): string {
  if (asBoolean(info.complete || info.Complete)) return "complete"
  if (asBoolean(info.failed || info.Failed)) return "failed"
  if (asBoolean(info.canceled || info.Canceled)) return "canceled"
  if (asBoolean(info.queued || info.Queued)) return "queued"
  if (asString(info.startTime || info.StartTime)) return "running"
  return ""
}

function isIdleRebalancePool(pool: PoolSummary): boolean {
  return ["", "none", "not_started", "not-started", "idle"].includes(normalizeState(pool.rebalanceStatus))
}

function isCompletedRebalancePool(pool: PoolSummary): boolean {
  return ["completed", "complete", "success", "finished"].includes(normalizeState(pool.rebalanceStatus))
}

function participatingRebalancePools(pools: PoolSummary[]): PoolSummary[] {
  return pools.filter((pool) => !isIdleRebalancePool(pool) || hasProgress(pool.progress))
}

function deriveStatusFromPools(pools: PoolSummary[]): string {
  if (pools.length === 0) return ""

  const states = pools.map((pool) => normalizeState(pool.rebalanceStatus))
  if (states.some((state) => ["failed", "error"].includes(state))) return "failed"
  if (states.some((state) => ["stopping", "stop_requested", "stop-requested"].includes(state))) return "stopping"
  if (
    states.some((state) =>
      ["running", "in_progress", "in-progress", "progressing", "starting", "started"].includes(state),
    )
  ) {
    return "running"
  }
  const rebalancePools = participatingRebalancePools(pools)
  if (rebalancePools.length > 0 && rebalancePools.every(isCompletedRebalancePool)) {
    return "completed"
  }
  if (pools.some(isCompletedRebalancePool) && pools.some(isIdleRebalancePool)) return "running"
  if (pools.every(isIdleRebalancePool)) return ""
  return ""
}

export function deriveRebalanceDisplayState(
  status: RebalanceStatus | null,
  supportState: PoolSupportState,
): RebalanceDisplayState {
  if (supportState === "unsupported") return "unsupported"
  if (!status) return "idle"

  const state = normalizeState(status.status)
  if (!state) return status.id ? "starting" : "idle"
  if (["running", "in_progress", "in-progress", "progressing"].includes(state)) return "running"
  if (["starting", "started"].includes(state)) return "starting"
  if (["stopping", "stop_requested", "stop-requested"].includes(state)) return "stopping"
  if (["completed", "complete", "success", "finished"].includes(state)) return "completed"
  if (["failed", "error"].includes(state)) return "failed"
  if (["stopped", "stopped_by_user", "cancelled", "canceled"].includes(state)) return "stopped"
  if (["idle", "none", "not_started", "not-started"].includes(state)) return "idle"
  return "unknown"
}

export function deriveDecommissionDisplayState(
  info: DecommissionInfo | null,
  supportState: PoolSupportState,
  rebalanceState: RebalanceDisplayState,
  isConfirming = false,
): DecommissionDisplayState {
  if (supportState === "unsupported") return "unsupported"
  if (["starting", "running", "stopping"].includes(rebalanceState)) {
    return "blocked-by-rebalance"
  }
  if (isConfirming) return "confirming"
  if (!info) return "ready"

  const state = normalizeState(info.status)
  if (info.complete || ["complete", "completed", "success", "finished"].includes(state)) return "completed"
  if (info.failed || ["failed", "error"].includes(state)) return "failed"
  if (info.canceled || ["canceled", "cancelled", "stopped"].includes(state)) return "canceled"
  if (info.queued || ["queued", "waiting"].includes(state)) return "running"
  if (["canceling", "cancelling", "stopping"].includes(state)) return "canceling"
  if (["running", "in_progress", "in-progress", "started", "starting"].includes(state)) return "running"
  return "ready"
}
