/**
 * EnergyPage — device management UI for the Energy (Modbus) domain.
 *
 * Features (T06 + T07):
 * - Device list with status indicators (enabled, last poll).
 * - Create / edit via DeviceForm modal.
 * - Delete with二次确认; 409 (has readings) → friendly prompt to disable instead.
 * - "Test read" button per device — calls POST /devices/{uuid}/test, shows
 *   decoded payload or error without persisting to the database (T06 OBS-1: error
 *   now caught and displayed rather than producing an unhandled rejection).
 * - Latest readings card per device (T07): fetches /latest and /metrics; tolerates
 *   missing payload keys.
 * - Trend charts per device (T07): EnergyCharts component (Recharts isolated inside).
 *
 * M6-T10: added Tabs layout with Devices / Contracts / Prices / Costs tabs.
 */

import { useState } from 'react'
import {
  Container,
  Title,
  Table,
  Button,
  Group,
  Text,
  Loader,
  Center,
  Alert,
  Stack,
  Badge,
  ScrollArea,
  Modal,
  Code,
  Tooltip,
  Paper,
  SimpleGrid,
  Divider,
  Switch,
  Tabs,
} from '@mantine/core'
import { useDevices, useDeleteDevice, useTestReadDevice, useLatestReading, useMetrics } from '../energy/hooks'
import { DeviceForm } from '../energy/DeviceForm'
import { EnergyCharts } from '../energy/EnergyCharts'
import { ContractManager } from '../energy/ContractManager'
import { MeterManager } from '../energy/MeterManager'
import { TibberPrices } from '../energy/TibberPrices'
import { CostView } from '../energy/CostView'
import { DsmrPanel } from '../energy/DsmrPanel'
import type { ModbusDevice, ModbusTestReadResponse, MetricInfo } from '../energy/hooks'
import { ApiError } from '../api/client'
import { formatMetricValue } from '../energy/format'
import { formatLocalDateTime } from '../utils/datetime'

// ---------------------------------------------------------------------------
// Delete confirmation modal (with 409 "disable instead" hint)
// ---------------------------------------------------------------------------

interface ConfirmDeleteProps {
  device: ModbusDevice
  onConfirm: () => void
  /** Called when the user explicitly opts into cascade (force) deletion. */
  onForceDelete: () => void
  onCancel: () => void
  loading: boolean
  /** Set when the delete failed with 409. */
  has409Error: boolean
}

function ConfirmDeleteModal({
  device,
  onConfirm,
  onForceDelete,
  onCancel,
  loading,
  has409Error,
}: ConfirmDeleteProps) {
  return (
    <Modal
      opened
      onClose={onCancel}
      title="Confirm Delete"
      size="sm"
      data-testid="device-delete-modal"
    >
      <Stack gap="md">
        <Text data-testid="device-delete-message">
          Delete device <strong>{device.friendly_name}</strong>?
        </Text>

        {has409Error && (
          <>
            <Alert color="orange" data-testid="device-delete-409-hint">
              This device has existing readings and cannot be deleted. Consider{' '}
              <strong>disabling</strong> it instead (click Edit → uncheck Enabled).
            </Alert>
            <Alert color="red" variant="light" data-testid="device-delete-force-warning">
              Alternatively, you can <strong>permanently delete</strong> the device together with{' '}
              all its historical readings and expose settings. This action{' '}
              <strong>cannot be undone</strong>.
            </Alert>
          </>
        )}

        <Group justify="flex-end" gap="sm">
          <Button variant="default" onClick={onCancel} data-testid="device-delete-cancel">
            Cancel
          </Button>
          {!has409Error && (
            <Button
              color="red"
              loading={loading}
              onClick={onConfirm}
              data-testid="device-delete-confirm"
            >
              Delete
            </Button>
          )}
          {has409Error && (
            <Button
              color="red"
              loading={loading}
              onClick={onForceDelete}
              data-testid="device-delete-force"
            >
              Force Delete (all data)
            </Button>
          )}
        </Group>
      </Stack>
    </Modal>
  )
}

// ---------------------------------------------------------------------------
// Test-read result modal
// ---------------------------------------------------------------------------

interface TestResultModalProps {
  result: ModbusTestReadResponse
  deviceName: string
  metrics?: MetricInfo[]
  onClose: () => void
}

