import "package:flutter/material.dart";

import "../i18n.dart";
import "../main.dart";
import "../models.dart";
import "../theme.dart";

/// Charging & map screen, mirroring the web Charging.vue. Two tabs split the
/// public charging network (a presentational discovery map + demo session +
/// nearby public stations) from the user's own home charger(s) and their real
/// OCPP control.
///
/// There is no live public-charging API yet — the session and station lists are
/// placeholders, exactly as on the web. The one real piece is the OCPP control
/// card on the Home tab, which drives a charger through the Anker Solix control
/// endpoints once the user picks Own/Proxy CSMS in Settings → Integrations.
class ChargingScreen extends StatefulWidget {
  const ChargingScreen({super.key});
  @override
  State<ChargingScreen> createState() => _ChargingScreenState();
}

/// A charging station, tone-coded by availability. Presentational demo data.
class _Station {
  final String id;
  final String name;
  final String dist;
  final int kw;
  final String conn;
  final int avail;
  final int total;
  final String price;
  final String tone; // "good" | "due" | "fault"
  final bool home;
  const _Station(this.id, this.name, this.dist, this.kw, this.conn, this.avail,
      this.total, this.price, this.tone,
      {this.home = false});
}

class _ChargingScreenState extends State<ChargingScreen> {
  int _tab = 0; // 0 = public, 1 = home
  String _selected = "sc";

  List<_Station> get _stations => [
        const _Station("sc", "DriverVault Supercharge", "0.4 km", 250, "CCS · NACS", 6, 8, "0,34 €", "good"),
        const _Station("evgo", "EVgo · Market St", "1.2 km", 150, "CCS", 2, 6, "0,41 €", "due"),
        const _Station("cp", "ChargePoint Garage", "2.1 km", 62, "J1772", 0, 4, "0,29 €", "fault"),
        _Station("home", t("charging.stations.homeCharger"), "—", 11, "NACS", 1, 1,
            t("charging.stations.offPeak"), "good", home: true),
      ];

  List<_Station> get _publicStations => _stations.where((s) => !s.home).toList();
  List<_Station> get _homeStations => _stations.where((s) => s.home).toList();

  Color _toneColor(String tone) {
    switch (tone) {
      case "due":
        return DriverVault.warning;
      case "fault":
        return DriverVault.danger;
      default:
        return DriverVault.success;
    }
  }

  String _stationStatus(_Station s) => s.avail == 0
      ? t("charging.stations.full")
      : t("charging.stations.free", params: {"avail": s.avail, "total": s.total});

  @override
  Widget build(BuildContext context) {
    final muted = DriverVault.muted(context);
    return Scaffold(
      appBar: AppBar(title: Text(t("charging.title"))),
      body: ListView(
        padding: const EdgeInsets.all(12),
        children: [
          // Eyebrow + title header, matching the web layout.
          Text(t("charging.eyebrow"),
              style: DriverVault.mono(context, size: 10, weight: FontWeight.w500, color: muted)
                  .copyWith(letterSpacing: 2.2)),
          const SizedBox(height: 2),
          Text(t("charging.title"),
              style: const TextStyle(fontSize: 26, fontWeight: FontWeight.w700, letterSpacing: -0.5)),
          const SizedBox(height: 16),

          // Tabs: public network vs. the user's own home charger(s).
          _TabBar(
            tabs: [t("charging.tabs.public"), t("charging.tabs.home")],
            index: _tab,
            onChanged: (i) => setState(() => _tab = i),
          ),
          const SizedBox(height: 16),

          // IndexedStack keeps each tab's state alive across switches (v-show parity).
          IndexedStack(
            index: _tab,
            children: [
              _PublicTab(
                stations: _publicStations,
                selected: _selected,
                onSelect: (id) => setState(() => _selected = id),
                toneColor: _toneColor,
                statusFor: _stationStatus,
              ),
              _HomeTab(
                stations: _homeStations,
                selected: _selected,
                onSelect: (id) => setState(() => _selected = id),
                toneColor: _toneColor,
                statusFor: _stationStatus,
              ),
            ],
          ),
          const SizedBox(height: 24),
        ],
      ),
    );
  }
}

