/**
 * MeterManager — electricity meter timeline UI.
 *
 * Features:
 * - Table of meter epochs: label / interval (started_at → ended_at or "active") /
 *   active badge / reason.
 * - "Declare New Meter" button: form with label + date (started_at) + reason +
 *   optional note.  Sends local-midnight naive datetime per FU10 convention.
 * - Edit modal: update label, note, or correct started_at (retroactive).
 * - Retroactive feedback: if started_at is changed, a success notice mentions
 *   that affected billing periods have been recomputed.
 * - Loading / error / empty states.
 */

import { useState } from 'react'
import {
  Table,
  Button,
  Group,
  Text,
  Loader,
  Center,
  Alert,
  Stack,
  Badge,
  ScrollArea,
  Modal,
  TextInput,
  Textarea,
  Select,
  Notification,
} from '@mantine/core'
import {
  useMeters,
  useDeclareMeter,
  useUpdateMeter,
  type MeterResponse,
  type MeterReason,
} from './hooks'
import { ApiError } from '../api/client'
import { formatLocalDate, parseBackendTimestamp } from '../utils/datetime'

// ---------------------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------------------

const REASON_OPTIONS: { value: MeterReason; label: string }[] = [
  { value: 'initial', label: 'Initial installation' },
  { value: 'meter_swap', label: 'Meter swap (same address)' },
  { value: 'home_move', label: 'Home / address move' },
  { value: 'other', label: 'Other' },
]

/**
 * Convert a local date string "YYYY-MM-DD" to a naive local-midnight datetime
 * string (no Z suffix) following the FU10 / ContractForm convention.
 * The backend interprets naive datetimes as server local wall-clock time.
 */
function toLocalMidnightNaive(dateStr: string): string {
  return `${dateStr}T00:00:00`
}

/**
 * Format a Date object as a "YYYY-MM-DD" string using the browser's local timezone.
 * Used to populate <input type="date"> fields.
 * Returns '' if the Date is invalid.
 */
function toLocalDateInputString(d: Date): string {
  if (isNaN(d.getTime())) return ''
  const y = d.getFullYear()
  const m = String(d.getMonth() + 1).padStart(2, '0')
  const day = String(d.getDate()).padStart(2, '0')
  return `${y}-${m}-${day}`
}

// ---------------------------------------------------------------------------
// Declare meter form (modal)
// ---------------------------------------------------------------------------

interface DeclareMeterFormProps {
  onClose: () => void
  onSaved: () => void
}

function DeclareMeterForm({ onClose, onSaved }: DeclareMeterFormProps) {
  const [label, setLabel] = useState('')
  const [dateStr, setDateStr] = useState('')
  const [reason, setReason] = useState<string | null>(null)
  const [note, setNote] = useState('')
  const [error, setError] = useState<string | null>(null)

  const declareMutation = useDeclareMeter()

  async function handleSubmit(e: React.FormEvent) {
    e.preventDefault()
    setError(null)

    if (!label.trim()) {
      setError('Label is required.')
      return
    }
    if (!dateStr) {
      setError('Start date is required.')
      return
    }
    if (!reason) {
      setError('Reason is required.')
      return
    }

    try {
      await declareMutation.mutateAsync({
        label: label.trim(),
        started_at: toLocalMidnightNaive(dateStr),
        reason: reason as MeterReason,
        note: note.trim() || undefined,
        commodity: 'electricity',
      })
      onSaved()
      onClose()
    } catch (err) {
      if (err instanceof ApiError) {
        const detail = (err.body as { detail?: string } | null)?.detail
        setError(detail ?? `Error ${err.status}: failed to declare meter.`)
      } else {
        setError('Failed to declare meter. Please try again.')
      }
    }
  }

  return (
    <Modal
      opened
      onClose={onClose}
      title="Declare New Meter"
      size="md"
      data-testid="declare-meter-modal"
    >
      <form onSubmit={handleSubmit} data-testid="declare-meter-form">
        <Stack gap="sm">
          <TextInput
            label="Label"
            description={'Human-readable identifier, e.g. "2G meter @ Dorpsstraat 1"'}
            required
            value={label}
            onChange={(e) => setLabel(e.currentTarget.value)}
            data-testid="meter-label"
          />

          <TextInput
            label="Start date"
            description="Date in YYYY-MM-DD format (interpreted as local midnight)"
            type="date"
            required
            value={dateStr}
            onChange={(e) => setDateStr(e.currentTarget.value)}
            data-testid="meter-started-at"
          />

          <Select
            label="Reason"
            required
            data={REASON_OPTIONS}
            value={reason}
            onChange={setReason}
            data-testid="meter-reason"
          />

          <Textarea
            label="Note (optional)"
            value={note}
            onChange={(e) => setNote(e.currentTarget.value)}
            autosize
            minRows={2}
            data-testid="meter-note"
          />

          {error && (
            <Alert color="red" data-testid="declare-meter-error">
              {error}
            </Alert>
          )}

          <Group justify="flex-end" gap="sm">
            <Button
              type="button"
              variant="default"
              onClick={onClose}
              data-testid="declare-meter-cancel"
            >
              Cancel
            </Button>
            <Button
              type="submit"
              loading={declareMutation.isPending}
              data-testid="declare-meter-submit"
            >
              Declare Meter
            </Button>
          </Group>
        </Stack>
      </form>
    </Modal>
  )
}

