import "package:flutter/material.dart";

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

/// Admin-only screen to manage user accounts: list, create, change role,
/// reset password, delete. The API enforces the real access control — an admin
/// is scoped to their own organization and cannot touch a superadmin, and
/// nobody may change their own role or delete their own account. The UI mirrors
/// those guards to avoid offering dead actions.
class AdminUsersScreen extends StatefulWidget {
  const AdminUsersScreen({super.key});
  @override
  State<AdminUsersScreen> createState() => _AdminUsersScreenState();
}

class _AdminUsersScreenState extends State<AdminUsersScreen> {
  late Future<List<AdminUser>> _future;

  @override
  void initState() {
    super.initState();
    _future = apiClient.listUsers();
  }

  void _reload() => setState(() => _future = apiClient.listUsers());

  String? get _myId => authService.user?.id;
  bool get _iamSuperadmin => authService.user?.isSuperadmin == true;

  /// Roles this viewer may hand out. Only a superadmin can mint another one.
  List<String> get _assignableRoles =>
      _iamSuperadmin ? const ["user", "admin", "superadmin"] : const ["user", "admin"];

  /// Why a row's destructive and role controls are locked, or "" when they are
  /// not. These mirror the server's guards, so the UI never offers an action
  /// that would come back a 403 — and, unlike simply greying the control out,
  /// they say which guard it is. Same wording as the web app's tooltips.
  String _deleteBlockedReason(AdminUser u) {
    if (u.id == _myId) return t("admin.cantDeleteSelf");
    if (u.isSuperadmin && !_iamSuperadmin) return t("admin.onlySuperadminDeletes");
    return "";
  }

  String _roleLockReason(AdminUser u) {
    if (u.id == _myId) return t("admin.cantChangeOwnRole");
    if (u.isSuperadmin && !_iamSuperadmin) return t("admin.onlySuperadminEdits");
    return "";
  }