// --- Shared: underline tab bar (matches Settings tabs) ----------------------

class _TabBar extends StatelessWidget {
  final List<String> tabs;
  final int index;
  final ValueChanged<int> onChanged;
  const _TabBar({required this.tabs, required this.index, required this.onChanged});

  @override
  Widget build(BuildContext context) {
    final accent = Theme.of(context).colorScheme.primary;
    final muted = DriverVault.muted(context);
    final border = DriverVault.isDark(context) ? DriverVault.darkBorder : DriverVault.ink100;
    return Container(
      decoration: BoxDecoration(border: Border(bottom: BorderSide(color: border))),
      child: Row(
        children: [
          for (var i = 0; i < tabs.length; i++)
            GestureDetector(
              onTap: () => onChanged(i),
              behavior: HitTestBehavior.opaque,
              child: Container(
                padding: const EdgeInsets.only(right: 20, bottom: 10, top: 2),
                decoration: BoxDecoration(
                  border: Border(
                    bottom: BorderSide(
                      color: i == index ? accent : Colors.transparent,
                      width: 2,
                    ),
                  ),
                ),
                child: Text(
                  tabs[i],
                  style: TextStyle(
                    fontSize: 14,
                    fontWeight: FontWeight.w600,
                    color: i == index ? (DriverVault.isDark(context) ? DriverVault.darkTextStrong : DriverVault.ink900) : muted,
                  ),
                ),
              ),
            ),
        ],
      ),
    );
  }
}

/// A titled bordered card, matching the web's dh-card.
class _Card extends StatelessWidget {
  final EdgeInsetsGeometry padding;
  final Widget child;
  const _Card({this.padding = const EdgeInsets.all(16), required this.child});

  @override
  Widget build(BuildContext context) {
    return Card(
      elevation: 0,
      margin: EdgeInsets.zero,
      shape: RoundedRectangleBorder(
        borderRadius: BorderRadius.circular(DriverVault.radiusControl),
        side: BorderSide(color: Theme.of(context).dividerColor),
      ),
      child: Padding(padding: padding, child: child),
    );
  }
}

/// The green/amber/red "Connected"/"Not connected" style pill.
class _StatusBadge extends StatelessWidget {
  final String label;
  final bool ok;
  const _StatusBadge({required this.label, required this.ok});

  @override
  Widget build(BuildContext context) {
    final dark = DriverVault.isDark(context);
    final fg = ok ? DriverVault.success : DriverVault.warning;
    final bg = ok
        ? (dark ? DriverVault.successSoftDark : DriverVault.successSoft)
        : (dark ? DriverVault.warningSoftDark : DriverVault.warningSoft);
    return Container(
      padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 3),
      decoration: BoxDecoration(color: bg, borderRadius: BorderRadius.circular(999)),
      child: Text(label, style: TextStyle(fontSize: 12, fontWeight: FontWeight.w600, color: fg)),
    );
  }
}

// --- A station row, shared by both station lists -----------------------------

class _StationTile extends StatelessWidget {
  final _Station station;
  final bool selected;
  final VoidCallback onTap;
  final Color tone;
  final String status;
  const _StationTile({
    required this.station,
    required this.selected,
    required this.onTap,
    required this.tone,
    required this.status,
  });