// ---------------------------------------------------------------------------
// Edit meter form (modal)
// ---------------------------------------------------------------------------

interface EditMeterFormProps {
  meter: MeterResponse
  onClose: () => void
  onSaved: (retroactive: boolean) => void
}

function EditMeterForm({ meter, onClose, onSaved }: EditMeterFormProps) {
  const [label, setLabel] = useState(meter.label)
  const [note, setNote] = useState(meter.note ?? '')
  // Convert started_at to a local date string for the <input type="date">.
  // parseBackendTimestamp correctly handles naive strings (no tz marker → treated as UTC,
  // matching the backend's storage convention), Z-suffixed strings, and strings with
  // explicit offsets like +02:00.  We then extract the local-timezone date components
  // so the displayed date matches the local wall-clock date of the meter start.
  const initialDateStr = toLocalDateInputString(parseBackendTimestamp(meter.started_at))
  const [dateStr, setDateStr] = useState(initialDateStr)
  const [error, setError] = useState<string | null>(null)

  const updateMutation = useUpdateMeter()

  // Detect if the user changed started_at (retroactive correction).
  const startedAtChanged = dateStr !== initialDateStr

  async function handleSubmit(e: React.FormEvent) {
    e.preventDefault()
    setError(null)

    const patchBody: { label?: string | null; note?: string | null; started_at?: string | null } = {}
    if (label.trim() !== meter.label) patchBody.label = label.trim()
    const noteVal = note.trim() || null
    if (noteVal !== meter.note) patchBody.note = noteVal
    if (startedAtChanged && dateStr) {
      patchBody.started_at = toLocalMidnightNaive(dateStr)
    }

    if (Object.keys(patchBody).length === 0) {
      onClose()
      return
    }

    try {
      await updateMutation.mutateAsync({ id: meter.id, body: patchBody })
      onSaved(startedAtChanged)
      onClose()
    } catch (err) {
      if (err instanceof ApiError) {
        const detail = (err.body as { detail?: string } | null)?.detail
        setError(detail ?? `Error ${err.status}: failed to update meter.`)
      } else {
        setError('Failed to update meter. Please try again.')
      }
    }
  }

  return (
    <Modal
      opened
      onClose={onClose}
      title={`Edit Meter — ${meter.label}`}
      size="md"
      data-testid="edit-meter-modal"
    >
      <form onSubmit={handleSubmit} data-testid="edit-meter-form">
        <Stack gap="sm">
          <TextInput
            label="Label"
            required
            value={label}
            onChange={(e) => setLabel(e.currentTarget.value)}
            data-testid="edit-meter-label"
          />

          <TextInput
            label="Start date"
            description="Retroactive correction: shifts the epoch boundary and re-judges billing periods"
            type="date"
            value={dateStr}
            onChange={(e) => setDateStr(e.currentTarget.value)}
            data-testid="edit-meter-started-at"
          />

          <Textarea
            label="Note (optional)"
            value={note}
            onChange={(e) => setNote(e.currentTarget.value)}
            autosize
            minRows={2}
            data-testid="edit-meter-note"
          />

          {error && (
            <Alert color="red" data-testid="edit-meter-error">
              {error}
            </Alert>
          )}

          <Group justify="flex-end" gap="sm">
            <Button
              type="button"
              variant="default"
              onClick={onClose}
              data-testid="edit-meter-cancel"
            >
              Cancel
            </Button>
            <Button
              type="submit"
              loading={updateMutation.isPending}
              data-testid="edit-meter-submit"
            >
              Save
            </Button>
          </Group>
        </Stack>
      </form>
    </Modal>
  )
}

// ---------------------------------------------------------------------------
// Meter timeline table
// ---------------------------------------------------------------------------