function TestResultModal({ result, deviceName, metrics, onClose }: TestResultModalProps) {
  return (
    <Modal
      opened
      onClose={onClose}
      title={`Test read — ${deviceName}`}
      size="md"
      data-testid="test-read-modal"
    >
      {result.ok ? (
        <Stack gap="sm">
          <Badge color="green" data-testid="test-read-ok">Success</Badge>
          {result.payload && typeof result.payload === 'object' ? (
            <SimpleGrid cols={2} spacing="xs" data-testid="test-read-payload">
              {Object.entries(result.payload as Record<string, unknown>).map(([key, val]) => {
                const metricMeta = metrics?.find((m) => m.key === key)
                return (
                  <Group key={key} justify="space-between" align="baseline" gap={4}
                    data-testid={`test-read-value-${key}`}>
                    <Text size="xs" c="dimmed" style={{ flexShrink: 0 }}>
                      {metricMeta?.label ?? key}
                    </Text>
                    <Text size="sm" fw={500}>
                      {formatMetricValue(val, metricMeta)}
                      {metricMeta?.unit ? (
                        <Text component="span" size="xs" c="dimmed" ml={2}>
                          {metricMeta.unit}
                        </Text>
                      ) : null}
                    </Text>
                  </Group>
                )
              })}
            </SimpleGrid>
          ) : (
            <Code block data-testid="test-read-payload">
              {JSON.stringify(result.payload, null, 2)}
            </Code>
          )}
        </Stack>
      ) : (
        <Stack gap="sm">
          <Badge color="red" data-testid="test-read-error-badge">Failed</Badge>
          <Text c="red" data-testid="test-read-error">
            {result.error ?? 'Unknown error'}
          </Text>
        </Stack>
      )}
      <Group justify="flex-end" mt="md">
        <Button variant="default" onClick={onClose}>
          Close
        </Button>
      </Group>
    </Modal>
  )
}

// ---------------------------------------------------------------------------
// Device row action: test-read button (self-contained per row)
// T06 OBS-1 fix: catch errors from mutateAsync so they don't produce unhandled
// promise rejections (e.g. 422 when profile cannot load).
// ---------------------------------------------------------------------------

interface TestReadButtonProps {
  device: ModbusDevice
}

function TestReadButton({ device }: TestReadButtonProps) {
  const testMutation = useTestReadDevice()
  const metricsQuery = useMetrics(device.uuid)
  const [result, setResult] = useState<ModbusTestReadResponse | null>(null)
  const [testError, setTestError] = useState<string | null>(null)

  async function handleTest() {
    setResult(null)
    setTestError(null)
    try {
      const res = await testMutation.mutateAsync(device.uuid)
      if (res.data) {
        setResult(res.data)
      }
    } catch (err) {
      // Network or API errors (e.g. 422 profile load failure) — surface inline.
      const message =
        err instanceof ApiError
          ? `Error ${err.status}: ${JSON.stringify(err.body?.detail ?? err.body ?? 'unknown error')}`
          : err instanceof Error
            ? err.message
            : 'Unexpected error during test read.'
      setTestError(message)
    }
  }

  return (
    <>
      <Tooltip label="Immediately read device (not saved)" position="top">
        <Button
          size="xs"
          variant="outline"
          color="blue"
          loading={testMutation.isPending}
          onClick={handleTest}
          data-testid={`device-test-${device.uuid}`}
        >
          Test read
        </Button>
      </Tooltip>

      {/* Show inline test error as a small alert (no modal needed for error path) */}
      {testError && (
        <Modal
          opened
          onClose={() => setTestError(null)}
          title={`Test read error — ${device.friendly_name}`}
          size="sm"
          data-testid="test-read-error-modal"
        >
          <Text c="red" data-testid="test-read-inline-error">
            {testError}
          </Text>
          <Group justify="flex-end" mt="md">
            <Button variant="default" onClick={() => setTestError(null)}>
              Close
            </Button>
          </Group>
        </Modal>
      )}

      {result && (
        <TestResultModal
          result={result}
          deviceName={device.friendly_name}
          metrics={metricsQuery.data?.metrics}
          onClose={() => setResult(null)}
        />
      )}
    </>
  )
}