  void _snack(String msg) =>
      ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(msg)));

  Future<void> _changeRole(AdminUser u, String role) async {
    try {
      await apiClient.updateUser(u.id, role: role);
      _reload();
    } catch (e) {
      _snack("$e");
    }
  }

  Future<void> _resetPassword(AdminUser u) async {
    final controller = TextEditingController();
    final saved = await showDialog<bool>(
      context: context,
      builder: (ctx) => AlertDialog(
        title: Text(t("admin.resetTitle", params: {"email": u.email})),
        content: TextField(
          controller: controller,
          autofocus: true,
          decoration: InputDecoration(
            labelText: '${t("admin.newPassword")} ${t("admin.minChars")}',
            border: const OutlineInputBorder(),
          ),
        ),
        actions: [
          TextButton(
              onPressed: () => Navigator.pop(ctx, false), child: Text(t("common.cancel"))),
          FilledButton(
              onPressed: () => Navigator.pop(ctx, true), child: Text(t("admin.setPassword"))),
        ],
      ),
    );
    if (saved != true) return;
    try {
      await apiClient.setUserPassword(u.id, controller.text);
      _snack(t("admin.passwordUpdated"));
    } catch (e) {
      _snack("$e");
    }
  }

  Future<void> _delete(AdminUser u) async {
    final confirmed = await showDialog<bool>(
      context: context,
      builder: (ctx) => AlertDialog(
        title: Text(t("admin.deleteTitle")),
        content: Text(t("admin.confirmDelete",
            params: {"name": u.name.isEmpty ? u.email : u.name})),
        actions: [
          TextButton(
              onPressed: () => Navigator.pop(ctx, false), child: Text(t("common.cancel"))),
          FilledButton(
            style: FilledButton.styleFrom(backgroundColor: DriverVault.danger),
            onPressed: () => Navigator.pop(ctx, true),
            child: Text(t("common.delete")),
          ),
        ],
      ),
    );
    if (confirmed != true) return;
    try {
      await apiClient.deleteUser(u.id);
      _reload();
    } catch (e) {
      _snack("$e");
    }
  }

  Future<void> _createUser() async {
    final created = await showModalBottomSheet<bool>(
      context: context,
      isScrollControlled: true,
      builder: (_) => const _CreateUserSheet(),
    );
    if (created == true) _reload();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text(t("admin.title"))),
      floatingActionButton: FloatingActionButton.extended(
        onPressed: _createUser,
        icon: const Icon(Icons.person_add_alt_1),
        label: Text(t("admin.addUser")),
      ),
      body: FutureBuilder<List<AdminUser>>(
        future: _future,
        builder: (context, snap) {
          if (snap.connectionState == ConnectionState.waiting) {
            return const Center(child: CircularProgressIndicator());
          }
          if (snap.hasError) {
            return Center(child: Text("${snap.error}"));
          }
          final users = snap.data ?? [];
          return ListView.separated(
            padding: const EdgeInsets.all(12),
            itemCount: users.length,
            separatorBuilder: (_, __) => const Divider(height: 1),
            itemBuilder: (context, i) {
              final u = users[i];
              // An admin may not edit or delete a superadmin, and nobody may
              // touch their own role or account. Both guards carry the sentence
              // that explains them.
              final roleLock = _roleLockReason(u);
              final deleteLock = _deleteBlockedReason(u);
              final isSelf = u.id == _myId;
              final locked = u.isSuperadmin && !_iamSuperadmin;
              return ListTile(
                contentPadding: const EdgeInsets.symmetric(horizontal: 4),
                title: Row(
                  children: [
                    Flexible(child: Text(u.email, overflow: TextOverflow.ellipsis)),
                    if (isSelf)
                      Padding(
                        padding: const EdgeInsets.only(left: 6),
                        child: Text(t("admin.you"),
                            style: const TextStyle(color: Colors.grey, fontSize: 12)),
                      ),
                  ],
                ),
                subtitle: Text(
                  "${u.name.isEmpty ? t("common.empty") : u.name}"
                  "${u.organizationName.isEmpty ? '' : ' · ${u.organizationName}'}"
                  " · ${formatDate(DateTime.tryParse(u.created))}",
                  style: const TextStyle(fontSize: 12),
                ),
                trailing: Row(
                  mainAxisSize: MainAxisSize.min,
                  children: [
                    // A long-press on the greyed-out picker says why it is
                    // greyed out — the touch equivalent of the web's tooltip.
                    Tooltip(
                      message: roleLock,
                      triggerMode: roleLock.isEmpty
                          ? TooltipTriggerMode.manual
                          : TooltipTriggerMode.longPress,
                      child: DropdownButton<String>(
                        value: u.role,
                        underline: const SizedBox.shrink(),
                        onChanged: roleLock.isNotEmpty
                            ? null
                            : (v) => v == null ? null : _changeRole(u, v),
                        items: [
                          for (final r in _assignableRoles)
                            DropdownMenuItem(value: r, child: Text(t("admin.roles.$r"))),
                          // Keep the current role selectable even when this
                          // viewer can't assign it, so the dropdown has a
                          // valid value.
                          if (!_assignableRoles.contains(u.role))
                            DropdownMenuItem(
                                value: u.role, child: Text(t("admin.roles.${u.role}"))),
                        ],
                      ),
                    ),
                    PopupMenuButton<String>(
                      onSelected: (choice) {
                        if (choice == "password") _resetPassword(u);
                        if (choice == "delete") _delete(u);
                      },
                      itemBuilder: (_) => [
                        PopupMenuItem(
                          value: "password",
                          enabled: !locked,
                          child: _MenuEntry(
                            label: t("admin.resetPassword"),
                            reason: locked ? t("admin.onlySuperadminEdits") : "",
                          ),
                        ),
                        PopupMenuItem(
                          value: "delete",
                          enabled: deleteLock.isEmpty,
                          child: _MenuEntry(
                            label: t("common.delete"),
                            reason: deleteLock,
                            color: DriverVault.danger,
                          ),
                        ),
                      ],
                    ),
                  ],
                ),
              );
            },
          );
        },
      ),
    );
  }
}

class _CreateUserSheet extends StatefulWidget {
  const _CreateUserSheet();
  @override
  State<_CreateUserSheet> createState() => _CreateUserSheetState();
}

class _CreateUserSheetState extends State<_CreateUserSheet> {
  final _email = TextEditingController();
  final _name = TextEditingController();
  final _password = TextEditingController();
  String _role = "user";
  bool _saving = false;
  String? _error;

  /// Organizations a superadmin can drop the new account into. An admin gets no
  /// picker: the server puts their members in their own org regardless, so
  /// offering a choice would be a lie. Empty until loaded, and left empty if the
  /// listing fails — a picker that cannot be filled must not block the form.
  List<Organization> _orgs = const [];

  /// The chosen tenant; "" is a real choice, meaning an account belonging to no
  /// organization at all.
  String _organization = "";

  bool get _iamSuperadmin => authService.user?.isSuperadmin == true;

  /// Only a superadmin can create another one. An admin's new users are placed
  /// in the admin's own organization by the server.
  List<String> get _assignableRoles => authService.user?.isSuperadmin == true
      ? const ["user", "admin", "superadmin"]
      : const ["user", "admin"];