interface MeterTableProps {
  meters: MeterResponse[]
  onEdit: (meter: MeterResponse) => void
}

function MeterTable({ meters, onEdit }: MeterTableProps) {
  if (meters.length === 0) {
    return (
      <Text c="dimmed" ta="center" size="sm" data-testid="meters-empty">
        No meters declared yet. Click "Declare New Meter" to add one.
      </Text>
    )
  }

  return (
    <ScrollArea>
      <Table striped highlightOnHover withTableBorder data-testid="meters-table">
        <Table.Thead>
          <Table.Tr>
            <Table.Th>Label</Table.Th>
            <Table.Th>Commodity</Table.Th>
            <Table.Th>From</Table.Th>
            <Table.Th>To</Table.Th>
            <Table.Th>Status</Table.Th>
            <Table.Th>Reason</Table.Th>
            <Table.Th style={{ textAlign: 'right' }}>Actions</Table.Th>
          </Table.Tr>
        </Table.Thead>
        <Table.Tbody>
          {meters.map((meter) => {
            const isActive = meter.ended_at === null
            return (
              <Table.Tr key={meter.id} data-testid={`meter-row-${meter.id}`}>
                <Table.Td>
                  <Text fw={500} size="sm">
                    {meter.label}
                  </Text>
                </Table.Td>
                <Table.Td>
                  <Badge variant="outline" size="sm">
                    {meter.commodity}
                  </Badge>
                </Table.Td>
                <Table.Td>
                  <Text size="xs" c="dimmed">
                    {formatLocalDate(meter.started_at)}
                  </Text>
                </Table.Td>
                <Table.Td>
                  <Text size="xs" c="dimmed">
                    {meter.ended_at ? formatLocalDate(meter.ended_at) : '—'}
                  </Text>
                </Table.Td>
                <Table.Td>
                  <Badge
                    color={isActive ? 'green' : 'gray'}
                    variant="light"
                    size="sm"
                    data-testid={`meter-status-${meter.id}`}
                  >
                    {isActive ? 'active' : 'closed'}
                  </Badge>
                </Table.Td>
                <Table.Td>
                  <Badge variant="outline" size="sm" color="blue">
                    {meter.reason}
                  </Badge>
                </Table.Td>
                <Table.Td>
                  <Group justify="flex-end" gap="xs">
                    <Button
                      size="xs"
                      variant="outline"
                      onClick={() => onEdit(meter)}
                      data-testid={`meter-edit-${meter.id}`}
                    >
                      Edit
                    </Button>
                  </Group>
                </Table.Td>
              </Table.Tr>
            )
          })}
        </Table.Tbody>
      </Table>
    </ScrollArea>
  )
}

// ---------------------------------------------------------------------------
// MeterManager — top-level
// ---------------------------------------------------------------------------

export function MeterManager() {
  const metersQuery = useMeters()

  const [showDeclareForm, setShowDeclareForm] = useState(false)
  const [editMeter, setEditMeter] = useState<MeterResponse | null>(null)
  const [recomputeNotice, setRecomputeNotice] = useState(false)

  // ---------------------------------------------------------------------------
  // Render states
  // ---------------------------------------------------------------------------

  if (metersQuery.isLoading) {
    return (
      <Center py="xl" data-testid="meters-loading">
        <Loader />
      </Center>
    )
  }

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

  const meters = metersQuery.data.items

  return (
    <Stack gap="lg" data-testid="meter-manager">
      <Group justify="space-between" align="center">
        <Text fw={500}>Electricity Meters</Text>
        <Button
          onClick={() => setShowDeclareForm(true)}
          data-testid="meter-declare-button"
        >
          Declare New Meter
        </Button>
      </Group>

      {recomputeNotice && (
        <Notification
          color="teal"
          title="Billing periods recomputed"
          onClose={() => setRecomputeNotice(false)}
          data-testid="meter-recompute-notice"
        >
          The start date was corrected. Affected billing periods have been
          re-judged and cost attributions updated.
        </Notification>
      )}

      <MeterTable meters={meters} onEdit={(m) => setEditMeter(m)} />

      {/* Declare new meter */}
      {showDeclareForm && (
        <DeclareMeterForm
          onClose={() => setShowDeclareForm(false)}
          onSaved={() => setShowDeclareForm(false)}
        />
      )}

      {/* Edit existing meter */}
      {editMeter && (
        <EditMeterForm
          meter={editMeter}
          onClose={() => setEditMeter(null)}
          onSaved={(retroactive) => {
            setEditMeter(null)
            if (retroactive) setRecomputeNotice(true)
          }}
        />
      )}
    </Stack>
  )
}
