import {
  ApiOutlined,
  ControlOutlined,
  DatabaseOutlined,
  DeleteOutlined,
  EditOutlined,
  EyeOutlined,
  FileTextOutlined,
  GoogleOutlined,
  PlusOutlined,
  SyncOutlined,
  ThunderboltOutlined,
  UploadOutlined,
} from '@ant-design/icons'
import {
  Alert,
  Breadcrumb,
  Button,
  Col,
  Descriptions,
  Divider,
  Form,
  Input,
  Modal,
  Popconfirm,
  Row,
  Segmented,
  Select,
  Space,
  Spin,
  Table,
  Tag,
  Tooltip,
  Typography,
  Upload,
  message,
} from 'antd'
import type { UploadFile } from 'antd'
import { useMemo, useState } from 'react'
import { useQueryClient } from '@tanstack/react-query'
import { useNavigate, useParams } from 'react-router-dom'
import {
  useDeleteDevice,
  useDeleteAllDevices,
  useForceDeleteObject,
  useDiscoverObject,
  useDevices,
  useImportCSV,
  useObject,
  usePreviewCSV,
  useSyncObject,
  useSyncSheets,
  type DeviceImportPreview,
  type DeviceItem,
} from '../api/objects'
import { useRacks, useCreateRack, useUpdateRack, useDeleteRack, type RackItem } from '../api/racks'
import { useZones, useCreateZone, useDeleteZone, type ZoneItem } from '../api/zones'
import { CategoryTag, DeviceStatusBadge } from '../components/StatusBadge'
import { DeviceEditModal } from '../components/DeviceEditModal'
import { DiscoveryDrawer } from '../components/DiscoveryDrawer'
import { stripEmpty } from '../utils/form'
import { getDeviceWebUrl } from '../utils/deviceUrl'
import dayjs from 'dayjs'

const SERVER_CATEGORIES = new Set(['main_server', 'vm', 'router'])

const rackLabel = (name: string, type: string | null | undefined): string =>
  type ? `${name} ${type}` : name
const DEVICE_COL_SPAN = 8

type GroupRow = { _type: 'group'; _key: string; label: string; count: number; onlineCount: number }
type DeviceRow = DeviceItem & { _type: 'device' }
type TableRow = GroupRow | DeviceRow


