import 'package:flutter/material.dart';
import 'package:get_it/get_it.dart';
import 'package:go_router/go_router.dart';
import 'package:lucide_icons_flutter/lucide_icons.dart' as lucide;
import 'package:shadcn_ui/shadcn_ui.dart';

import '../core/constants/app_sections.dart';
import '../core/permissions/permission_manager.dart';
import '../core/network/failure.dart';
import '../core/theme/theme_controller.dart';
import '../core/validation/password_rules.dart';
import '../features/auth/application/auth_service.dart';
import '../features/auth/domain/entities/auth_session.dart';
import '../features/masters/user/domain/entities/user.dart';
import '../features/masters/user/domain/repositories/user_repository.dart';
import 'components/superport_dialog.dart';

/// 앱 기본 레이아웃을 제공하는 셸 위젯. 사이드 네비게이션과 AppBar를 구성한다.
class AppShell extends StatelessWidget {
  const AppShell({
    super.key,
    required this.child,
    required this.currentLocation,
  });

  final Widget child;
  final String currentLocation;

  @override
  Widget build(BuildContext context) {
    return LayoutBuilder(
      builder: (context, constraints) {
        final isWide = constraints.maxWidth >= 960;
        final manager = PermissionScope.of(context);
        final filteredPages = <AppPageDescriptor>[
          for (final section in appSections)
            for (final page in section.pages)
              if (_hasPageAccess(manager, page)) page,
        ];
        final pages = filteredPages.isEmpty ? allAppPages : filteredPages;
        final themeController = ThemeControllerScope.of(context);
        final authService = GetIt.I<AuthService>();
        final appBar = _GradientAppBar(
          title: const _BrandTitle(),
          actions: [
            _ThemeMenuButton(
              mode: themeController.mode,
              onChanged: themeController.update,
            ),
            const SizedBox(width: 8),
            _AccountMenuButton(service: authService),
            const SizedBox(width: 8),
          ],
        );

        if (isWide) {
          return Scaffold(
            appBar: appBar,
            body: Row(
              children: [
                _NavigationRail(currentLocation: currentLocation, pages: pages),
                const VerticalDivider(width: 1),
                Expanded(child: child),
              ],
            ),
          );
        }

        return Scaffold(
          appBar: appBar,
          drawer: Drawer(
            child: SafeArea(
              child: _NavigationList(
                currentLocation: currentLocation,
                onTap: (path) {
                  Navigator.of(context).pop();
                  context.go(path);
                },
                pages: pages,
              ),
            ),
          ),
          body: child,
        );
      },
    );
  }
}

class _NavigationRail extends StatelessWidget {
  const _NavigationRail({required this.currentLocation, required this.pages});

  final String currentLocation;
  final List<AppPageDescriptor> pages;

  @override
  Widget build(BuildContext context) {
    final selectedIndex = _selectedIndex(currentLocation, pages);
    final theme = Theme.of(context);
    final colorScheme = theme.colorScheme;

    return Container(
      width: 220,
      decoration: BoxDecoration(
        border: Border(right: BorderSide(color: colorScheme.outlineVariant)),
      ),
      child: Column(
        children: [
          const SizedBox(height: 24),
          Expanded(
            child: ListView.builder(
              padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
              itemCount: pages.length,
              itemBuilder: (context, index) {
                final page = pages[index];
                final isSelected = index == selectedIndex;
                final textStyle = theme.textTheme.labelSmall?.copyWith(
                  color: isSelected
                      ? colorScheme.primary
                      : colorScheme.onSurfaceVariant,
                );

                return Padding(
                  padding: const EdgeInsets.symmetric(vertical: 4),
                  child: Material(
                    color: isSelected
                        ? colorScheme.primary.withValues(alpha: 0.12)
                        : Colors.transparent,
                    borderRadius: BorderRadius.circular(12),
                    child: InkWell(
                      borderRadius: BorderRadius.circular(12),
                      onTap: () {
                        if (page.path != currentLocation) {
                          context.go(page.path);
                        }
                      },
                      child: Padding(
                        padding: const EdgeInsets.symmetric(
                          vertical: 10,
                          horizontal: 12,
                        ),
                        child: Row(
                          children: [
                            Icon(
                              page.icon,
                              size: 22,
                              color: isSelected
                                  ? colorScheme.primary
                                  : colorScheme.onSurfaceVariant,
                            ),
                            const SizedBox(width: 12),
                            Expanded(
                              child: Text(
                                page.label,
                                maxLines: 1,
                                overflow: TextOverflow.ellipsis,
                                style: textStyle,
                              ),
                            ),
                          ],
                        ),
                      ),
                    ),
                  ),
                );
              },
            ),
          ),
        ],
      ),
    );
  }
}