  @override
  Widget build(BuildContext context) {
    final muted = DriverVault.muted(context);
    final sunken = DriverVault.isDark(context) ? DriverVault.darkSunken : DriverVault.ink50;
    return InkWell(
      onTap: onTap,
      borderRadius: BorderRadius.circular(DriverVault.radiusControl),
      child: Container(
        padding: const EdgeInsets.all(10),
        decoration: BoxDecoration(
          color: selected ? DriverVault.brandTint(context) : null,
          borderRadius: BorderRadius.circular(DriverVault.radiusControl),
        ),
        child: Row(
          children: [
            Container(
              width: 36,
              height: 36,
              decoration: BoxDecoration(color: sunken, borderRadius: BorderRadius.circular(10)),
              child: Icon(Icons.bolt, size: 18, color: tone),
            ),
            const SizedBox(width: 12),
            Expanded(
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  Text(station.name,
                      maxLines: 1,
                      overflow: TextOverflow.ellipsis,
                      style: const TextStyle(fontSize: 14, fontWeight: FontWeight.w600)),
                  const SizedBox(height: 2),
                  Text("${station.dist} · ${station.kw} kW · ${station.conn}",
                      style: DriverVault.mono(context, size: 11, color: muted)),
                ],
              ),
            ),
            const SizedBox(width: 8),
            Column(
              crossAxisAlignment: CrossAxisAlignment.end,
              children: [
                Text(status, style: DriverVault.mono(context, size: 11, weight: FontWeight.w500, color: tone)),
                const SizedBox(height: 2),
                Text(station.price, style: DriverVault.mono(context, size: 11, color: muted)),
              ],
            ),
          ],
        ),
      ),
    );
  }
}

/// A section wrapper with an eyebrow header and a list of stations.
class _StationList extends StatelessWidget {
  final String heading;
  final List<_Station> stations;
  final String selected;
  final ValueChanged<String> onSelect;
  final Color Function(String) toneColor;
  final String Function(_Station) statusFor;
  const _StationList({
    required this.heading,
    required this.stations,
    required this.selected,
    required this.onSelect,
    required this.toneColor,
    required this.statusFor,
  });

  @override
  Widget build(BuildContext context) {
    final muted = DriverVault.muted(context);
    return _Card(
      padding: const EdgeInsets.all(8),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Padding(
            padding: const EdgeInsets.fromLTRB(6, 6, 6, 8),
            child: Text(
              "$heading · ${t("charging.stations.count", params: {"n": stations.length}, n: stations.length)}",
              style: DriverVault.mono(context, size: 10, weight: FontWeight.w500, color: muted)
                  .copyWith(letterSpacing: 1.4),
            ),
          ),
          for (final s in stations)
            _StationTile(
              station: s,
              selected: selected == s.id,
              onTap: () => onSelect(s.id),
              tone: toneColor(s.tone),
              status: statusFor(s),
            ),
        ],
      ),
    );
  }
}

// --- Public tab: discovery map + demo session + public station list ----------

class _PublicTab extends StatefulWidget {
  final List<_Station> stations;
  final String selected;
  final ValueChanged<String> onSelect;
  final Color Function(String) toneColor;
  final String Function(_Station) statusFor;
  const _PublicTab({
    required this.stations,
    required this.selected,
    required this.onSelect,
    required this.toneColor,
    required this.statusFor,
  });

  @override
  State<_PublicTab> createState() => _PublicTabState();
}

class _PublicTabState extends State<_PublicTab> {
  // The demo session card is presentational, so its running/stopped state is
  // purely local.
  bool _charging = true;

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        _MapPanel(
          stations: widget.stations,
          selected: widget.selected,
          onSelect: widget.onSelect,
          toneColor: widget.toneColor,
        ),
        const SizedBox(height: 16),
        _SessionCard(charging: _charging, onStop: () => setState(() => _charging = false)),
        const SizedBox(height: 16),
        _StationList(
          heading: t("charging.stations.heading"),
          stations: widget.stations,
          selected: widget.selected,
          onSelect: widget.onSelect,
          toneColor: widget.toneColor,
          statusFor: widget.statusFor,
        ),
      ],
    );
  }
}

/// A stylized discovery map with a "you are here" marker and charger pins,
/// standing in for a real map the way the web's CSS grid panel does.
class _MapPanel extends StatelessWidget {
  final List<_Station> stations;
  final String selected;
  final ValueChanged<String> onSelect;
  final Color Function(String) toneColor;
  const _MapPanel({
    required this.stations,
    required this.selected,
    required this.onSelect,
    required this.toneColor,
  });

  // Pin positions as fractions of the panel, mirroring the web's x/y percents.
  static const _pos = {
    "sc": Offset(0.47, 0.34),
    "evgo": Offset(0.26, 0.60),
    "cp": Offset(0.70, 0.64),
  };