// ---------------------------------------------------------------------------
// Latest readings card (T07)
// Fetches /latest and /metrics; tolerates missing payload keys.
// ---------------------------------------------------------------------------

interface LatestReadingsCardProps {
  device: ModbusDevice
  autoRefresh?: { refetchIntervalMs: number }
}

function LatestReadingsCard({ device, autoRefresh }: LatestReadingsCardProps) {
  const latestQuery = useLatestReading(device.uuid, autoRefresh)
  const metricsQuery = useMetrics(device.uuid)

  const metrics: MetricInfo[] = metricsQuery.data?.metrics ?? []
  const latest = latestQuery.data

  if (latestQuery.isLoading || metricsQuery.isLoading) {
    return (
      <Paper withBorder p="sm" data-testid={`latest-card-loading-${device.uuid}`}>
        <Group gap="xs">
          <Loader size="xs" />
          <Text size="sm" c="dimmed">
            Loading latest reading…
          </Text>
        </Group>
      </Paper>
    )
  }

  if (latestQuery.isError || metricsQuery.isError) {
    return (
      <Alert color="red" data-testid={`latest-card-error-${device.uuid}`}>
        Failed to load latest reading.
      </Alert>
    )
  }

  if (!latest?.found || !latest.payload) {
    return (
      <Paper withBorder p="sm" data-testid={`latest-card-empty-${device.uuid}`}>
        <Text size="sm" c="dimmed">
          No readings yet.
        </Text>
      </Paper>
    )
  }

  const payload = latest.payload as Record<string, unknown>

  return (
    <Paper withBorder p="sm" data-testid={`latest-card-${device.uuid}`}>
      <Stack gap={4}>
        <Group justify="space-between" align="center">
          <Text size="xs" fw={600} c="dimmed">
            Latest reading
          </Text>
          {latest.recorded_at && (
            <Text size="xs" c="dimmed">
              {formatLocalDateTime(latest.recorded_at)}
            </Text>
          )}
        </Group>
        <Divider />
        <SimpleGrid cols={2} spacing="xs">
          {metrics.map((m) => {
            const raw = payload[m.key]
            const displayValue = formatMetricValue(raw, m)
            const hasValue = displayValue !== '—'

            return (
              <Group key={m.key} justify="space-between" align="baseline" gap={4}
                data-testid={`latest-value-${device.uuid}-${m.key}`}>
                <Text size="xs" c="dimmed" style={{ flexShrink: 0 }}>
                  {m.label}
                </Text>
                <Text size="sm" fw={500}>
                  {displayValue}
                  {hasValue && m.unit ? (
                    <Text component="span" size="xs" c="dimmed" ml={2}>
                      {m.unit}
                    </Text>
                  ) : null}
                </Text>
              </Group>
            )
          })}
        </SimpleGrid>
      </Stack>
    </Paper>
  )
}

// ---------------------------------------------------------------------------
// Device list table
// ---------------------------------------------------------------------------

interface DeviceTableProps {
  devices: ModbusDevice[]
  onEdit: (device: ModbusDevice) => void
  onDelete: (device: ModbusDevice) => void
}