class _NavigationList extends StatelessWidget {
  const _NavigationList({
    required this.currentLocation,
    required this.onTap,
    required this.pages,
  });

  final String currentLocation;
  final ValueChanged<String> onTap;
  final List<AppPageDescriptor> pages;

  @override
  Widget build(BuildContext context) {
    final selectedIndex = _selectedIndex(currentLocation, pages);
    final themeController = ThemeControllerScope.of(context);

    return ListView.builder(
      itemCount: pages.length + 1,
      itemBuilder: (context, index) {
        if (index == pages.length) {
          return Padding(
            padding: const EdgeInsets.fromLTRB(16, 12, 16, 16),
            child: _ThemeMenuButton(
              mode: themeController.mode,
              onChanged: (mode) {
                themeController.update(mode);
                Navigator.of(context).maybePop();
              },
            ),
          );
        }

        final page = pages[index];
        final selected = index == selectedIndex;
        return ListTile(
          leading: Icon(page.icon),
          title: Text(page.label),
          subtitle: Text(
            page.summary,
            maxLines: 1,
            overflow: TextOverflow.ellipsis,
          ),
          selected: selected,
          selectedColor: Theme.of(context).colorScheme.primary,
          onTap: () => onTap(page.path),
        );
      },
    );
  }
}

class _BrandTitle extends StatelessWidget {
  const _BrandTitle();

  @override
  Widget build(BuildContext context) {
    final theme = Theme.of(context);
    final colorScheme = theme.colorScheme;

    return Row(
      mainAxisSize: MainAxisSize.min,
      children: [
        DecoratedBox(
          decoration: BoxDecoration(
            shape: BoxShape.circle,
            color: colorScheme.primary,
          ),
          child: Padding(
            padding: const EdgeInsets.all(12),
            child: Icon(
              lucide.LucideIcons.ship,
              size: 28,
              color: colorScheme.onPrimary,
            ),
          ),
        ),
        const SizedBox(width: 12),
        Flexible(
          child: Text(
            'Superport v2',
            maxLines: 1,
            overflow: TextOverflow.ellipsis,
            style: theme.textTheme.titleLarge?.copyWith(
              fontWeight: FontWeight.w600,
            ),
          ),
        ),
      ],
    );
  }
}

class _GradientAppBar extends StatelessWidget implements PreferredSizeWidget {
  const _GradientAppBar({required this.title, required this.actions});

  final Widget title;
  final List<Widget> actions;

  @override
  Size get preferredSize => const Size.fromHeight(kToolbarHeight);

  @override
  Widget build(BuildContext context) {
    final theme = Theme.of(context);
    final colorScheme = theme.colorScheme;

    return AppBar(
      backgroundColor: Colors.transparent,
      surfaceTintColor: Colors.transparent,
      automaticallyImplyLeading: false,
      titleSpacing: 16,
      toolbarHeight: kToolbarHeight,
      title: title,
      actions: actions,
      flexibleSpace: Container(
        decoration: BoxDecoration(
          gradient: LinearGradient(
            begin: Alignment.bottomCenter,
            end: Alignment.topCenter,
            colors: [
              colorScheme.primary.withValues(alpha: 0),
              colorScheme.primary.withValues(alpha: 0.2),
            ],
            stops: const [0, 1],
          ),
        ),
      ),
    );
  }
}

class _ThemeMenuButton extends StatelessWidget {
  const _ThemeMenuButton({required this.mode, required this.onChanged});

