import type {
  BusinessDependency,
  BusinessHealth,
  BusinessSystemItem,
  BusinessTopology,
  BusinessTopologyEdge,
  BusinessTopologyNode,
} from '@/api/ops/businessSystem'
import type {
  BusinessTopologyFlowEdge,
  BusinessTopologyFlowNode,
  BusinessTopologyGraph,
  BusinessTopologyHealthFilter,
  BusinessTopologyViewMode,
} from './types'

const HEALTHY_THRESHOLD = 90
const DEGRADED_THRESHOLD = 60

export function scoreValue(score?: number): number {
  if (typeof score !== 'number' || Number.isNaN(score)) {
    return 0
  }

  return Math.max(0, Math.min(100, Math.round(score)))
}

export function healthLevelFromScore(score?: number): 'healthy' | 'degraded' | 'critical' {
  const value = scoreValue(score)

  if (value >= HEALTHY_THRESHOLD) {
    return 'healthy'
  }

  if (value >= DEGRADED_THRESHOLD) {
    return 'degraded'
  }

  return 'critical'
}

export function statusColor(status?: string): string {
  const normalized = status?.trim().toLowerCase()

  if (!normalized) {
    return '#8c8c8c'
  }

  if (['healthy', 'normal', 'ok', 'online', 'active', 'running', 'enabled'].includes(normalized)) {
    return '#52c41a'
  }

  if (['degraded', 'warning', 'warn', 'pending', 'maintenance', 'medium'].includes(normalized)) {
    return '#faad14'
  }

  if (['critical', 'error', 'failed', 'failure', 'offline', 'inactive', 'disabled', 'high'].includes(normalized)) {
    return '#ff4d4f'
  }

  return '#1677ff'
}

export function healthFilterMatch(item: BusinessSystemItem, filter: BusinessTopologyHealthFilter): boolean {
  if (filter === 'all') {
    return true
  }

  const level = normalizedHealthLevel(item)

  if (filter === 'unknown') {
    return level === 'unknown'
  }

  return level === filter
}

export function buildBusinessTopologyGraph(input: {
  currentBusiness: BusinessSystemItem
  topology: BusinessTopology
  health?: BusinessHealth | null
  mode: BusinessTopologyViewMode
  keyword?: string
}): BusinessTopologyGraph {
  const { currentBusiness, topology, health, mode } = input
  const keyword = input.keyword?.trim().toLowerCase()
  const currentBusinessId = businessNodeId(currentBusiness.id)
  const includeResourceTopology = mode === 'resource'
  const emphasizeDependencyEdges = mode === 'dependency'
  const dependencies = topology.dependencies ?? []
  const topologyNodes = topology.nodes ?? []
  const topologyLinks = topology.links ?? []
  const nodes: BusinessTopologyFlowNode[] = []
  const edges: BusinessTopologyFlowEdge[] = []
  const businessNodeIds = new Set<string>()
  const resourceNodeIds = new Map<string, string>()
  const resourceFlowNodeIds = new Set<string>()
  const resourceContainmentEdgeIds = new Set<string>()
  const matchedNodeIds = new Set<string>()

  const addBusinessNode = (node: BusinessTopologyFlowNode) => {
    if (businessNodeIds.has(node.id)) {
      return
    }

    businessNodeIds.add(node.id)
    nodes.push(node)

    if (keyword && nodeMatchesKeyword(node, keyword)) {
      matchedNodeIds.add(node.id)
    }
  }

  addBusinessNode(createCurrentBusinessNode(currentBusiness, health))

  dependencies.forEach((dependency, index) => {
    const sourceBusinessId = dependency.business_system_id
    const targetBusinessId = dependency.depends_on_business_system_id
    const externalIds = [sourceBusinessId, targetBusinessId].filter((id) => id !== currentBusiness.id)

    externalIds.forEach((businessId, externalIndex) => {
      addBusinessNode(createDependencyBusinessNode(businessId, dependency, index, externalIndex))
    })

    const edge: BusinessTopologyFlowEdge = {
      id: `business-dependency:${dependency.id}:${sourceBusinessId}->${targetBusinessId}`,
      source: businessNodeId(sourceBusinessId),
      target: businessNodeId(targetBusinessId),
      animated: false,
      data: {
        kind: 'business_dependency',
        label: dependency.dependency_type || '依赖',
        status: dependency.criticality,
        dependency,
      },
      label: dependency.dependency_type || '依赖',
      ...(emphasizeDependencyEdges ? { style: dependencyEdgeStyle(dependency) } : {}),
    }

    edge.animated = shouldAnimateEdge(edge, matchedNodeIds, keyword) || (emphasizeDependencyEdges && !keyword)
    edges.push(edge)
  })

  if (includeResourceTopology) {
    topologyNodes.forEach((topologyNode, index) => {
      const key = resourceKey(topologyNode)

      if (!key) {
        return
      }

      const id = resourceNodeId(key)
      rememberResourceNodeIds(resourceNodeIds, topologyNode, id)

      if (resourceFlowNodeIds.has(id)) {
        return
      }

      resourceFlowNodeIds.add(id)
      const node = createResourceNode(topologyNode, index)
      nodes.push(node)

      if (keyword && nodeMatchesKeyword(node, keyword)) {
        matchedNodeIds.add(node.id)
      }
    })

    topologyNodes.forEach((topologyNode) => {
      const target = resourceNodeIds.get(resourceKey(topologyNode))

      if (!target) {
        return
      }

      const edgeId = `business-resource:${currentBusiness.id}->${target}`

      if (resourceContainmentEdgeIds.has(edgeId)) {
        return
      }

      resourceContainmentEdgeIds.add(edgeId)
      const edge: BusinessTopologyFlowEdge = {
        id: edgeId,
        source: currentBusinessId,
        target,
        animated: false,
        data: {
          kind: 'resource_link',
          label: '包含',
        },
        label: '包含',
      }

      edge.animated = shouldAnimateEdge(edge, matchedNodeIds, keyword)
      edges.push(edge)
    })

    topologyLinks.forEach((link, index) => {
      const source = resourceNodeIds.get(link.source_node_id)
      const target = resourceNodeIds.get(link.target_node_id)

      if (!source || !target) {
        return
      }

      edges.push(createResourceLinkEdge(link, index, source, target, matchedNodeIds, keyword))
    })
  }

  return { nodes, edges }
}