function DeviceTable({ devices, onEdit, onDelete }: DeviceTableProps) {
  if (devices.length === 0) {
    return (
      <Text c="dimmed" ta="center" size="sm" data-testid="devices-empty">
        No devices configured yet. Click "New Device" to add one.
      </Text>
    )
  }

  return (
    <ScrollArea>
      <Table striped highlightOnHover withTableBorder data-testid="devices-table">
        <Table.Thead>
          <Table.Tr>
            <Table.Th>Name</Table.Th>
            <Table.Th>Host</Table.Th>
            <Table.Th>Port</Table.Th>
            <Table.Th>Unit ID</Table.Th>
            <Table.Th>Profile</Table.Th>
            <Table.Th>Poll (s)</Table.Th>
            <Table.Th>Status</Table.Th>
            <Table.Th style={{ textAlign: 'right' }}>Actions</Table.Th>
          </Table.Tr>
        </Table.Thead>
        <Table.Tbody>
          {devices.map((device) => (
            <Table.Tr key={device.uuid} data-testid={`device-row-${device.uuid}`}>
              <Table.Td>
                <Text fw={500} size="sm">
                  {device.friendly_name}
                </Text>
              </Table.Td>
              <Table.Td>{device.host}</Table.Td>
              <Table.Td>{device.port}</Table.Td>
              <Table.Td>{device.unit_id}</Table.Td>
              <Table.Td>
                <Badge variant="outline" size="sm">
                  {device.profile}
                </Badge>
              </Table.Td>
              <Table.Td>{device.poll_interval_s}</Table.Td>
              <Table.Td>
                <Group gap="xs">
                  <Badge color={device.enabled ? 'green' : 'gray'} variant="light" size="sm">
                    {device.enabled ? 'enabled' : 'disabled'}
                  </Badge>
                  {device.last_poll_ok !== null && (
                    <Badge
                      color={device.last_poll_ok ? 'teal' : 'red'}
                      variant="dot"
                      size="sm"
                    >
                      {device.last_poll_ok ? 'online' : 'offline'}
                    </Badge>
                  )}
                </Group>
              </Table.Td>
              <Table.Td>
                <Group justify="flex-end" gap="xs">
                  <TestReadButton device={device} />
                  <Button
                    size="xs"
                    variant="outline"
                    onClick={() => onEdit(device)}
                    data-testid={`device-edit-${device.uuid}`}
                  >
                    Edit
                  </Button>
                  <Button
                    size="xs"
                    variant="outline"
                    color="red"
                    onClick={() => onDelete(device)}
                    data-testid={`device-delete-${device.uuid}`}
                  >
                    Delete
                  </Button>
                </Group>
              </Table.Td>
            </Table.Tr>
          ))}
        </Table.Tbody>
      </Table>
    </ScrollArea>
  )
}

// ---------------------------------------------------------------------------
// Device readings section — latest card + trend chart per device (T07)
// Auto-refresh switch controls TanStack Query refetchInterval for all
// readings and latest queries in this section.
// ---------------------------------------------------------------------------

/** Default auto-refresh interval in milliseconds (matches backend ~5s poll). */
const AUTO_REFRESH_INTERVAL_MS = 5_000

interface DeviceReadingsSectionProps {
  devices: ModbusDevice[]
}

function DeviceReadingsSection({ devices }: DeviceReadingsSectionProps) {
  // Default on — user wants charts to auto-update without manual refresh.
  const [autoRefreshEnabled, setAutoRefreshEnabled] = useState(true)

  if (devices.length === 0) return null

  const autoRefresh = autoRefreshEnabled
    ? { refetchIntervalMs: AUTO_REFRESH_INTERVAL_MS }
    : undefined

  return (
    <Stack gap="xl" data-testid="device-readings-section">
      <Group justify="space-between" align="center">
        <Divider label="Readings & Trends" labelPosition="left" style={{ flex: 1 }} />
        <Switch
          label="Auto-refresh"
          checked={autoRefreshEnabled}
          onChange={(e) => setAutoRefreshEnabled(e.currentTarget.checked)}
          size="sm"
          data-testid="auto-refresh-switch"
        />
      </Group>
      {devices.map((device) => (
        <Stack key={device.uuid} gap="sm" data-testid={`device-readings-${device.uuid}`}>
          <Text fw={500} size="sm">
            {device.friendly_name}
          </Text>
          <LatestReadingsCard device={device} autoRefresh={autoRefresh} />
          <EnergyCharts
            uuid={device.uuid}
            deviceName={device.friendly_name}
            autoRefresh={autoRefresh}
          />
        </Stack>
      ))}
    </Stack>
  )
}

// ---------------------------------------------------------------------------
// DevicesTab — self-contained devices management tab (extracted from original EnergyPage)
// ---------------------------------------------------------------------------