  final ThemeMode mode;
  final ValueChanged<ThemeMode> onChanged;

  @override
  Widget build(BuildContext context) {
    final theme = Theme.of(context);
    final colorScheme = theme.colorScheme;
    final label = _label(mode);
    final icon = _icon(mode);

    return PopupMenuButton<ThemeMode>(
      tooltip: '테마 변경',
      onSelected: onChanged,
      itemBuilder: (context) => ThemeMode.values
          .map(
            (value) => PopupMenuItem<ThemeMode>(
              value: value,
              child: Row(
                mainAxisSize: MainAxisSize.min,
                children: [
                  Icon(_icon(value), size: 18),
                  const SizedBox(width: 8),
                  Flexible(
                    child: Text(
                      _label(value),
                      maxLines: 1,
                      overflow: TextOverflow.ellipsis,
                    ),
                  ),
                ],
              ),
            ),
          )
          .toList(),
      child: Container(
        padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 10),
        decoration: BoxDecoration(
          borderRadius: BorderRadius.circular(12),
          border: Border.all(color: colorScheme.outlineVariant),
        ),
        child: Row(
          mainAxisAlignment: MainAxisAlignment.center,
          mainAxisSize: MainAxisSize.min,
          children: [
            Icon(icon, size: 18),
            const SizedBox(width: 8),
            Flexible(
              child: Text(
                '테마 · $label',
                style: theme.textTheme.labelSmall,
                maxLines: 1,
                overflow: TextOverflow.ellipsis,
              ),
            ),
          ],
        ),
      ),
    );
  }

  static String _label(ThemeMode mode) {
    switch (mode) {
      case ThemeMode.system:
        return '시스템';
      case ThemeMode.light:
        return '라이트';
      case ThemeMode.dark:
        return '다크';
    }
  }

  static IconData _icon(ThemeMode mode) {
    switch (mode) {
      case ThemeMode.system:
        return lucide.LucideIcons.monitorCog;
      case ThemeMode.light:
        return lucide.LucideIcons.sun;
      case ThemeMode.dark:
        return lucide.LucideIcons.moon;
    }
  }
}

int _selectedIndex(String location, List<AppPageDescriptor> pages) {
  final normalized = location.toLowerCase();
  final exact = pages.indexWhere(
    (page) => normalized == page.path.toLowerCase(),
  );
  if (exact != -1) {
    return exact;
  }

  final prefix = pages.indexWhere(
    (page) => normalized.startsWith(page.path.toLowerCase()),
  );
  return prefix == -1 ? 0 : prefix;
}

bool _hasPageAccess(PermissionManager manager, AppPageDescriptor page) {
  final requirements = <String>{page.path, ...page.extraRequiredResources};
  for (final resource in requirements) {
    if (resource.isEmpty) {
      continue;
    }
    if (!manager.can(resource, PermissionAction.view)) {
      return false;
    }
  }
  return true;
}

/// 계정 정보를 확인하고 로그아웃을 수행하는 상단바 버튼.
class _AccountMenuButton extends StatelessWidget {
  const _AccountMenuButton({required this.service});

  final AuthService service;

  @override
  Widget build(BuildContext context) {
    return AnimatedBuilder(
      animation: service,
      builder: (context, _) {
        return IconButton(
          tooltip: '내 정보',
          icon: const Icon(lucide.LucideIcons.userRound),
          onPressed: () => _handlePressed(context),
        );
      },
    );
  }

  Future<void> _handlePressed(BuildContext context) async {
    final userRepository = GetIt.I<UserRepository>();
    final result = await showDialog<_AccountDialogResult>(
      context: context,
      barrierDismissible: false,
      builder: (_) => _AccountDialog(
        authService: service,
        userRepository: userRepository,
        hostContext: context,
      ),
    );

    if (!context.mounted) {
      return;
    }

    switch (result) {
      case _AccountDialogResult.logout:
        await service.clearSession();
        if (context.mounted) {
          context.go(loginRoutePath);
        }
        break;
      case _AccountDialogResult.passwordChanged:
        final confirmed = await _showMandatoryLogoutDialog(context);
        if (!context.mounted) {
          break;
        }
        if (confirmed == true) {
          await service.clearSession();
          if (context.mounted) {
            context.go(loginRoutePath);
          }
        }
        break;
      case _AccountDialogResult.none:
      case null:
        break;
    }
  }