  @override
  void initState() {
    super.initState();
    if (_iamSuperadmin) _loadOrgs();
  }

  Future<void> _loadOrgs() async {
    try {
      final orgs = await apiClient.listOrgs();
      if (mounted) setState(() => _orgs = orgs);
    } catch (_) {
      // Listing is manager-only and can fail; the picker then offers only "no
      // organization", which is still a valid account to create.
    }
  }

  @override
  void dispose() {
    _email.dispose();
    _name.dispose();
    _password.dispose();
    super.dispose();
  }

  Future<void> _save() async {
    setState(() {
      _saving = true;
      _error = null;
    });
    try {
      await apiClient.createUser(
        email: _email.text.trim(),
        password: _password.text,
        name: _name.text.trim(),
        role: _role,
        // Only a superadmin picks; for an admin the server forces its own org,
        // so sending anything here would be noise.
        organization: _iamSuperadmin ? _organization : null,
      );
      if (mounted) Navigator.pop(context, true);
    } catch (e) {
      setState(() => _error = e.toString());
    } finally {
      if (mounted) setState(() => _saving = false);
    }
  }

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: EdgeInsets.only(
        left: 16,
        right: 16,
        top: 16,
        bottom: DriverVault.sheetBottomInset(context),
      ),
      child: Column(
        mainAxisSize: MainAxisSize.min,
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Text(t("admin.createTitle"),
              style: const TextStyle(fontSize: 18, fontWeight: FontWeight.w600)),
          const SizedBox(height: 12),
          if (_error != null)
            Padding(
              padding: const EdgeInsets.only(bottom: 8),
              child: Text(_error!, style: const TextStyle(color: DriverVault.danger)),
            ),
          TextField(
            controller: _email,
            keyboardType: TextInputType.emailAddress,
            decoration: InputDecoration(
                labelText: t("admin.emailRequired"), border: const OutlineInputBorder()),
          ),
          const SizedBox(height: 8),
          TextField(
            controller: _name,
            decoration: InputDecoration(
                labelText: t("admin.colName"), border: const OutlineInputBorder()),
          ),
          const SizedBox(height: 8),
          Row(
            children: [
              Expanded(
                child: TextField(
                  controller: _password,
                  decoration: InputDecoration(
                    labelText: '${t("admin.passwordRequired")} ${t("admin.minChars")}',
                    border: const OutlineInputBorder(),
                  ),
                ),
              ),
              const SizedBox(width: 8),
              DropdownButton<String>(
                value: _role,
                onChanged: (v) => setState(() => _role = v ?? "user"),
                items: [
                  for (final r in _assignableRoles)
                    DropdownMenuItem(value: r, child: Text(t("admin.roles.$r"))),
                ],
              ),
            ],
          ),
          if (_iamSuperadmin) ...[
            const SizedBox(height: 8),
            DropdownButtonFormField<String>(
              initialValue: _organization,
              isExpanded: true,
              decoration: InputDecoration(
                labelText: t("admin.colOrganization"),
                helperText: t("admin.organizationHint"),
                helperMaxLines: 3,
                border: const OutlineInputBorder(),
              ),
              items: [
                DropdownMenuItem(value: "", child: Text(t("admin.noOrganization"))),
                for (final o in _orgs) DropdownMenuItem(value: o.id, child: Text(o.name)),
              ],
              onChanged: (v) => setState(() => _organization = v ?? ""),
            ),
          ],
          const SizedBox(height: 12),
          SizedBox(
            width: double.infinity,
            child: FilledButton(
              onPressed: _saving ? null : _save,
              child: Text(t(_saving ? "admin.creating" : "admin.createUser")),
            ),
          ),
        ],
      ),
    );
  }
}

/// One entry of a row's overflow menu. A blocked action keeps its label and
/// gains the sentence saying why it is blocked: the item is disabled, so it
/// cannot be long-pressed for a tooltip the way the role picker can, and a
/// silently greyed-out action is the thing this is meant to avoid.
class _MenuEntry extends StatelessWidget {
  final String label;
  final String reason;
  final Color? color;
  const _MenuEntry({required this.label, required this.reason, this.color});

  @override
  Widget build(BuildContext context) {
    if (reason.isEmpty) return Text(label, style: TextStyle(color: color));
    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      mainAxisSize: MainAxisSize.min,
      children: [
        Text(label, style: TextStyle(color: color)),
        Text(reason,
            style: TextStyle(fontSize: 11, color: DriverVault.muted(context)),
            softWrap: true),
      ],
    );
  }
}
