import { View, Text, Image, Input, Button } from '@tarojs/components'
import { useEffect, useRef, useState } from 'react'
import Taro, { useDidShow, useDidHide } from '@tarojs/taro'

import { getToken } from '@/services/auth'
import {
  getNFCBindSession,
  resolveActiveBinding,
  setBindingChild,
  setSelectedBindingDeviceId,
  startNFCBind,
} from '@/services/binding'
import {
  Child,
  createChild,
  getChildren,
  resolveChildSelection,
  setSelectedChildId as setStoredSelectedChildId,
} from '@/services/child'

import './index.scss'

const SESSION_STATUS_PENDING = 1
const SESSION_STATUS_COMPLETED = 2
const SESSION_STATUS_EXPIRED = 3
const SESSION_STATUS_FAILED = 4
const SESSION_STATUS_CANCELLED = 5

function parseBindingPayload(rawValue: string): { deviceId: string; serialNumber: string } {
  const raw = String(rawValue || '').trim()
  if (!raw) return { deviceId: '', serialNumber: '' }

  try {
    const parsed = JSON.parse(raw)
    return {
      deviceId: String(parsed?.device_id || parsed?.deviceId || '').trim(),
      serialNumber: String(parsed?.serial_number || parsed?.serialNumber || '').trim(),
    }
  } catch (error) {
    console.log('[bind] scan payload is not json:', error)
  }

  const queryPart = raw.includes('?') ? raw.slice(raw.indexOf('?') + 1) : raw
  if (queryPart.includes('=')) {
    const pairMap = queryPart.split('&').reduce<Record<string, string>>((result, item) => {
      const [key, ...restValue] = item.split('=')
      if (!key) return result
      result[decodeURIComponent(key)] = decodeURIComponent(restValue.join('=') || '')
      return result
    }, {})
    const deviceId = pairMap.device_id || pairMap.deviceId || ''
    const serialNumber = pairMap.serial_number || pairMap.serialNumber || pairMap.sn || ''
    if (deviceId || serialNumber) {
      return {
        deviceId: deviceId.trim(),
        serialNumber: serialNumber.trim(),
      }
    }
  }

  const deviceIdMatch = raw.match(/device(?:_id|Id)?[:=]([A-Za-z0-9_-]+)/i)
  const serialNumberMatch = raw.match(/serial(?:_number|Number)?[:=]([A-Za-z0-9_-]+)/i)
  if (deviceIdMatch || serialNumberMatch) {
    return {
      deviceId: deviceIdMatch?.[1] || '',
      serialNumber: serialNumberMatch?.[1] || '',
    }
  }

  const parts = raw.split(/[\s,;|]+/).filter(Boolean)
  if (parts.length >= 2) {
    return {
      deviceId: parts[0],
      serialNumber: parts[1],
    }
  }

  return {
    deviceId: raw,
    serialNumber: '',
  }
}