  Future<bool?> _showMandatoryLogoutDialog(BuildContext context) {
    return SuperportDialog.show<bool>(
      context: context,
      barrierDismissible: false,
      dialog: SuperportDialog(
        title: '비밀번호 변경 완료',
        description: '비밀번호가 변경되었습니다. 다시 로그인해주세요.',
        showCloseButton: false,
        primaryAction: ShadButton(
          onPressed: () => Navigator.of(context, rootNavigator: true).pop(true),
          child: const Text('확인'),
        ),
      ),
    );
  }
}

enum _AccountDialogResult { none, logout, passwordChanged }

class _AccountDialog extends StatefulWidget {
  const _AccountDialog({
    required this.authService,
    required this.userRepository,
    required this.hostContext,
  });

  final AuthService authService;
  final UserRepository userRepository;
  final BuildContext hostContext;

  @override
  State<_AccountDialog> createState() => _AccountDialogState();
}

class _AccountDialogState extends State<_AccountDialog> {
  static final RegExp _emailRegExp = RegExp(r'^[^@\s]+@[^@\s]+\.[^@\s]+$');
  static final RegExp _phoneRegExp = RegExp(r'^[0-9+\-\s]{7,}$');

  late final TextEditingController _emailController;
  late final TextEditingController _phoneController;
  String? _emailError;
  String? _phoneError;
  String? _generalError;
  bool _isSaving = false;
  late String _initialEmail;
  late String _initialPhone;

  AuthSession? get _session => widget.authService.session;

  bool get _isDirty =>
      _emailController.text.trim() != _initialEmail ||
      _phoneController.text.trim() != _initialPhone;

  bool get _canEdit => _session != null;

  @override
  void initState() {
    super.initState();
    final session = _session;
    _initialEmail = session?.user.email ?? '';
    _initialPhone = session?.user.phone ?? '';
    _emailController = TextEditingController(text: _initialEmail)
      ..addListener(_handleChanged);
    _phoneController = TextEditingController(text: _initialPhone)
      ..addListener(_handleChanged);
  }

  @override
  void dispose() {
    _emailController.dispose();
    _phoneController.dispose();
    super.dispose();
  }

  void _handleChanged() {
    if ((_emailError != null || _phoneError != null) && mounted) {
      setState(() {
        _emailError = null;
        _phoneError = null;
        _generalError = null;
      });
    } else {
      setState(() {});
    }
  }

  @override
  Widget build(BuildContext context) {
    final session = _session;
    return _wrapWithWillPop(
      SuperportDialog(
        title: '내 정보',
        description: session == null
            ? '로그인 정보를 찾을 수 없습니다.'
            : '${session.user.name}님의 계정 정보를 확인하고 수정하세요.',
        scrollable: true,
        showCloseButton: false,
        footer: Padding(
          padding: const EdgeInsets.fromLTRB(20, 16, 20, 20),
          child: Row(
            mainAxisAlignment: MainAxisAlignment.end,
            children: [
              ShadButton.outline(
                onPressed: _isSaving
                    ? null
                    : () => Navigator.of(
                        context,
                        rootNavigator: true,
                      ).pop(_AccountDialogResult.none),
                child: const Text('닫기'),
              ),
              const SizedBox(width: 12),
              ShadButton.destructive(
                onPressed: !_canEdit || _isSaving
                    ? null
                    : () => Navigator.of(
                        context,
                        rootNavigator: true,
                      ).pop(_AccountDialogResult.logout),
                child: const Text('로그아웃'),
              ),
            ],
          ),
        ),
        child: _AccountDialogBody(
          session: session,
          emailController: _emailController,
          phoneController: _phoneController,
          emailError: _emailError,
          phoneError: _phoneError,
          generalError: _generalError,
          isSaving: _isSaving,
          canEdit: _canEdit,
          onSave: _saveProfile,
          onPasswordChange: _handlePasswordChange,
          canSave: _isDirty && !_isSaving && _canEdit,
        ),
      ),
    );
  }