  @override
  Widget build(BuildContext context) {
    final dark = DriverVault.isDark(context);
    final base = dark ? DriverVault.darkSunken : DriverVault.ink25;
    final grid = dark ? Colors.white.withValues(alpha: 0.04) : const Color(0x0B0F1E3D);
    return ClipRRect(
      borderRadius: BorderRadius.circular(DriverVault.radiusCard),
      child: Container(
        height: 320,
        decoration: BoxDecoration(
          color: base,
          border: Border.all(color: Theme.of(context).dividerColor),
          borderRadius: BorderRadius.circular(DriverVault.radiusCard),
        ),
        child: LayoutBuilder(
          builder: (context, c) {
            final w = c.maxWidth, h = c.maxHeight;
            return Stack(
              children: [
                // Grid lines.
                CustomPaint(size: Size(w, h), painter: _GridPainter(grid)),
                // Roads.
                Positioned(left: 0, right: 0, top: h * 0.52, child: Container(height: 12, color: DriverVault.brand100.withValues(alpha: dark ? 0.10 : 1))),
                Positioned(top: 0, bottom: 0, left: w * 0.58, child: Container(width: 12, color: DriverVault.brand100.withValues(alpha: dark ? 0.10 : 1))),
                // Legend chip.
                Positioned(
                  left: 12,
                  top: 12,
                  child: Container(
                    padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 6),
                    decoration: BoxDecoration(
                      color: Theme.of(context).cardColor.withValues(alpha: 0.9),
                      borderRadius: BorderRadius.circular(999),
                      border: Border.all(color: Theme.of(context).dividerColor),
                    ),
                    child: Row(mainAxisSize: MainAxisSize.min, children: [
                      const Icon(Icons.map_outlined, size: 13, color: DriverVault.brand600),
                      const SizedBox(width: 5),
                      Text(t("charging.liveMap"),
                          style: DriverVault.mono(context, size: 10, weight: FontWeight.w500, color: DriverVault.muted(context))
                              .copyWith(letterSpacing: 1.4)),
                    ]),
                  ),
                ),
                // You are here.
                Positioned(
                  left: w * 0.5 - 20,
                  top: h * 0.5 - 20,
                  child: Tooltip(
                    message: t("charging.youAreHere"),
                    child: Container(
                      width: 40,
                      height: 40,
                      decoration: BoxDecoration(color: DriverVault.brand600.withValues(alpha: 0.16), shape: BoxShape.circle),
                      child: Center(
                        child: Container(
                          width: 14,
                          height: 14,
                          decoration: BoxDecoration(
                            color: DriverVault.brand600,
                            shape: BoxShape.circle,
                            border: Border.all(color: Colors.white, width: 2),
                          ),
                        ),
                      ),
                    ),
                  ),
                ),
                // Charger pins.
                for (final s in stations)
                  if (_pos[s.id] != null)
                    Positioned(
                      left: w * _pos[s.id]!.dx - (selected == s.id ? 18 : 14),
                      top: h * _pos[s.id]!.dy - (selected == s.id ? 36 : 28),
                      child: _Pin(
                        selected: selected == s.id,
                        color: toneColor(s.tone),
                        label: "${s.avail}/${s.total} · ${s.kw} kW",
                        onTap: () => onSelect(s.id),
                      ),
                    ),
              ],
            );
          },
        ),
      ),
    );
  }
}

class _Pin extends StatelessWidget {
  final bool selected;
  final Color color;
  final String label;
  final VoidCallback onTap;
  const _Pin({required this.selected, required this.color, required this.label, required this.onTap});

  @override
  Widget build(BuildContext context) {
    final size = selected ? 36.0 : 28.0;
    return GestureDetector(
      onTap: onTap,
      child: Column(
        mainAxisSize: MainAxisSize.min,
        children: [
          if (selected)
            Container(
              margin: const EdgeInsets.only(bottom: 6),
              padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 5),
              decoration: BoxDecoration(
                color: Theme.of(context).cardColor,
                borderRadius: BorderRadius.circular(8),
                border: Border.all(color: Theme.of(context).dividerColor),
              ),
              child: Text(label, style: const TextStyle(fontSize: 11, fontWeight: FontWeight.w600)),
            ),
          Container(
            width: size,
            height: size,
            decoration: BoxDecoration(
              color: color,
              border: Border.all(color: Colors.white, width: 2),
              borderRadius: const BorderRadius.only(
                topLeft: Radius.circular(999),
                topRight: Radius.circular(999),
                bottomRight: Radius.circular(999),
                bottomLeft: Radius.circular(3),
              ),
            ),
            child: Icon(Icons.bolt, color: Colors.white, size: selected ? 18 : 14),
          ),
        ],
      ),
    );
  }
}