export default function Bind() {
  const [loading, setLoading] = useState(true)
  const [submitting, setSubmitting] = useState(false)
  const [isScanning, setIsScanning] = useState(false)
  const [deviceId, setDeviceId] = useState('')
  const [serialNumber, setSerialNumber] = useState('')
  const [children, setChildren] = useState<Child[]>([])
  const [selectedChildId, setSelectedChildId] = useState<number | null>(null)
  const [newChildName, setNewChildName] = useState('')
  const [pendingDeviceId, setPendingDeviceId] = useState<string | null>(null)
  const [bindToken, setBindToken] = useState('')
  const [bindStatus, setBindStatus] = useState<number | null>(null)
  const [cardUUID, setCardUUID] = useState('')
  const [bindHint, setBindHint] = useState('')
  const pollingRef = useRef<ReturnType<typeof setTimeout> | null>(null)

  useDidShow(() => {
    void loadPageData()
  })

  useDidHide(() => {
    stopPolling()
  })

  useEffect(() => () => stopPolling(), [])

  const hasSelectedOrNewChild = selectedChildId !== null || Boolean(newChildName.trim())
  const isPendingBinding = Boolean(pendingDeviceId)
  const isPollingBind = bindStatus === SESSION_STATUS_PENDING && Boolean(bindToken)
  const canEditDeviceFields = hasSelectedOrNewChild && !isPollingBind

  const goDevicePage = () => {
    Taro.reLaunch({ url: '/pages/device/index' })
  }

  const stopPolling = () => {
    if (pollingRef.current) {
      clearTimeout(pollingRef.current)
      pollingRef.current = null
    }
  }

  const schedulePoll = () => {
    stopPolling()
    pollingRef.current = setTimeout(() => {
      void pollBindSession()
    }, 1500)
  }

  const resetBindSessionState = () => {
    stopPolling()
    setBindToken('')
    setBindStatus(null)
    setCardUUID('')
    setBindHint('')
  }

  const pollBindSession = async () => {
    if (!bindToken) return

    try {
      const session = await getNFCBindSession(bindToken)
      setBindStatus(session.status)
      setCardUUID(session.card_uuid || '')

      if (session.status === SESSION_STATUS_PENDING) {
        setBindHint('已发送绑卡指令，请拿自己的卡去设备上贴一下')
        schedulePoll()
        return
      }

      stopPolling()

      if (session.status === SESSION_STATUS_COMPLETED) {
        setSelectedBindingDeviceId(session.device_id)
        setBindHint(session.card_uuid ? `绑定完成，卡号 ${session.card_uuid}` : '绑定完成')
        Taro.showToast({ title: '设备绑定成功', icon: 'success' })
        setTimeout(() => {
          goDevicePage()
        }, 500)
        return
      }

      if (session.status === SESSION_STATUS_EXPIRED) {
        setBindHint('绑定会话已过期，请重新扫码并贴卡')
        return
      }

      if (session.status === SESSION_STATUS_FAILED) {
        setBindHint('贴卡绑定失败，请重试')
        return
      }

      if (session.status === SESSION_STATUS_CANCELLED) {
        setBindHint('当前绑定已被新的绑定流程替代，请重新扫码')
        return
      }
    } catch (error: any) {
      console.error('[bind] poll bind session failed:', error)
      setBindHint(error?.message || '查询绑定状态失败，请重试')
    }
  }

  const loadPageData = async () => {
    if (!getToken()) {
      Taro.reLaunch({ url: '/pages/login/index' })
      return
    }

    setLoading(true)
    try {
      const [childResponse, activeBinding] = await Promise.all([getChildren(), resolveActiveBinding()])
      const currentChildren = childResponse.items || []
      const currentChild = resolveChildSelection(currentChildren)
      setChildren(currentChildren)
      setSelectedChildId(currentChild?.child_id || null)

      if (activeBinding?.device_id && !activeBinding.child_id) {
        setPendingDeviceId(activeBinding.device_id)
        setDeviceId(activeBinding.device_id)
      } else {
        setPendingDeviceId(null)
      }
    } catch (error: any) {
      console.error('[bind] load failed:', error)
      Taro.showToast({
        title: error?.message || '加载失败，请稍后重试',
        icon: 'none',
      })
    } finally {
      setLoading(false)
    }
  }

  const ensureChildId = async (): Promise<number | null> => {
    if (selectedChildId) return selectedChildId

    const childName = newChildName.trim()
    if (!childName) return null

    const child = await createChild({ child_name: childName })
    setChildren((currentChildren) => [child, ...currentChildren])
    setSelectedChildId(child.child_id)
    setStoredSelectedChildId(child.child_id)
    setNewChildName('')
    return child.child_id
  }

  const handleScanCode = () => {
    if (!canEditDeviceFields) {
      Taro.showToast({
        title: '请先选择或新建儿童资料',
        icon: 'none',
      })
      return
    }

    setIsScanning(true)
    Taro.scanCode({
      onlyFromCamera: true,
      scanType: ['qrCode', 'barCode'],
      success: (res) => {
        setIsScanning(false)
        const parsed = parseBindingPayload(res.result || '')
        if (parsed.deviceId) setDeviceId(parsed.deviceId)
        if (parsed.serialNumber) setSerialNumber(parsed.serialNumber)

        Taro.showToast({
          title: parsed.serialNumber ? '已填入设备信息' : '已识别设备号，请补充序列号',
          icon: 'none',
        })
      },
      fail: (err) => {
        setIsScanning(false)
        if (err.errMsg && err.errMsg.includes('cancel')) return
        Taro.showToast({
          title: '扫码失败，请重试',
          icon: 'none',
        })
      },
    })
  }

  const handleSubmit = async () => {
    if (submitting || isPollingBind) return

    setSubmitting(true)
    Taro.showLoading({ title: isPendingBinding ? '关联中...' : '发送绑卡指令...' })

    try {
      const childId = await ensureChildId()
      if (!childId) {
        throw new Error('请先选择儿童或填写新的儿童昵称')
      }

      if (isPendingBinding) {
        await setBindingChild(pendingDeviceId!, { child_id: childId })
        setSelectedBindingDeviceId(pendingDeviceId)
        Taro.showToast({ title: '关联完成', icon: 'success' })
        setTimeout(() => {
          goDevicePage()
        }, 300)
        return
      }

      if (!deviceId.trim()) {
        throw new Error('请输入设备号')
      }
      if (!serialNumber.trim()) {
        throw new Error('请输入设备序列号')
      }

      resetBindSessionState()
      const session = await startNFCBind({
        device_id: deviceId.trim(),
        serial_number: serialNumber.trim(),
        child_id: childId,
      })

      setBindToken(session.bind_token)
      setBindStatus(session.status)
      setBindHint('已发送绑卡指令，请去设备上贴自己的卡')
      schedulePoll()
      Taro.showToast({ title: '请去设备上贴卡', icon: 'none' })
    } catch (error: any) {
      console.error('[bind] submit failed:', error)
      setBindHint(error?.message || '绑定失败，请重试')
      Taro.showToast({
        title: error?.message || '绑定失败，请重试',
        icon: 'none',
      })
    } finally {
      Taro.hideLoading()
      setSubmitting(false)
    }
  }

  if (loading) {
    return (
      <View className='bind-page'>
        <View className='loading'>
          <Text>加载中...</Text>
        </View>
      </View>
    )
  }

  return (
    <View className='bind-page'>
      <View className='bind-header'>
        <Text className='title'>{isPendingBinding ? '补全绑定' : '扫码贴卡绑定设备'}</Text>
        <Text className='subtitle'>
          {isPendingBinding
            ? '当前设备已存在待补全关系，请先补充儿童资料'
            : '先选择儿童，再扫码设备，系统会下发绑卡指令，随后去设备上贴卡确认'}
        </Text>
      </View>

      <View className='scan-area'>
        <View className='scan-frame' onClick={handleScanCode}>
          <View className='icon-bg orange'>
            <Image className='control-icon-img' src={require('../../assets/tab-icons/rings.png')} mode='aspectFit' />
          </View>
          <Text className='scan-text'>
            {!canEditDeviceFields ? '请先完成儿童资料' : isScanning ? '正在扫码...' : '点击扫码'}
          </Text>
          <Text className='scan-hint'>
            {!canEditDeviceFields ? '先选儿童，再扫设备二维码' : '扫描设备二维码或条码'}
          </Text>
        </View>

        <View className='scan-corners'>
          <View className='corner top-left'></View>
          <View className='corner top-right'></View>
          <View className='corner bottom-left'></View>
          <View className='corner bottom-right'></View>
        </View>
      </View>

      <View className='bind-form'>
        <View className='form-card'>
          <Text className='form-title'>儿童资料</Text>

          {children.length > 0 && (
            <View className='child-list'>
              {children.map((child) => (
                <View
                  key={child.child_id}
                  className={`child-item ${selectedChildId === child.child_id ? 'selected' : ''}`}
                  onClick={() => {
                    setSelectedChildId((currentChildId) => {
                      const nextChildId = currentChildId === child.child_id ? null : child.child_id
                      setStoredSelectedChildId(nextChildId)
                      return nextChildId
                    })
                    setNewChildName('')
                  }}
                >
                  <Text className='child-item-text'>{child.child_name}</Text>
                </View>
              ))}
            </View>
          )}

          <View className='field-item'>
            <Text className='field-label'>新建儿童昵称</Text>
            <Input
              className='field-input'
              value={newChildName}
              placeholder={children.length > 0 ? '不选已有儿童时，可填写新昵称' : '请输入儿童昵称'}
              onInput={(event) => {
                setNewChildName(event.detail.value)
                if (event.detail.value) {
                  setSelectedChildId(null)
                  setStoredSelectedChildId(null)
                }
              }}
            />
          </View>

          <Text className='field-hint'>
            {children.length > 0
              ? '可以选已有儿童，也可以在这里新建一个儿童后再绑定设备'
              : '当前还没有儿童资料，请先创建一个儿童资料'}
          </Text>
        </View>

        <View className='form-card'>
          <Text className='form-title'>设备信息</Text>

          <View className='field-item'>
            <Text className='field-label'>设备号</Text>
            <Input
              className='field-input'
              value={deviceId}
              disabled={!canEditDeviceFields || isPendingBinding}
              placeholder={canEditDeviceFields ? '请输入设备号' : '请先选择儿童资料'}
              onInput={(event) => setDeviceId(event.detail.value)}
            />
          </View>

          <View className='field-item'>
            <Text className='field-label'>设备序列号</Text>
            <Input
              className='field-input'
              value={serialNumber}
              disabled={!canEditDeviceFields || isPendingBinding}
              placeholder={canEditDeviceFields ? '请输入设备序列号' : '请先选择儿童资料'}
              onInput={(event) => setSerialNumber(event.detail.value)}
            />
          </View>

          {bindHint ? (
            <View className='pending-tip'>
              <Text className='pending-tip-text'>{bindHint}</Text>
              {cardUUID ? <Text className='pending-tip-subtext'>卡片 UUID：{cardUUID}</Text> : null}
            </View>
          ) : null}

          {isPendingBinding && (
            <View className='pending-tip'>
              <Text className='pending-tip-text'>当前待补全设备：{pendingDeviceId}</Text>
            </View>
          )}
        </View>

        <Button
          className='submit-btn'
          loading={submitting}
          disabled={submitting || !hasSelectedOrNewChild || isPollingBind}
          onClick={handleSubmit}
        >
          {isPendingBinding ? '完成儿童关联' : isPollingBind ? '等待贴卡确认' : '发送绑卡指令'}
        </Button>
      </View>

      <View className='bind-tips'>
        <Text className='tips-title'>绑定帮助</Text>
        <View className='tip-item'>
          <Text className='tip-number'>1</Text>
          <Text className='tip-text'>先选择已有儿童，或者在本页新建一个儿童</Text>
        </View>
        <View className='tip-item'>
          <Text className='tip-number'>2</Text>
          <Text className='tip-text'>扫描设备二维码，确认设备号和序列号正确</Text>
        </View>
        <View className='tip-item'>
          <Text className='tip-number'>3</Text>
          <Text className='tip-text'>点击发送绑卡指令，然后去设备上贴自己的卡完成确认</Text>
        </View>
      </View>
    </View>
  )
}