  Widget _wrapWithWillPop(Widget child) {
    return PopScope(canPop: !_isSaving, child: child);
  }

  Future<void> _saveProfile() async {
    if (!_canEdit || _isSaving) {
      return;
    }

    final email = _emailController.text.trim();
    final phone = _phoneController.text.trim();
    var hasError = false;

    if (email.isEmpty || !_emailRegExp.hasMatch(email)) {
      _emailError = '올바른 이메일 주소를 입력하세요.';
      hasError = true;
    }
    if (phone.isEmpty || !_phoneRegExp.hasMatch(phone)) {
      _phoneError = '연락처는 숫자/+, -/공백만 사용해 7자 이상 입력하세요.';
      hasError = true;
    }

    if (hasError) {
      setState(() {});
      return;
    }

    setState(() {
      _isSaving = true;
      _generalError = null;
    });

    try {
      final result = await widget.userRepository.updateMe(
        UserProfileUpdateInput(email: email, phone: phone),
      );
      final updatedEmail = result.email ?? email;
      final updatedPhone = result.mobileNo ?? phone;

      _initialEmail = updatedEmail;
      _initialPhone = updatedPhone;

      if (_emailController.text != updatedEmail) {
        _emailController.text = updatedEmail;
      }
      if (_phoneController.text != updatedPhone) {
        _phoneController.text = updatedPhone;
      }

      final session = _session;
      if (session != null) {
        final updatedUser = session.user.copyWith(
          email: updatedEmail,
          phone: updatedPhone,
          name: result.employeeName,
          employeeNo: result.employeeNo,
        );
        widget.authService.updateSessionUser(updatedUser);
      }

      setState(() {
        _isSaving = false;
        _emailError = null;
        _phoneError = null;
      });
      _showSnack('프로필 정보를 저장했습니다.');
    } catch (error) {
      final failure = Failure.from(error);
      setState(() {
        _isSaving = false;
        _generalError = failure.describe();
      });
    }
  }

  Future<void> _handlePasswordChange() async {
    if (!_canEdit || _isSaving) {
      return;
    }
    final changed = await _PasswordChangeDialog.show(
      context: context,
      userRepository: widget.userRepository,
    );
    if (changed == true && mounted) {
      Navigator.of(
        context,
        rootNavigator: true,
      ).pop(_AccountDialogResult.passwordChanged);
    }
  }

  void _showSnack(String message) {
    final messenger = ScaffoldMessenger.maybeOf(widget.hostContext);
    messenger?.showSnackBar(SnackBar(content: Text(message)));
  }
}

class _AccountDialogBody extends StatelessWidget {
  const _AccountDialogBody({
    required this.session,
    required this.emailController,
    required this.phoneController,
    required this.emailError,
    required this.phoneError,
    required this.generalError,
    required this.isSaving,
    required this.canEdit,
    required this.onSave,
    required this.onPasswordChange,
    required this.canSave,
  });

  final AuthSession? session;
  final TextEditingController emailController;
  final TextEditingController phoneController;
  final String? emailError;
  final String? phoneError;
  final String? generalError;
  final bool isSaving;
  final bool canEdit;
  final bool canSave;
  final VoidCallback onSave;
  final VoidCallback onPasswordChange;