/// Faint grid lines behind the map, echoing the web's repeating gradients.
class _GridPainter extends CustomPainter {
  final Color color;
  _GridPainter(this.color);
  @override
  void paint(Canvas canvas, Size size) {
    final p = Paint()
      ..color = color
      ..strokeWidth = 1;
    const step = 44.0;
    for (double x = 0; x < size.width; x += step) {
      canvas.drawLine(Offset(x, 0), Offset(x, size.height), p);
    }
    for (double y = 0; y < size.height; y += step) {
      canvas.drawLine(Offset(0, y), Offset(size.width, y), p);
    }
  }

  @override
  bool shouldRepaint(covariant _GridPainter old) => old.color != color;
}

/// The presentational "Charging now" session card (dark brand background).
class _SessionCard extends StatelessWidget {
  final bool charging;
  final VoidCallback onStop;
  const _SessionCard({required this.charging, required this.onStop});

  @override
  Widget build(BuildContext context) {
    const from = 62, to = 80;
    final metrics = [
      (t("charging.session.rate"), "142 kW"),
      (t("charging.session.added"), "+29 km"),
      (t("charging.session.cost"), "5,80 €"),
      (t("charging.session.done"), "~18 min"),
    ];
    return Container(
      width: double.infinity,
      padding: const EdgeInsets.all(20),
      decoration: BoxDecoration(
        color: DriverVault.brand900,
        borderRadius: BorderRadius.circular(DriverVault.radiusCard),
      ),
      child: charging
          ? Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                Row(mainAxisSize: MainAxisSize.min, children: [
                  Container(width: 6, height: 6, decoration: const BoxDecoration(color: DriverVault.success, shape: BoxShape.circle)),
                  const SizedBox(width: 8),
                  Text("${t("charging.session.chargingNow")} · Model Y",
                      style: DriverVault.mono(context, size: 10, weight: FontWeight.w500, color: DriverVault.brand300)
                          .copyWith(letterSpacing: 1.4)),
                ]),
                const SizedBox(height: 12),
                RichText(
                  text: TextSpan(
                    style: DriverVault.mono(context, size: 34, weight: FontWeight.w500, color: Colors.white),
                    children: [
                      const TextSpan(text: "$from"),
                      TextSpan(
                        text: " % → $to%",
                        style: DriverVault.mono(context, size: 15, weight: FontWeight.w500, color: Colors.white.withValues(alpha: 0.7)),
                      ),
                    ],
                  ),
                ),
                const SizedBox(height: 14),
                ClipRRect(
                  borderRadius: BorderRadius.circular(999),
                  child: LinearProgressIndicator(
                    value: from / 100,
                    minHeight: 8,
                    backgroundColor: Colors.white.withValues(alpha: 0.15),
                    valueColor: const AlwaysStoppedAnimation(DriverVault.brand400),
                  ),
                ),
                const SizedBox(height: 16),
                Row(
                  mainAxisAlignment: MainAxisAlignment.spaceBetween,
                  children: [
                    for (final m in metrics)
                      Column(
                        crossAxisAlignment: CrossAxisAlignment.start,
                        children: [
                          Text(m.$1,
                              style: DriverVault.mono(context, size: 9, weight: FontWeight.w500, color: DriverVault.brand300)
                                  .copyWith(letterSpacing: 1.2)),
                          const SizedBox(height: 2),
                          Text(m.$2, style: DriverVault.mono(context, size: 14, weight: FontWeight.w500, color: Colors.white)),
                        ],
                      ),
                  ],
                ),
                const SizedBox(height: 16),
                SizedBox(
                  width: double.infinity,
                  child: OutlinedButton(
                    onPressed: onStop,
                    style: OutlinedButton.styleFrom(
                      foregroundColor: Colors.white,
                      backgroundColor: Colors.white.withValues(alpha: 0.10),
                      side: BorderSide(color: Colors.white.withValues(alpha: 0.20)),
                    ),
                    child: Text(t("charging.session.stop")),
                  ),
                ),
              ],
            )
          : Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                Row(mainAxisSize: MainAxisSize.min, children: [
                  Container(width: 6, height: 6, decoration: BoxDecoration(color: Colors.white.withValues(alpha: 0.4), shape: BoxShape.circle)),
                  const SizedBox(width: 8),
                  Text(t("charging.session.idle"),
                      style: DriverVault.mono(context, size: 10, weight: FontWeight.w500, color: DriverVault.brand300)
                          .copyWith(letterSpacing: 1.4)),
                ]),
                const SizedBox(height: 12),
                Text(t("charging.session.idleHint"),
                    style: TextStyle(fontSize: 14, color: Colors.white.withValues(alpha: 0.7))),
              ],
            ),
    );
  }
}