function normalizedHealthLevel(item: BusinessSystemItem): 'healthy' | 'degraded' | 'critical' | 'unknown' {
  const level = item.health_level?.trim().toLowerCase()

  if (level === 'healthy' || level === 'degraded' || level === 'critical') {
    return level
  }

  if (typeof item.health_score === 'number' && !Number.isNaN(item.health_score)) {
    return healthLevelFromScore(item.health_score)
  }

  return 'unknown'
}

function createCurrentBusinessNode(currentBusiness: BusinessSystemItem, health?: BusinessHealth | null): BusinessTopologyFlowNode {
  const healthScore = health?.health.score ?? currentBusiness.health_score
  const label = currentBusiness.name || currentBusiness.code || `业务系统 ${currentBusiness.id}`

  return {
    id: businessNodeId(currentBusiness.id),
    type: 'businessTopology',
    position: { x: 0, y: 0 },
    data: {
      kind: 'business',
      label,
      status: currentBusiness.status,
      ...(typeof healthScore === 'number' ? { healthScore } : {}),
      businessSystem: currentBusiness,
    },
  }
}

function createDependencyBusinessNode(
  businessId: number,
  dependency: BusinessDependency,
  index: number,
  externalIndex: number,
): BusinessTopologyFlowNode {
  const isUpstream = dependency.depends_on_business_system_id === businessId
  const x = isUpstream ? 320 : -320
  const y = (index + externalIndex) * 120 - 120

  return {
    id: businessNodeId(businessId),
    type: 'businessTopology',
    position: { x, y },
    data: {
      kind: 'business',
      label: `业务系统 ${businessId}`,
      status: dependency.criticality || dependency.dependency_type || 'unknown',
      dependency,
    },
  }
}

function createResourceNode(topologyNode: BusinessTopologyNode, index: number): BusinessTopologyFlowNode {
  const key = resourceKey(topologyNode)

  return {
    id: resourceNodeId(key),
    type: 'businessTopology',
    position: {
      x: (index % 4) * 240 - 360,
      y: 260 + Math.floor(index / 4) * 140,
    },
    data: {
      kind: 'resource',
      label: topologyNode.label || topologyNode.resource_uid || topologyNode.node_id,
      status: topologyNode.status,
      ...(topologyNode.resource_uid ? { resourceUID: topologyNode.resource_uid } : {}),
      ...(topologyNode.type ? { resourceType: topologyNode.type } : {}),
      topologyNode,
    },
  }
}