  @override
  Widget build(BuildContext context) {
    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      mainAxisSize: MainAxisSize.min,
      children: [
        _AccountInfoContent(session: session),
        const SizedBox(height: 24),
        Divider(color: Theme.of(context).colorScheme.outlineVariant),
        const SizedBox(height: 16),
        Text('연락처 / 이메일 수정', style: Theme.of(context).textTheme.titleSmall),
        const SizedBox(height: 12),
        _LabeledField(
          label: '이메일',
          controller: emailController,
          fieldKey: const ValueKey('account_email_field'),
          enabled: canEdit && !isSaving,
          keyboardType: TextInputType.emailAddress,
          errorText: emailError,
        ),
        const SizedBox(height: 12),
        _LabeledField(
          label: '연락처',
          controller: phoneController,
          fieldKey: const ValueKey('account_phone_field'),
          enabled: canEdit && !isSaving,
          keyboardType: TextInputType.phone,
          errorText: phoneError,
        ),
        if (generalError != null) ...[
          const SizedBox(height: 12),
          Text(
            generalError!,
            style: Theme.of(context).textTheme.bodySmall?.copyWith(
              color: Theme.of(context).colorScheme.error,
            ),
          ),
        ],
        const SizedBox(height: 16),
        Align(
          alignment: Alignment.centerRight,
          child: ShadButton(
            onPressed: canSave ? onSave : null,
            child: isSaving
                ? const SizedBox(
                    width: 16,
                    height: 16,
                    child: CircularProgressIndicator(strokeWidth: 2),
                  )
                : const Text('저장'),
          ),
        ),
        const SizedBox(height: 24),
        Divider(color: Theme.of(context).colorScheme.outlineVariant),
        const SizedBox(height: 16),
        Text('보안', style: Theme.of(context).textTheme.titleSmall),
        const SizedBox(height: 12),
        ShadButton.outline(
          onPressed: isSaving ? null : onPasswordChange,
          child: const Text('비밀번호 변경'),
        ),
      ],
    );
  }
}

class _LabeledField extends StatelessWidget {
  const _LabeledField({
    required this.label,
    required this.controller,
    this.fieldKey,
    this.enabled = true,
    this.keyboardType,
    this.errorText,
  });

  final String label;
  final TextEditingController controller;
  final Key? fieldKey;
  final bool enabled;
  final TextInputType? keyboardType;
  final String? errorText;

  @override
  Widget build(BuildContext context) {
    final theme = ShadTheme.of(context);
    final materialTheme = Theme.of(context);
    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        Text(label, style: theme.textTheme.small),
        const SizedBox(height: 6),
        ShadInput(
          key: fieldKey,
          controller: controller,
          enabled: enabled,
          keyboardType: keyboardType,
        ),
        if (errorText != null) ...[
          const SizedBox(height: 6),
          Text(
            errorText!,
            style: theme.textTheme.small.copyWith(
              color: materialTheme.colorScheme.error,
            ),
          ),
        ],
      ],
    );
  }
}

class _PasswordChangeDialog extends StatefulWidget {
  const _PasswordChangeDialog({required this.userRepository});

  final UserRepository userRepository;

  static Future<bool?> show({
    required BuildContext context,
    required UserRepository userRepository,
  }) {
    return showDialog<bool>(
      context: context,
      barrierDismissible: false,
      builder: (_) => _PasswordChangeDialog(userRepository: userRepository),
    );
  }

  @override
  State<_PasswordChangeDialog> createState() => _PasswordChangeDialogState();
}

class _PasswordChangeDialogState extends State<_PasswordChangeDialog> {
  final TextEditingController _currentController = TextEditingController();
  final TextEditingController _newController = TextEditingController();
  final TextEditingController _confirmController = TextEditingController();

  String? _currentError;
  String? _newError;
  String? _confirmError;
  String? _generalError;
  bool _isSaving = false;