function DevicesTab() {
  const devicesQuery = useDevices()
  const deleteMutation = useDeleteDevice()

  const [editDevice, setEditDevice] = useState<ModbusDevice | null>(null)
  const [showCreateForm, setShowCreateForm] = useState(false)
  const [deleteDevice, setDeleteDevice] = useState<ModbusDevice | null>(null)
  const [delete409, setDelete409] = useState(false)

  function openCreate() {
    setEditDevice(null)
    setShowCreateForm(true)
  }

  function openEdit(device: ModbusDevice) {
    setShowCreateForm(false)
    setEditDevice(device)
  }

  function openDelete(device: ModbusDevice) {
    setDeleteDevice(device)
    setDelete409(false)
  }

  function closeDelete() {
    setDeleteDevice(null)
    setDelete409(false)
  }

  async function handleDeleteConfirm() {
    if (!deleteDevice) return
    setDelete409(false)
    try {
      await deleteMutation.mutateAsync({ uuid: deleteDevice.uuid })
      closeDelete()
    } catch (err) {
      if (err instanceof ApiError && err.status === 409) {
        setDelete409(true)
      }
      // Leave modal open so user sees the hint.
    }
  }

  async function handleForceDelete() {
    if (!deleteDevice) return
    try {
      await deleteMutation.mutateAsync({ uuid: deleteDevice.uuid, cascade: true })
      closeDelete()
    } catch {
      // If cascade delete also fails for some reason, keep the modal open.
      // (Should be very rare — only if the device was concurrently deleted.)
    }
  }

  if (devicesQuery.isLoading) {
    return (
      <Center pt="xl" data-testid="energy-loading">
        <Loader />
      </Center>
    )
  }

  if (devicesQuery.isError || !devicesQuery.data) {
    return (
      <Alert color="red" data-testid="energy-load-error">
        Failed to load devices. Please refresh.
      </Alert>
    )
  }

  const devices = devicesQuery.data.items

  return (
    <Stack gap="lg">
      <Group justify="space-between" align="center">
        <Title order={2}>Energy — Devices</Title>
        <Button onClick={openCreate} data-testid="device-new-button">
          New Device
        </Button>
      </Group>

      <DeviceTable
        devices={devices}
        onEdit={openEdit}
        onDelete={openDelete}
      />

      {/* Latest readings cards + trend charts (T07) */}
      <DeviceReadingsSection devices={devices} />

      {/* Create form */}
      {showCreateForm && (
        <DeviceForm
          onClose={() => setShowCreateForm(false)}
          onSaved={() => setShowCreateForm(false)}
        />
      )}

      {/* Edit form */}
      {editDevice && (
        <DeviceForm
          device={editDevice}
          onClose={() => setEditDevice(null)}
          onSaved={() => setEditDevice(null)}
        />
      )}

      {/* Delete confirmation */}
      {deleteDevice && (
        <ConfirmDeleteModal
          device={deleteDevice}
          onConfirm={handleDeleteConfirm}
          onForceDelete={handleForceDelete}
          onCancel={closeDelete}
          loading={deleteMutation.isPending}
          has409Error={delete409}
        />
      )}
    </Stack>
  )
}

// ---------------------------------------------------------------------------
// EnergyPage — top-level with Tabs
// ---------------------------------------------------------------------------

export function EnergyPage() {
  return (
    <Container size="xl" pt="xl" pb="xl" data-testid="energy-page">
      <Tabs defaultValue="devices">
        <Tabs.List mb="lg">
          <Tabs.Tab value="devices" data-testid="tab-devices">
            Devices
          </Tabs.Tab>
          <Tabs.Tab value="meters" data-testid="tab-meters">
            Meters
          </Tabs.Tab>
          <Tabs.Tab value="contracts" data-testid="tab-contracts">
            Contracts
          </Tabs.Tab>
          <Tabs.Tab value="prices" data-testid="tab-prices">
            Prices
          </Tabs.Tab>
          <Tabs.Tab value="costs" data-testid="tab-costs">
            Costs
          </Tabs.Tab>
          <Tabs.Tab value="dsmr" data-testid="tab-dsmr">
            DSMR
          </Tabs.Tab>
        </Tabs.List>

        <Tabs.Panel value="devices" data-testid="panel-devices">
          <DevicesTab />
        </Tabs.Panel>

        <Tabs.Panel value="meters" data-testid="panel-meters">
          <MeterManager />
        </Tabs.Panel>

        <Tabs.Panel value="contracts" data-testid="panel-contracts">
          <ContractManager />
        </Tabs.Panel>

        <Tabs.Panel value="prices" data-testid="panel-prices">
          <TibberPrices />
        </Tabs.Panel>

        <Tabs.Panel value="costs" data-testid="panel-costs">
          <CostView />
        </Tabs.Panel>

        <Tabs.Panel value="dsmr" data-testid="panel-dsmr">
          <DsmrPanel />
        </Tabs.Panel>
      </Tabs>
    </Container>
  )
}