// --- Home tab: real OCPP control + home station list -------------------------

class _HomeTab extends StatelessWidget {
  final List<_Station> stations;
  final String selected;
  final ValueChanged<String> onSelect;
  final Color Function(String) toneColor;
  final String Function(_Station) statusFor;
  const _HomeTab({
    required this.stations,
    required this.selected,
    required this.onSelect,
    required this.toneColor,
    required this.statusFor,
  });

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        const _ControlCard(),
        const SizedBox(height: 16),
        _StationList(
          heading: t("charging.stations.homeHeading"),
          stations: stations,
          selected: selected,
          onSelect: onSelect,
          toneColor: toneColor,
          statusFor: statusFor,
        ),
      ],
    );
  }
}

/// The real OCPP control card, gated by the per-user Anker Solix control mode.
/// When no control mode is active it degrades to a hint pointing at Settings.
class _ControlCard extends StatefulWidget {
  const _ControlCard();
  @override
  State<_ControlCard> createState() => _ControlCardState();
}

class _ControlCardState extends State<_ControlCard> {
  final _serial = TextEditingController();
  List<AnkerCharger> _chargers = const []; // the account's chargers, when known
  String _mode = "off"; // effective control mode (off | own | proxy)
  AnkerControl? _ctl;
  String? _error;
  String _busy = ""; // action name currently in flight
  double _limitAmps = 16;
  bool _resetPrompt = false;
  final _resetPassword = TextEditingController();

  bool get _active => _mode != "off";
  bool get _connected => _ctl?.connected == true;

  @override
  void initState() {
    super.initState();
    _init();
  }

  @override
  void dispose() {
    _serial.dispose();
    _resetPassword.dispose();
    super.dispose();
  }

  Future<void> _init() async {
    try {
      final v = await apiClient.getAnkerSolix();
      if (mounted) setState(() => _mode = v.controlMode);
    } catch (_) {
      if (mounted) setState(() => _mode = "off");
    }
    if (_active) await _loadChargers();
    await _refresh();
  }

  /// The account's chargers turn the serial into a pick from a list. Without
  /// them (nothing linked, or the cloud unreachable) the field stays a text box
  /// so a serial can still be typed in by hand.
  Future<void> _loadChargers() async {
    try {
      final res = await apiClient.listAnkerChargers();
      if (!mounted) return;
      setState(() {
        _chargers = res.chargers;
        // Nothing chosen yet: start on the first charger the account reports.
        if (_serial.text.trim().isEmpty && _chargers.isNotEmpty) {
          _serial.text = _chargers.first.sn;
        }
      });
    } catch (_) {
      if (mounted) setState(() => _chargers = const []);
    }
  }