export function InventoryObjectDetailPage() {
  const { id } = useParams<{ id: string }>()
  const objId = Number(id)
  const navigate = useNavigate()
  const queryClient = useQueryClient()

  const { data: obj, isLoading: objLoading } = useObject(objId)
  const { data: devices, isLoading: devLoading } = useDevices(objId)
  const { data: racks } = useRacks(objId)
  const { data: zones } = useZones(objId)
  const syncMutation = useSyncObject(objId)
  const discoverMutation = useDiscoverObject(objId)
  const syncSheetsMutation = useSyncSheets(objId)
  const deleteDevice = useDeleteDevice(objId)
  const previewCSV = usePreviewCSV(objId)
  const importCSV = useImportCSV(objId)
  const createRack = useCreateRack(objId)
  const updateRack = useUpdateRack(objId)
  const deleteRack = useDeleteRack(objId)
  const createZone = useCreateZone(objId)
  const deleteZone = useDeleteZone(objId)
  const deleteAllDevices = useDeleteAllDevices(objId)
  const forceDeleteObject = useForceDeleteObject()

  const [deviceModal, setDeviceModal] = useState(false)
  const [editDevice, setEditDevice] = useState<DeviceItem | null>(null)
  const [csvModal, setCsvModal] = useState(false)
  const [rackModal, setRackModal] = useState(false)
  const [sheetsModal, setSheetsModal] = useState(false)
  const [discoveryTaskId, setDiscoveryTaskId] = useState<string | null>(null)
  const [discoveryOpen, setDiscoveryOpen] = useState(false)
  const [editRack, setEditRack] = useState<RackItem | null>(null)
  const [csvFile, setCsvFile] = useState<File | null>(null)
  const [csvPreview, setCsvPreview] = useState<DeviceImportPreview | null>(null)
  const [searchText, setSearchText] = useState('')

  const [rackForm] = Form.useForm()
  const [zoneForm] = Form.useForm()
  const [sheetsForm] = Form.useForm()
  const [editRackForm] = Form.useForm()

  const handleSync = async () => {
    try {
      const result = await syncMutation.mutateAsync()
      message.success(`Синхронизировано: +${result.created} новых, ${result.updated} обновлено`)
    } catch {
      message.error('Ошибка синхронизации')
    }
  }

  const handleDiscover = async () => {
    try {
      const { task_id } = await discoverMutation.mutateAsync()
      setDiscoveryTaskId(task_id)
      setDiscoveryOpen(true)
    } catch {
      message.error('Ошибка SSH discovery')
    }
  }

  const handleDiscoveryComplete = () => {
    queryClient.invalidateQueries({ queryKey: ['devices', objId] })
  }

  const handleSyncSheets = async () => {
    try {
      const values = await sheetsForm.validateFields()
      const result = await syncSheetsMutation.mutateAsync({
        spreadsheet_id: values.spreadsheet_id,
        sheet_name: values.sheet_name || undefined,
      })
      message.success(`Google Sheets: +${result.created} создано, ${result.updated} обновлено`)
      setSheetsModal(false)
      sheetsForm.resetFields()
    } catch (err: any) {
      const detail = err?.response?.data?.detail
      if (detail) message.error(detail)
    }
  }

  const openAddDevice = () => {
    setEditDevice(null)
    setDeviceModal(true)
  }

  const openEditDevice = (row: DeviceItem) => {
    setEditDevice(row)
    setDeviceModal(true)
  }

  const handleDeleteDevice = async (row: DeviceItem) => {
    try {
      await deleteDevice.mutateAsync(row.id)
      message.success(`Устройство ${row.ip} удалено`)
    } catch {
      message.error('Ошибка при удалении устройства')
    }
  }

  const handleDeleteAllDevices = async () => {
    try {
      await deleteAllDevices.mutateAsync()
      message.success('Все устройства удалены')
    } catch {
      message.error('Ошибка при удалении устройств')
    }
  }

  const handleDeleteObject = () => {
    Modal.confirm({
      title: `Удалить объект «${obj?.name}»?`,
      content: `Будут удалены все устройства (${devices?.length ?? 0} шт.) и сам объект. Это действие необратимо.`,
      okText: 'Удалить',
      cancelText: 'Отмена',
      okButtonProps: { danger: true },
      onOk: async () => {
        try {
          await forceDeleteObject.mutateAsync(objId)
          message.success('Объект удалён')
          navigate('/inventory/objects')
        } catch {
          message.error('Ошибка при удалении объекта')
        }
      },
    })
  }

  const handleCsvPreview = async () => {
    if (!csvFile) return
    try {
      const preview = await previewCSV.mutateAsync(csvFile)
      setCsvPreview(preview)
    } catch {
      message.error('Не удалось разобрать CSV')
    }
  }

  const handleCsvImport = async () => {
    if (!csvFile) return
    try {
      const result = await importCSV.mutateAsync(csvFile)
      message.success(`Импортировано: +${result.created} создано, ${result.updated} обновлено`)
      setCsvModal(false)
      setCsvFile(null)
      setCsvPreview(null)
    } catch {
      message.error('Ошибка при импорте CSV')
    }
  }

  const closeCsvModal = () => {
    setCsvModal(false)
    setCsvFile(null)
    setCsvPreview(null)
  }

  const handleAddZone = async () => {
    const values = await zoneForm.validateFields()
    try {
      await createZone.mutateAsync(stripEmpty(values) as any)
      message.success('Зона добавлена')
      zoneForm.resetFields()
    } catch {
      message.error('Ошибка при добавлении зоны')
    }
  }

  const handleAddRack = async () => {
    const values = await rackForm.validateFields()
    try {
      await createRack.mutateAsync(stripEmpty(values) as any)
      message.success('Стойка добавлена')
      rackForm.resetFields()
    } catch {
      message.error('Ошибка при добавлении стойки')
    }
  }

  const openEditRack = (row: RackItem) => {
    editRackForm.setFieldsValue({
      rack_type: row.rack_type ?? '',
      location: row.location ?? '',
    })
    setEditRack(row)
  }

  const handleSaveRack = async () => {
    if (!editRack) return
    const values = editRackForm.getFieldsValue()
    try {
      await updateRack.mutateAsync({ id: editRack.id, body: stripEmpty(values) as any })
      message.success('Стойка обновлена')
      setEditRack(null)
    } catch {
      message.error('Ошибка при обновлении стойки')
    }
  }

  const filteredDevices = useMemo(() => {
    if (!searchText) return devices ?? []
    const q = searchText.toLowerCase()
    return (devices ?? []).filter(
      (d) => d.ip.toLowerCase().includes(q) || (d.hostname ?? '').toLowerCase().includes(q)
    )
  }, [devices, searchText])

  const groupedTableData = useMemo((): TableRow[] => {
    const servers = filteredDevices.filter((d) => SERVER_CATEGORIES.has(d.category))
    const rest = filteredDevices.filter((d) => !SERVER_CATEGORIES.has(d.category))

    const rackMap = new Map<string, DeviceItem[]>()
    const noRack: DeviceItem[] = []
    for (const d of rest) {
      if (d.rack_name) {
        if (!rackMap.has(d.rack_name)) rackMap.set(d.rack_name, [])
        rackMap.get(d.rack_name)!.push(d)
      } else {
        noRack.push(d)
      }
    }

    const rows: TableRow[] = []
    const addGroup = (label: string, key: string, items: DeviceItem[]) => {
      rows.push({
        _type: 'group',
        _key: key,
        label,
        count: items.length,
        onlineCount: items.filter((d) => d.status === 'online').length,
      })
      items.forEach((d) => rows.push({ ...d, _type: 'device' }))
    }

    if (servers.length > 0) addGroup('Серверная инфраструктура', '__servers__', servers)

    const sortedRacks = [...rackMap.entries()].sort(([a], [b]) => a.localeCompare(b))
    for (const [rackName, items] of sortedRacks) {
      const label = rackLabel(rackName, items[0]?.rack_type)
      addGroup(label, `rack:${rackName}`, items)
    }

    if (noRack.length > 0) addGroup('Без привязки', '__norack__', noRack)

    return rows
  }, [filteredDevices])

  if (objLoading) return <Spin />

  const deviceColumns = [
    {
      title: 'IP',
      dataIndex: 'ip',
      key: 'ip',
      onCell: (row: TableRow) =>
        row._type === 'group'
          ? { colSpan: DEVICE_COL_SPAN, style: { background: '#f5f5f5', padding: '6px 16px', borderBottom: '1px solid #e8e8e8' } }
          : {},
      render: (_: unknown, row: TableRow) => {
        if (row._type === 'group') {
          return (
            <Space size={8}>
              <Typography.Text strong style={{ fontSize: 13 }}>{row.label}</Typography.Text>
              <Tag style={{ marginLeft: 2 }}>{row.count} устройств</Tag>
              {row.onlineCount > 0 && <Tag color="green">Online: {row.onlineCount}</Tag>}
            </Space>
          )
        }
        const d = row as DeviceRow
        const webUrl = getDeviceWebUrl(d)
        return webUrl ? (
          <Tooltip title={`Открыть ${webUrl}`}>
            <a href={webUrl} target="_blank" rel="noopener noreferrer">
              <code>{d.ip}</code>
            </a>
          </Tooltip>
        ) : (
          <code style={{ color: 'inherit' }}>{d.ip}</code>
        )
      },
    },
    {
      title: 'Hostname',
      dataIndex: 'hostname',
      key: 'hostname',
      onCell: (row: TableRow) => row._type === 'group' ? { colSpan: 0 } : {},
      render: (_: unknown, row: TableRow) => {
        if (row._type === 'group') return null
        const d = row as DeviceRow
        if (!d.hostname) return <Typography.Text type="secondary">—</Typography.Text>
        return (
          <a onClick={() => navigate(`/inventory/objects/${objId}/devices/${d.id}`)}>
            {d.hostname}
          </a>
        )
      },
    },
    {
      title: 'Стойка / локация',
      key: 'location',
      onCell: (row: TableRow) => row._type === 'group' ? { colSpan: 0 } : {},
      render: (_: unknown, row: TableRow) => {
        if (row._type === 'group') return null
        const d = row as DeviceRow
        if (d.rack_name) return <Tag color="blue">{rackLabel(d.rack_name, d.rack_type)}</Tag>
        if (d.location === 'server_room') return <Tag color="purple">Серверная</Tag>
        if (d.location === 'street') return <Tag color="orange">Улица</Tag>
        return <Typography.Text type="secondary">—</Typography.Text>
      },
    },
    {
      title: 'Категория',
      dataIndex: 'category',
      key: 'category',
      onCell: (row: TableRow) => row._type === 'group' ? { colSpan: 0 } : {},
      render: (_: unknown, row: TableRow) =>
        row._type === 'group' ? null : <CategoryTag category={(row as DeviceRow).category} />,
    },
    {
      title: 'Статус',
      dataIndex: 'status',
      key: 'status',
      onCell: (row: TableRow) => row._type === 'group' ? { colSpan: 0 } : {},
      render: (_: unknown, row: TableRow) =>
        row._type === 'group' ? null : <DeviceStatusBadge status={(row as DeviceRow).status} />,
    },
    {
      title: 'Последний ping',
      dataIndex: 'last_seen',
      key: 'last_seen',
      onCell: (row: TableRow) => row._type === 'group' ? { colSpan: 0 } : {},
      render: (_: unknown, row: TableRow) => {
        if (row._type === 'group') return null
        const d = row as DeviceRow
        return d.last_seen ? (
          <Tooltip title={dayjs(d.last_seen).format('DD.MM.YYYY HH:mm:ss')}>
            <span>{d.last_ping_ms}ms</span>
          </Tooltip>
        ) : '—'
      },
    },
    {
      title: 'Источник',
      dataIndex: 'source',
      key: 'source',
      onCell: (row: TableRow) => row._type === 'group' ? { colSpan: 0 } : {},
      render: (_: unknown, row: TableRow) =>
        row._type === 'group' ? null : (
          <Typography.Text type="secondary" style={{ fontSize: 12 }}>
            {(row as DeviceRow).source}
          </Typography.Text>
        ),
    },
    {
      title: '',
      key: 'actions',
      width: 100,
      onCell: (row: TableRow) => row._type === 'group' ? { colSpan: 0 } : {},
      render: (_: unknown, row: TableRow) => {
        if (row._type === 'group') return null
        const d = row as DeviceRow
        return (
          <Space>
            <Tooltip title="Карточка устройства">
              <Button size="small" icon={<EyeOutlined />} onClick={() => navigate(`/inventory/objects/${objId}/devices/${d.id}`)} />
            </Tooltip>
            <Button size="small" icon={<EditOutlined />} onClick={() => openEditDevice(d)} />
            <Popconfirm
              title="Удалить устройство?"
              okText="Удалить"
              cancelText="Отмена"
              okButtonProps={{ danger: true }}
              onConfirm={() => handleDeleteDevice(d)}
            >
              <Button size="small" danger icon={<DeleteOutlined />} loading={deleteDevice.isPending} />
            </Popconfirm>
          </Space>
        )
      },
    },
  ]

  const zoneColumns = [
    { title: 'Название', dataIndex: 'name', key: 'name' },
    { title: 'Описание', dataIndex: 'description', key: 'description', render: (v: string | null) => v ?? '—' },
    {
      title: '',
      key: 'actions',
      width: 60,
      render: (_: unknown, row: ZoneItem) => (
        <Popconfirm
          title="Удалить зону?"
          description="Стойки останутся, привязка к зоне будет снята."
          okText="Удалить"
          cancelText="Отмена"
          okButtonProps={{ danger: true }}
          onConfirm={() => deleteZone.mutateAsync(row.id).catch(() => message.error('Ошибка удаления зоны'))}
        >
          <Button size="small" danger icon={<DeleteOutlined />} />
        </Popconfirm>
      ),
    },
  ]

  const rackColumns = [
    {
      title: 'Название',
      key: 'name',
      render: (_: unknown, row: RackItem) => {
        if (editRack?.id === row.id) {
          return (
            <Space>
              <span>{row.name}</span>
              <Form form={editRackForm} layout="inline" size="small">
                <Form.Item name="rack_type" style={{ marginBottom: 0 }}>
                  <Input placeholder="Тип (entry, exit...)" style={{ width: 130 }} />
                </Form.Item>
                <Form.Item name="location" style={{ marginBottom: 0 }}>
                  <Input placeholder="Расположение" style={{ width: 120 }} />
                </Form.Item>
              </Form>
              <Button size="small" type="primary" onClick={handleSaveRack} loading={updateRack.isPending}>Сохранить</Button>
              <Button size="small" onClick={() => setEditRack(null)}>Отмена</Button>
            </Space>
          )
        }
        return (
          <span>
            {row.name}
            {row.rack_type ? <Tag style={{ marginLeft: 6 }}>{row.rack_type}</Tag> : null}
          </span>
        )
      },
    },
    { title: 'Зона', dataIndex: 'zone_name', key: 'zone_name', render: (v: string | null) => v ? <Tag>{v}</Tag> : '—' },
    { title: 'Расположение', dataIndex: 'location', key: 'location', render: (v: string | null) => v ?? '—' },
    {
      title: '',
      key: 'actions',
      width: 90,
      render: (_: unknown, row: RackItem) => (
        <Space>
          <Button size="small" icon={<EditOutlined />} onClick={() => openEditRack(row)} />
          <Popconfirm
            title="Удалить стойку?"
            description="Устройства останутся, привязка к стойке будет снята."
            okText="Удалить"
            cancelText="Отмена"
            okButtonProps={{ danger: true }}
            onConfirm={() => deleteRack.mutateAsync(row.id).catch(() => message.error('Ошибка удаления стойки'))}
          >
            <Button size="small" danger icon={<DeleteOutlined />} />
          </Popconfirm>
        </Space>
      ),
    },
  ]

  const previewCreateCols = [
    { title: 'IP', dataIndex: 'ip', key: 'ip', render: (v: string) => <code>{v}</code> },
    { title: 'Hostname', dataIndex: 'hostname', key: 'hostname', render: (v: string) => v ?? '—' },
    { title: 'Категория', dataIndex: 'category', key: 'category' },
    { title: 'Роль', dataIndex: 'role', key: 'role' },
  ]
  const previewUpdateCols = [
    { title: 'IP', dataIndex: 'ip', key: 'ip', render: (v: string) => <code>{v}</code> },
    {
      title: 'Изменения',
      dataIndex: 'changes',
      key: 'changes',
      render: (changes: Record<string, { from: unknown; to: unknown }>) =>
        Object.entries(changes).map(([k, v]) => `${k}: ${v.from} → ${v.to}`).join(', ') || '—',
    },
  ]

  // Breadcrumb items: Инвентарь → [City →] Object
  const breadcrumbItems = [
    { title: <a onClick={() => navigate('/inventory/objects')}>Инвентарь</a> },
    ...(obj?.city ? [{ title: <a onClick={() => navigate('/inventory/objects')}>{obj.city}</a> }] : []),
    { title: obj?.name },
  ]

  return (
    <div>
      <Row justify="space-between" align="middle" style={{ marginBottom: 16 }}>
        <Col><Breadcrumb items={breadcrumbItems} /></Col>
        <Col>
          {/* TODO (Вариант В): заменить Segmented на <Tabs> с вкладками Устройства / Задачи / Снапшоты */}
          <Segmented
            value="inventory"
            options={[
              { label: 'Инвентарь', value: 'inventory', icon: <DatabaseOutlined /> },
              { label: 'Работа',    value: 'work',      icon: <ControlOutlined /> },
            ]}
            onChange={(val) => { if (val === 'work') navigate(`/work/objects/${objId}`) }}
          />
        </Col>
      </Row>

      <Row gutter={16} style={{ marginBottom: 16 }}>
        <Col flex="auto">
          <Descriptions size="small" bordered column={4}>
            <Descriptions.Item label="Сервер">{obj?.server_ip ?? '—'}</Descriptions.Item>
            <Descriptions.Item label="SSH">{obj?.ssh_user ?? '—'}</Descriptions.Item>
            <Descriptions.Item label="БД">{obj?.db_host ?? '—'}</Descriptions.Item>
            <Descriptions.Item label="Клиент">{obj?.client ?? '—'}</Descriptions.Item>
          </Descriptions>
        </Col>
        <Col>
          <Space wrap>
            <Button icon={<ApiOutlined />} loading={discoverMutation.isPending} onClick={handleDiscover}>
              SSH Discovery
            </Button>
            {/*
            <Button icon={<SyncOutlined />} loading={syncMutation.isPending} onClick={handleSync}>
              Синхронизировать из БД
            </Button>
            <Button icon={<UploadOutlined />} onClick={() => setCsvModal(true)}>
              Импорт CSV
            </Button>
            <Button icon={<GoogleOutlined />} onClick={() => setSheetsModal(true)}>
              Google Sheets
            </Button>
            */}
            <Button icon={<PlusOutlined />} type="primary" onClick={openAddDevice}>
              Добавить устройство
            </Button>
            <Button icon={<FileTextOutlined />} onClick={() => navigate(`/inventory/objects/${objId}/snapshots`)}>
              Снапшоты
            </Button>
            <Button icon={<ThunderboltOutlined />} onClick={() => setRackModal(true)}>
              Стойки и зоны
            </Button>
            <Button danger icon={<DeleteOutlined />} onClick={handleDeleteObject} loading={forceDeleteObject.isPending}>
              Удалить объект
            </Button>
          </Space>
        </Col>
      </Row>

      <Row justify="space-between" align="middle" style={{ marginBottom: 12 }}>
        <Col>
          <Input.Search
            placeholder="Поиск по IP или hostname..."
            allowClear
            style={{ width: 360 }}
            onChange={(e) => setSearchText(e.target.value)}
          />
        </Col>
        <Col>
          <Popconfirm
            title="Удалить все устройства?"
            description={`Будет удалено ${devices?.length ?? 0} устройств. Отменить нельзя.`}
            okText="Удалить все"
            cancelText="Отмена"
            okButtonProps={{ danger: true }}
            onConfirm={handleDeleteAllDevices}
            disabled={!devices?.length}
          >
            <Button
              danger
              icon={<DeleteOutlined />}
              loading={deleteAllDevices.isPending}
              disabled={!devices?.length}
            >
              Удалить все устройства
            </Button>
          </Popconfirm>
        </Col>
      </Row>

      <Table
        dataSource={groupedTableData}
        columns={deviceColumns}
        rowKey={(row) => row._type === 'group' ? row._key : String((row as DeviceRow).id)}
        loading={devLoading}
        size="middle"
        pagination={{ pageSize: 50 }}
      />

      {/* Rack & Zone Management Modal */}
      <Modal
        title="Стойки и зоны"
        open={rackModal}
        onCancel={() => { setRackModal(false); rackForm.resetFields(); zoneForm.resetFields(); setEditRack(null) }}
        footer={null}
        width={820}
      >
        <Typography.Title level={5} style={{ marginTop: 0 }}>Зоны</Typography.Title>
        <Form form={zoneForm} layout="inline" style={{ marginBottom: 12 }}>
          <Form.Item name="name" rules={[{ required: true, message: 'Введите название' }]}>
            <Input placeholder="Название зоны" style={{ width: 200 }} />
          </Form.Item>
          <Form.Item name="description">
            <Input placeholder="Описание (опц.)" style={{ width: 180 }} />
          </Form.Item>
          <Form.Item>
            <Button type="primary" icon={<PlusOutlined />} onClick={handleAddZone} loading={createZone.isPending}>
              Добавить зону
            </Button>
          </Form.Item>
        </Form>
        <Table dataSource={zones ?? []} columns={zoneColumns} rowKey="id" size="small" pagination={false} locale={{ emptyText: 'Нет зон' }} style={{ marginBottom: 24 }} />

        <Divider />

        <Typography.Title level={5}>Стойки</Typography.Title>
        <Form form={rackForm} layout="inline" style={{ marginBottom: 12 }}>
          <Form.Item name="name" rules={[{ required: true, message: 'Введите название' }]}>
            <Input placeholder="Название стойки" style={{ width: 160 }} />
          </Form.Item>
          <Form.Item name="rack_type">
            <Input placeholder="Тип (entry, exit...)" style={{ width: 140 }} />
          </Form.Item>
          <Form.Item name="zone_id">
            <Select placeholder="Зона (опц.)" allowClear style={{ width: 130 }} options={(zones ?? []).map((z) => ({ value: z.id, label: z.name }))} />
          </Form.Item>
          <Form.Item name="location">
            <Input placeholder="Расположение (опц.)" style={{ width: 150 }} />
          </Form.Item>
          <Form.Item>
            <Button type="primary" icon={<PlusOutlined />} onClick={handleAddRack} loading={createRack.isPending}>
              Добавить стойку
            </Button>
          </Form.Item>
        </Form>
        <Table dataSource={racks ?? []} columns={rackColumns} rowKey="id" size="small" pagination={false} locale={{ emptyText: 'Нет стоек' }} />
      </Modal>

      {/* CSV Import Modal */}
      <Modal
        title="Импорт устройств из CSV"
        open={csvModal}
        onCancel={closeCsvModal}
        width={700}
        footer={
          <Space>
            <Button onClick={closeCsvModal}>Отмена</Button>
            {!csvPreview && <Button onClick={handleCsvPreview} loading={previewCSV.isPending} disabled={!csvFile}>Предпросмотр</Button>}
            {csvPreview && <Button type="primary" onClick={handleCsvImport} loading={importCSV.isPending}>Импортировать</Button>}
          </Space>
        }
      >
        {!csvPreview ? (
          <>
            <Typography.Paragraph type="secondary" style={{ marginBottom: 12 }}>
              Формат CSV: <code>ip, hostname, category, role, model, mac, notes</code>
            </Typography.Paragraph>
            <Upload.Dragger
              accept=".csv"
              maxCount={1}
              beforeUpload={(file) => { setCsvFile(file); return false }}
              onRemove={() => setCsvFile(null)}
              fileList={csvFile ? [{ uid: '1', name: csvFile.name, status: 'done' } as UploadFile] : []}
            >
              <p className="ant-upload-drag-icon"><UploadOutlined style={{ fontSize: 32, color: '#1677ff' }} /></p>
              <p className="ant-upload-text">Перетащите CSV-файл или нажмите для выбора</p>
            </Upload.Dragger>
          </>
        ) : (
          <>
            {csvPreview.errors.length > 0 && (
              <Alert type="warning" style={{ marginBottom: 12 }} message={`${csvPreview.errors.length} строк с ошибками`} description={csvPreview.errors.map((e) => `Строка ${e.row}: ${e.error}`).join('\n')} />
            )}
            {csvPreview.would_create.length > 0 && (
              <>
                <Typography.Text strong>Будет создано: {csvPreview.would_create.length}</Typography.Text>
                <Table size="small" dataSource={csvPreview.would_create} columns={previewCreateCols} rowKey="ip" pagination={false} style={{ marginTop: 8, marginBottom: 16 }} />
              </>
            )}
            {csvPreview.would_update.length > 0 && (
              <>
                <Typography.Text strong>Будет обновлено: {csvPreview.would_update.length}</Typography.Text>
                <Table size="small" dataSource={csvPreview.would_update} columns={previewUpdateCols} rowKey="ip" pagination={false} style={{ marginTop: 8 }} />
              </>
            )}
          </>
        )}
      </Modal>

      <DeviceEditModal
        open={deviceModal}
        device={editDevice}
        objId={objId}
        onClose={() => { setDeviceModal(false); setEditDevice(null) }}
      />

      <DiscoveryDrawer
        open={discoveryOpen}
        onClose={() => setDiscoveryOpen(false)}
        objectName={obj?.name ?? ''}
        taskId={discoveryTaskId}
        onComplete={handleDiscoveryComplete}
      />

      {/* Google Sheets Sync Modal */}
      <Modal
        title="Синхронизация из Google Sheets"
        open={sheetsModal}
        onCancel={() => { setSheetsModal(false); sheetsForm.resetFields() }}
        onOk={handleSyncSheets}
        confirmLoading={syncSheetsMutation.isPending}
        okText="Синхронизировать"
      >
        <Form form={sheetsForm} layout="vertical" style={{ marginTop: 16 }}>
          <Form.Item
            name="spreadsheet_id"
            label="ID таблицы Google Sheets"
            rules={[{ required: true, message: 'Введите ID таблицы' }]}
            extra="Часть URL после /spreadsheets/d/ и перед /edit"
          >
            <Input placeholder="1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgVE2upms" />
          </Form.Item>
          <Form.Item name="sheet_name" label="Название листа (необязательно)" extra="По умолчанию используется первый лист">
            <Input placeholder="Sheet1" />
          </Form.Item>
        </Form>
        <Typography.Text type="secondary" style={{ fontSize: 12 }}>
          Ожидаемые столбцы: ip, hostname, category, role, model, mac, notes
        </Typography.Text>
      </Modal>
    </div>
  )
}