  @override
  void dispose() {
    _currentController.dispose();
    _newController.dispose();
    _confirmController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return SuperportDialog(
      title: '비밀번호 변경',
      showCloseButton: !_isSaving,
      primaryAction: ShadButton(
        onPressed: _isSaving ? null : _handleSubmit,
        child: _isSaving
            ? const SizedBox(
                width: 16,
                height: 16,
                child: CircularProgressIndicator(strokeWidth: 2),
              )
            : const Text('변경'),
      ),
      secondaryAction: ShadButton.outline(
        onPressed: _isSaving
            ? null
            : () => Navigator.of(context, rootNavigator: true).pop(false),
        child: const Text('취소'),
      ),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        mainAxisSize: MainAxisSize.min,
        children: [
          _PasswordField(
            label: '현재 비밀번호',
            controller: _currentController,
            fieldKey: const ValueKey('account_current_password'),
            errorText: _currentError,
            enabled: !_isSaving,
          ),
          const SizedBox(height: 12),
          _PasswordField(
            label: '새 비밀번호',
            controller: _newController,
            fieldKey: const ValueKey('account_new_password'),
            errorText: _newError,
            enabled: !_isSaving,
            helper: '8~24자, 대문자/소문자/숫자/특수문자 각 1자 이상 포함',
          ),
          const SizedBox(height: 12),
          _PasswordField(
            label: '새 비밀번호 확인',
            controller: _confirmController,
            fieldKey: const ValueKey('account_confirm_password'),
            errorText: _confirmError,
            enabled: !_isSaving,
          ),
          if (_generalError != null) ...[
            const SizedBox(height: 12),
            Text(
              _generalError!,
              style: Theme.of(context).textTheme.bodySmall?.copyWith(
                color: Theme.of(context).colorScheme.error,
              ),
            ),
          ],
        ],
      ),
    );
  }

  Future<void> _handleSubmit() async {
    if (_isSaving) return;
    final current = _currentController.text.trim();
    final next = _newController.text.trim();
    final confirm = _confirmController.text.trim();

    var hasError = false;
    if (current.isEmpty) {
      _currentError = '현재 비밀번호를 입력하세요.';
      hasError = true;
    } else {
      _currentError = null;
    }
    if (!PasswordRules.isValid(next)) {
      _newError = '비밀번호 정책을 만족하도록 입력하세요.';
      hasError = true;
    } else {
      _newError = null;
    }
    if (next != confirm) {
      _confirmError = '새 비밀번호가 일치하지 않습니다.';
      hasError = true;
    } else {
      _confirmError = null;
    }

    if (hasError) {
      setState(() {});
      return;
    }

    setState(() {
      _isSaving = true;
      _generalError = null;
    });

    try {
      await widget.userRepository.updateMe(
        UserProfileUpdateInput(password: next, currentPassword: current),
      );
      if (mounted) {
        Navigator.of(context, rootNavigator: true).pop(true);
      }
    } catch (error) {
      final failure = Failure.from(error);
      setState(() {
        _generalError = failure.describe();
        _isSaving = false;
      });
    }
  }
}

class _PasswordField extends StatelessWidget {
  const _PasswordField({
    required this.label,
    required this.controller,
    this.helper,
    this.errorText,
    this.enabled = true,
    this.fieldKey,
  });

  final String label;
  final TextEditingController controller;
  final String? helper;
  final String? errorText;
  final bool enabled;
  final Key? fieldKey;

  @override
  Widget build(BuildContext context) {
    final theme = ShadTheme.of(context);
    final materialTheme = Theme.of(context);
    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        Text(label, style: theme.textTheme.small),
        const SizedBox(height: 6),
        ShadInput(
          key: fieldKey,
          controller: controller,
          enabled: enabled,
          obscureText: true,
        ),
        if (helper != null)
          Padding(
            padding: const EdgeInsets.only(top: 6),
            child: Text(helper!, style: theme.textTheme.muted),
          ),
        if (errorText != null)
          Padding(
            padding: const EdgeInsets.only(top: 6),
            child: Text(
              errorText!,
              style: theme.textTheme.small.copyWith(
                color: materialTheme.colorScheme.error,
              ),
            ),
          ),
      ],
    );
  }
}

/// 로그인된 계정의 핵심 정보를 보여주는 다이얼로그 본문.
class _AccountInfoContent extends StatelessWidget {
  const _AccountInfoContent({required this.session});

  final AuthSession? session;