  Future<void> _refresh() async {
    final sn = _serial.text.trim();
    if (sn.isEmpty) {
      setState(() => _ctl = null);
      return;
    }
    setState(() => _error = null);
    try {
      final c = await apiClient.getAnkerControl(sn);
      if (mounted) setState(() => _ctl = c);
    } catch (e) {
      if (mounted) {
        setState(() {
          _error = "$e";
          _ctl = null;
        });
      }
    }
  }

  Future<void> _action(String action, [Map<String, dynamic> body = const {}]) async {
    final sn = _serial.text.trim();
    if (sn.isEmpty) return;
    setState(() {
      _busy = action;
      _error = null;
    });
    try {
      await apiClient.ankerControlAction(sn, action, body);
      await _refresh();
    } catch (e) {
      if (mounted) setState(() => _error = "$e");
    } finally {
      if (mounted) setState(() => _busy = "");
    }
  }

  Future<void> _confirmReset() async {
    if (_resetPassword.text.isEmpty) return;
    setState(() => _resetPrompt = false);
    await _action("reset", {"hard": false, "confirm": true, "password": _resetPassword.text});
    _resetPassword.clear();
  }

  @override
  Widget build(BuildContext context) {
    final muted = DriverVault.muted(context);
    if (!_active) {
      // No control mode active — point the user at Settings to enable it.
      return _Card(
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Text(t("charging.control.title"), style: const TextStyle(fontSize: 15, fontWeight: FontWeight.w600)),
            const SizedBox(height: 8),
            Text(t("charging.stations.noControlHint"), style: TextStyle(fontSize: 12, color: muted)),
          ],
        ),
      );
    }

    return _Card(
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Row(
            children: [
              Expanded(
                child: Text(t("charging.control.title"),
                    style: const TextStyle(fontSize: 15, fontWeight: FontWeight.w600)),
              ),
              _StatusBadge(
                label: _connected ? t("charging.control.connected") : t("charging.control.disconnected"),
                ok: _connected,
              ),
            ],
          ),
          const SizedBox(height: 12),
          Row(children: [
            Expanded(
              child: _chargers.isNotEmpty
                  ? DropdownButtonFormField<String>(
                      initialValue: _serial.text.trim().isEmpty ? null : _serial.text.trim(),
                      isExpanded: true,
                      decoration: const InputDecoration(border: OutlineInputBorder(), isDense: true),
                      items: [
                        for (final c in _chargers)
                          DropdownMenuItem(
                            value: c.sn,
                            child: Text(c.name.isNotEmpty ? "${c.name} · ${c.sn}" : c.sn,
                                overflow: TextOverflow.ellipsis),
                          ),
                      ],
                      onChanged: (sn) {
                        if (sn == null) return;
                        setState(() => _serial.text = sn);
                        _refresh();
                      },
                    )
                  : TextField(
                      controller: _serial,
                      autocorrect: false,
                      decoration: InputDecoration(
                        border: const OutlineInputBorder(),
                        isDense: true,
                        hintText: t("charging.control.serialPlaceholder"),
                      ),
                    ),
            ),
            const SizedBox(width: 8),
            OutlinedButton(onPressed: _refresh, child: Text(t("charging.control.refresh"))),
          ]),
          if (_connected) ...[
            const SizedBox(height: 12),
            Row(children: [
              Expanded(child: _MetricTile(value: _ctl?.connectorStatus.isNotEmpty == true ? _ctl!.connectorStatus : "—", label: t("charging.control.status"))),
              const SizedBox(width: 8),
              Expanded(child: _MetricTile(value: "${_ctl!.meterKwh.toStringAsFixed(2)} kWh", label: t("charging.control.meter"))),
            ]),
            const SizedBox(height: 12),
            Row(children: [
              Expanded(
                child: FilledButton(
                  onPressed: _busy == "start" ? null : () => _action("start"),
                  child: Text(t("charging.control.start")),
                ),
              ),
              const SizedBox(width: 8),
              Expanded(
                child: OutlinedButton(
                  onPressed: _busy == "stop" ? null : () => _action("stop"),
                  child: Text(t("charging.control.stop")),
                ),
              ),
            ]),
            const SizedBox(height: 12),
            Row(children: [
              Text(t("charging.control.limit"), style: TextStyle(fontSize: 13, color: muted)),
              const Spacer(),
              Text("${_limitAmps.round()} A", style: DriverVault.mono(context, size: 13, weight: FontWeight.w500)),
            ]),
            Slider(
              value: _limitAmps,
              min: 6,
              max: 32,
              divisions: 26,
              label: "${_limitAmps.round()} A",
              onChanged: (v) => setState(() => _limitAmps = v),
            ),
            Row(children: [
              Expanded(
                child: OutlinedButton(
                  onPressed: _busy == "limit" ? null : () => _action("limit", {"amps": _limitAmps.round()}),
                  child: Text(t("charging.control.applyLimit")),
                ),
              ),
              const SizedBox(width: 8),
              Expanded(
                child: OutlinedButton(
                  onPressed: _busy == "clear-limit" ? null : () => _action("clear-limit"),
                  child: Text(t("charging.control.clearLimit")),
                ),
              ),
            ]),
            const SizedBox(height: 12),
            if (!_resetPrompt)
              SizedBox(
                width: double.infinity,
                child: OutlinedButton(
                  onPressed: _busy == "reset"
                      ? null
                      : () => setState(() {
                            _error = null;
                            _resetPassword.clear();
                            _resetPrompt = true;
                          }),
                  child: Text(t("charging.control.reset")),
                ),
              )
            else
              // Step-up: destructive reset requires re-entering the password.
              Container(
                padding: const EdgeInsets.all(12),
                decoration: BoxDecoration(
                  color: DriverVault.isDark(context) ? DriverVault.dangerSoftDark : DriverVault.dangerSoft,
                  borderRadius: BorderRadius.circular(DriverVault.radiusControl),
                  border: Border.all(color: DriverVault.danger.withValues(alpha: 0.4)),
                ),
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    Text(t("charging.control.resetConfirm"),
                        style: const TextStyle(fontSize: 12, fontWeight: FontWeight.w500, color: DriverVault.danger)),
                    const SizedBox(height: 8),
                    TextField(
                      controller: _resetPassword,
                      obscureText: true,
                      decoration: InputDecoration(
                        border: const OutlineInputBorder(),
                        isDense: true,
                        hintText: t("charging.control.resetPassword"),
                      ),
                      onSubmitted: (_) => _confirmReset(),
                    ),
                    const SizedBox(height: 8),
                    Row(children: [
                      Expanded(
                        child: OutlinedButton(
                          onPressed: () => setState(() {
                            _resetPrompt = false;
                            _resetPassword.clear();
                          }),
                          child: Text(t("common.cancel")),
                        ),
                      ),
                      const SizedBox(width: 8),
                      Expanded(
                        child: FilledButton(
                          style: FilledButton.styleFrom(backgroundColor: DriverVault.danger),
                          onPressed: _busy == "reset" ? null : _confirmReset,
                          child: Text(t("charging.control.reset")),
                        ),
                      ),
                    ]),
                  ],
                ),
              ),
          ] else
            Padding(
              padding: const EdgeInsets.only(top: 12),
              child: Text(t("charging.control.connectHint"), style: TextStyle(fontSize: 12, color: muted)),
            ),
          if (_error != null)
            Padding(
              padding: const EdgeInsets.only(top: 8),
              child: Text(_error!, style: const TextStyle(fontSize: 13, color: DriverVault.danger)),
            ),
        ],
      ),
    );
  }
}

/// A small sunken metric tile (connector status / energy).
class _MetricTile extends StatelessWidget {
  final String value;
  final String label;
  const _MetricTile({required this.value, required this.label});

  @override
  Widget build(BuildContext context) {
    final muted = DriverVault.muted(context);
    final sunken = DriverVault.isDark(context) ? DriverVault.darkSunken : DriverVault.ink50;
    return Container(
      padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
      decoration: BoxDecoration(color: sunken, borderRadius: BorderRadius.circular(DriverVault.radiusControl)),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Text(value, style: DriverVault.mono(context, size: 14, weight: FontWeight.w600)),
          const SizedBox(height: 2),
          Text(label, style: TextStyle(fontSize: 11, color: muted)),
        ],
      ),
    );
  }
}