function createResourceLinkEdge(
  link: BusinessTopologyEdge,
  index: number,
  source: string,
  target: string,
  matchedNodeIds: Set<string>,
  keyword?: string,
): BusinessTopologyFlowEdge {
  const edge: BusinessTopologyFlowEdge = {
    id: `resource-link:${link.id ?? index}:${source}->${target}`,
    source,
    target,
    animated: false,
    data: {
      kind: 'resource_link',
      label: '关联',
      topologyLink: link,
    },
    label: '关联',
  }

  edge.animated = shouldAnimateEdge(edge, matchedNodeIds, keyword)
  return edge
}

function dependencyEdgeStyle(dependency: BusinessDependency): NonNullable<BusinessTopologyFlowEdge['style']> {
  return {
    stroke: dependencyCriticalityColor(dependency.criticality),
    strokeWidth: 3,
  }
}

function dependencyCriticalityColor(criticality?: string): string {
  const normalized = criticality?.trim().toLowerCase()

  if (!normalized) {
    return '#f53f3f'
  }

  if (['critical', 'high', 'error', 'failed', 'failure'].includes(normalized)) {
    return '#f53f3f'
  }

  if (['medium', 'warning', 'warn', 'degraded'].includes(normalized)) {
    return '#faad14'
  }

  if (['low', 'healthy', 'normal', 'ok'].includes(normalized)) {
    return '#1677ff'
  }

  return '#f53f3f'
}

function businessNodeId(id: number): string {
  return `business:${id}`
}

function resourceNodeId(id: string): string {
  return `resource:${id}`
}

function resourceKey(node: BusinessTopologyNode): string {
  return node.node_id || node.resource_uid
}

function rememberResourceNodeIds(resourceNodeIds: Map<string, string>, node: BusinessTopologyNode, id: string) {
  const aliases = [resourceKey(node), node.node_id, node.resource_uid]

  aliases.forEach((alias) => {
    if (alias && !resourceNodeIds.has(alias)) {
      resourceNodeIds.set(alias, id)
    }
  })
}

function nodeMatchesKeyword(node: BusinessTopologyFlowNode, keyword: string): boolean {
  const data = node.data

  if (!data) {
    return node.id.toLowerCase().includes(keyword)
  }

  const values: Array<string | number | undefined> = [node.id, data.label, data.status]

  if (data.kind === 'business') {
    values.push(data.businessSystem?.code, data.businessSystem?.name, data.dependency?.dependency_type, data.dependency?.description)
  } else {
    values.push(data.resourceUID, data.resourceType, data.topologyNode?.node_id)
  }

  return values.some((value) => value?.toString().toLowerCase().includes(keyword))
}

function shouldAnimateEdge(edge: BusinessTopologyFlowEdge, matchedNodeIds: Set<string>, keyword?: string): boolean {
  if (!keyword) {
    return false
  }

  return matchedNodeIds.has(edge.source) || matchedNodeIds.has(edge.target) || edgeMatchesKeyword(edge, keyword)
}

function edgeMatchesKeyword(edge: BusinessTopologyFlowEdge, keyword: string): boolean {
  const data = edge.data
  const values: Array<string | number | undefined> = [
    edge.id,
    edge.source,
    edge.target,
    typeof edge.label === 'string' ? edge.label : undefined,
    data?.label,
    data?.status,
  ]

  if (data?.kind === 'business_dependency') {
    values.push(
      data.dependency?.id,
      data.dependency?.business_system_id,
      data.dependency?.depends_on_business_system_id,
      data.dependency?.dependency_type,
      data.dependency?.criticality,
      data.dependency?.description,
    )
  }

  if (data?.kind === 'resource_link') {
    values.push(data.topologyLink?.id, data.topologyLink?.source_node_id, data.topologyLink?.target_node_id, data.topologyLink?.topology_id)
  }

  return values.some((value) => value?.toString().toLowerCase().includes(keyword))
}