  @override
  Widget build(BuildContext context) {
    final theme = Theme.of(context);
    final colorScheme = theme.colorScheme;

    if (session == null) {
      return Padding(
        padding: const EdgeInsets.symmetric(vertical: 24),
        child: Text(
          '로그인된 사용자 정보를 불러오지 못했습니다. 다시 로그인하면 세션이 갱신됩니다.',
          style: theme.textTheme.bodyMedium?.copyWith(color: colorScheme.error),
        ),
      );
    }

    final user = session!.user;
    final expiryLabel = _formatExpiry(session!.expiresAt);
    final permissionSummaries = _PermissionSummary.build(session!);

    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      mainAxisSize: MainAxisSize.min,
      children: [
        _AccountInfoRow(label: '이름', value: user.name),
        _AccountInfoRow(label: '사번', value: user.employeeNo ?? '-'),
        _AccountInfoRow(label: '이메일', value: user.email ?? '-'),
        _AccountInfoRow(label: '연락처', value: user.phone ?? '-'),
        _AccountInfoRow(label: '기본 그룹', value: user.primaryGroupName ?? '-'),
        _AccountInfoRow(label: '토큰 만료', value: expiryLabel),
        _AccountInfoRow(
          label: '권한 리소스',
          value: '${permissionSummaries.length}개',
        ),
        if (permissionSummaries.isNotEmpty) ...[
          const SizedBox(height: 16),
          Text(
            '권한 요약',
            style: theme.textTheme.titleSmall?.copyWith(
              fontWeight: FontWeight.w600,
            ),
          ),
          const SizedBox(height: 8),
          Wrap(
            spacing: 8,
            runSpacing: 8,
            children: [
              for (final summary in permissionSummaries.take(6))
                ShadBadge.outline(
                  child: Text(
                    '${summary.resource} · ${_formatActions(summary.actions)}',
                  ),
                ),
              if (permissionSummaries.length > 6)
                ShadBadge.outline(
                  child: Text('외 ${permissionSummaries.length - 6}개 리소스'),
                ),
            ],
          ),
        ],
      ],
    );
  }

  String _formatExpiry(DateTime? expiresAt) {
    if (expiresAt == null) {
      return '만료 정보 없음';
    }
    final formatter = DateFormat('yyyy-MM-dd HH:mm');
    return formatter.format(expiresAt.toLocal());
  }
}

/// 다이얼로그에서 라벨과 값을 한 줄로 표시한다.
class _AccountInfoRow extends StatelessWidget {
  const _AccountInfoRow({required this.label, required this.value});

  final String label;
  final String value;

  @override
  Widget build(BuildContext context) {
    final theme = Theme.of(context);
    final colorScheme = theme.colorScheme;

    return Padding(
      padding: const EdgeInsets.symmetric(vertical: 6),
      child: Row(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          SizedBox(
            width: 112,
            child: Text(
              label,
              style: theme.textTheme.bodySmall?.copyWith(
                fontWeight: FontWeight.w600,
                color: colorScheme.onSurfaceVariant,
              ),
            ),
          ),
          Expanded(
            child: Text(
              value.isEmpty ? '-' : value,
              style: theme.textTheme.bodyMedium,
            ),
          ),
        ],
      ),
    );
  }
}

/// 리소스별 권한 액션 목록 요약 데이터.
class _PermissionSummary {
  const _PermissionSummary({required this.resource, required this.actions});

  final String resource;
  final List<String> actions;

  static List<_PermissionSummary> build(AuthSession session) {
    final Map<String, Set<String>> aggregated = {};
    for (final permission in session.permissions) {
      final bucket = aggregated.putIfAbsent(
        permission.resource,
        () => <String>{},
      );
      for (final raw in permission.actions) {
        final normalized = raw.trim().toLowerCase();
        if (normalized.isEmpty) continue;
        bucket.add(normalized);
      }
    }
    final summaries =
        aggregated.entries
            .map(
              (entry) => _PermissionSummary(
                resource: entry.key,
                actions: entry.value.toList()..sort(),
              ),
            )
            .toList()
          ..sort((a, b) => a.resource.compareTo(b.resource));
    return summaries;
  }
}

String _formatActions(List<String> actions) {
  if (actions.isEmpty) {
    return '권한 없음';
  }
  final labels = actions.map(_koreanActionLabel).toList()..sort();
  return labels.join(', ');
}

String _koreanActionLabel(String action) {
  return switch (action.trim().toLowerCase()) {
    'view' => '조회',
    'read' => '조회',
    'create' => '생성',
    'edit' => '수정',
    'update' => '수정',
    'delete' => '삭제',
    'restore' => '복구',
    'approve' => '결재',
    _ => action,
  };
}
