import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';

import '../../../core/actions/contact_launcher.dart';
import '../../../core/network/api_error.dart';
import '../../auth/data/auth_repository.dart';
import '../../auth/domain/auth_models.dart';
import '../../auth/presentation/login_screen.dart';
import '../../favorites/data/favorites_repository.dart';
import '../../organization/data/organization_api_client.dart';
import '../../organization/domain/organization_models.dart';
import '../data/directory_repository.dart';
import '../data/profile_image_api_client.dart';
import '../domain/employee.dart';
import 'directory_navigation.dart';

class DirectoryScreen extends ConsumerStatefulWidget {
  const DirectoryScreen({super.key});

  static const routePath = '/directory';

  @override
  ConsumerState<DirectoryScreen> createState() => _DirectoryScreenState();
}

enum _DirectoryView { employees, organization, favorites }

const _syntheticPinnedDepartmentSlug = '__pinned_department__';

class _InitialDirectoryContext {
  const _InitialDirectoryContext({
    required this.companySlug,
    required this.departmentName,
  });

  final String companySlug;
  final String? departmentName;
}

final initialDirectoryContextProvider =
    FutureProvider.autoDispose<_InitialDirectoryContext>((ref) async {
      final session = await ref.watch(authRepositoryProvider).loadSession();
      if (session == null) {
        return const _InitialDirectoryContext(
          companySlug: 'all',
          departmentName: null,
        );
      }

      final companySlug = session.user.tenantSlug.trim();
      if (companySlug.isEmpty) {
        return _resolveInitialDirectoryContextFromOrgChart(
          ref,
          fallback: _InitialDirectoryContext(
            companySlug: 'all',
            departmentName: session.user.department,
          ),
          user: session.user,
        );
      }
      final context = _InitialDirectoryContext(
        companySlug: companySlug,
        departmentName: session.user.department,
      );
      if (_normalizedDepartmentName(context.departmentName) != null) {
        return context;
      }
      return _resolveInitialDirectoryContextFromOrgChart(
        ref,
        fallback: context,
        user: session.user,
      );
    });

Future<_InitialDirectoryContext> _resolveInitialDirectoryContextFromOrgChart(
  Ref ref, {
  required _InitialDirectoryContext fallback,
  required LoginUser user,
}) async {
  try {
    final snapshot = await ref
        .watch(organizationRepositoryProvider)
        .getOrgChart();
    final employee = _findCurrentEmployee(snapshot.employees, user);
    if (employee == null) {
      return fallback;
    }

    final employeeTenantSlug = employee.tenantSlug.trim();
    final employeeDepartment = _normalizedDepartmentName(employee.department);
    final employeeTenantName = _normalizedDepartmentName(employee.tenantName);
    return _InitialDirectoryContext(
      companySlug: employeeTenantSlug.isEmpty
          ? fallback.companySlug
          : employeeTenantSlug,
      departmentName:
          employeeDepartment ?? employeeTenantName ?? fallback.departmentName,
    );
  } catch (_) {
    return fallback;
  }
}

Employee? _findCurrentEmployee(List<Employee> employees, LoginUser user) {
  final userId = user.id.trim();
  if (userId.isNotEmpty) {
    for (final employee in employees) {
      if (employee.id.trim() == userId) {
        return employee;
      }
    }
  }

  final userPhone = _normalizedPhone(user.phoneNumber);
  if (userPhone.isNotEmpty) {
    for (final employee in employees) {
      if (_phonesMatch(_normalizedPhone(employee.phoneNumber), userPhone) ||
          _phonesMatch(_normalizedPhone(employee.phoneDisplay), userPhone)) {
        return employee;
      }
    }
  }

  final userName = user.name.trim();
  if (userName.isEmpty) {
    return null;
  }
  final matches = [
    for (final employee in employees)
      if (employee.name.trim() == userName) employee,
  ];
  return matches.length == 1 ? matches.single : null;
}

String _normalizedPhone(String? value) {
  var digits = (value ?? '').replaceAll(RegExp(r'[^0-9]'), '');
  if (digits.startsWith('82') && digits.length >= 11) {
    digits = '0${digits.substring(2)}';
  }
  return digits;
}

bool _phonesMatch(String employeePhone, String userPhone) {
  if (employeePhone.isEmpty || userPhone.isEmpty) {
    return false;
  }
  if (employeePhone == userPhone) {
    return true;
  }
  if (employeePhone.length >= 8 && userPhone.length >= 8) {
    return employeePhone.substring(employeePhone.length - 8) ==
        userPhone.substring(userPhone.length - 8);
  }
  return false;
}

class _DirectoryScreenState extends ConsumerState<DirectoryScreen> {
  final _searchController = TextEditingController();
  final _tenantChipScrollController = ScrollController();
  var _view = _DirectoryView.employees;
  String? _selectedTenantSlugOverride;
  var _hierarchyMode = false;
  var _query = '';

  @override
  void dispose() {
    _searchController.dispose();
    _tenantChipScrollController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    final initialContext = ref.watch(initialDirectoryContextProvider);
    return Scaffold(
      appBar: AppBar(
        title: const Text('직원검색'),
        actions: [
          IconButton(
            tooltip: '로그아웃',
            icon: const Icon(Icons.logout),
            onPressed: _logout,
          ),
        ],
      ),
      body: SafeArea(
        child: initialContext.when(
          loading: () => const _LoadingState(),
          error: (_, _) => _buildDirectoryContent(
            const _InitialDirectoryContext(
              companySlug: 'all',
              departmentName: null,
            ),
          ),
          data: _buildDirectoryContent,
        ),
      ),
    );
  }

  Widget _buildDirectoryContent(_InitialDirectoryContext initialContext) {
    final initialTenantSlug = initialContext.companySlug;
    final effectiveQuery = _effectiveSearchQuery(_query);
    final searchHelperText = _searchHelperText(_query);
    final pinnedDepartmentName = _normalizedDepartmentName(
      initialContext.departmentName,
    );
    final tenantsAsync = ref.watch(tenantListProvider);
    final tenants = tenantsAsync.asData?.value;
    final pinnedTenantSlug = tenants == null
        ? initialTenantSlug
        : resolvePinnedTenantSlug(
            tenants: tenants,
            companySlug: initialTenantSlug,
            departmentName: initialContext.departmentName,
          );
    final showSyntheticPinnedDepartmentChip =
        pinnedDepartmentName != null && pinnedTenantSlug == initialTenantSlug;
    final defaultSelectedTenantSlug = showSyntheticPinnedDepartmentChip
        ? _syntheticPinnedDepartmentSlug
        : pinnedTenantSlug == initialTenantSlug
        ? initialTenantSlug
        : pinnedTenantSlug;
    final selectedTenantSlug =
        _selectedTenantSlugOverride ?? defaultSelectedTenantSlug;
    final navigation = tenants == null
        ? null
        : buildTenantNavigationModel(
            tenants: tenants,
            selectedTenantSlug: selectedTenantSlug,
            pinnedTenantSlug: pinnedTenantSlug,
          );
    final directoryQuery = _buildDirectoryQuery(
      query: effectiveQuery,
      selectedTenantSlug: selectedTenantSlug,
      initialTenantSlug: initialTenantSlug,
      navigation: navigation,
      pinnedDepartmentName: pinnedDepartmentName,
    );
    final showsHierarchy =
        tenants != null &&
        _view == _DirectoryView.organization &&
        directoryQuery.query.trim().isEmpty &&
        _hierarchyMode &&
        navigation != null &&
        navigation.hasChildren;
    final shouldLoadEmployees =
        tenants != null &&
        (!showsHierarchy || navigation.selectedTenant != null);
    final employeesAsync = shouldLoadEmployees
        ? ref.watch(directoryEmployeesProvider(directoryQuery))
        : const AsyncValue<List<Employee>>.data([]);
    final favoriteIds = ref.watch(favoriteEmployeeIdsProvider).value ?? {};
    return Padding(
      padding: const EdgeInsets.all(16),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.stretch,
        children: [
          _SearchField(
            controller: _searchController,
            helperText: searchHelperText,
            onChanged: (value) => setState(() => _query = value),
          ),
          const SizedBox(height: 12),
          Expanded(
            child: _buildAsyncDirectorySection(
              directoryQuery: directoryQuery,
              effectiveQuery: effectiveQuery,
              employeesAsync: employeesAsync,
              tenantsAsync: tenantsAsync,
              favoriteIds: favoriteIds,
              initialTenantSlug: initialTenantSlug,
              selectedTenantSlug: selectedTenantSlug,
              pinnedTenantSlug: pinnedTenantSlug,
              pinnedDepartmentName: pinnedDepartmentName,
              showSyntheticPinnedDepartmentChip:
                  showSyntheticPinnedDepartmentChip,
              navigation: navigation,
            ),
          ),
        ],
      ),
    );
  }

  Widget _buildAsyncDirectorySection({
    required DirectoryQuery directoryQuery,
    required String effectiveQuery,
    required AsyncValue<List<Employee>> employeesAsync,
    required AsyncValue<List<TenantSummary>> tenantsAsync,
    required Set<String> favoriteIds,
    required String initialTenantSlug,
    required String selectedTenantSlug,
    required String pinnedTenantSlug,
    required String? pinnedDepartmentName,
    required bool showSyntheticPinnedDepartmentChip,
    required TenantNavigationModel? navigation,
  }) {
    final employees = employeesAsync.asData?.value;
    final tenants = tenantsAsync.asData?.value;
    final firstError =
        employeesAsync.whenOrNull(error: (error, _) => error) ??
        tenantsAsync.whenOrNull(error: (error, _) => error);

    if (firstError != null) {
      return _ErrorState(
        message: _errorMessage(firstError),
        onRetry: () {
          ref.invalidate(directoryEmployeesProvider(directoryQuery));
          ref.invalidate(tenantListProvider);
        },
        onRelogin: _requiresRelogin(firstError) ? _relogin : null,
      );
    }

    if (employees == null || tenants == null) {
      return const _LoadingState();
    }

    final visibleEmployees = _visibleEmployees(
      employees,
      favoriteIds,
      effectiveQuery,
    );
    final resolvedNavigation =
        navigation ??
        buildTenantNavigationModel(
          tenants: tenants,
          selectedTenantSlug: selectedTenantSlug,
          pinnedTenantSlug: pinnedTenantSlug,
        );
    final showsHierarchy =
        _view == _DirectoryView.organization &&
        directoryQuery.query.trim().isEmpty &&
        _hierarchyMode &&
        resolvedNavigation.hasChildren;
    final directHierarchyEmployees = showsHierarchy
        ? _directEmployeesForSelectedTenant(
            visibleEmployees,
            resolvedNavigation.selectedTenant,
          )
        : const <Employee>[];
    return Column(
      crossAxisAlignment: CrossAxisAlignment.stretch,
      children: [
        _TenantNavigationBar(
          scrollController: _tenantChipScrollController,
          navigation: resolvedNavigation,
          selectedTenantSlug: selectedTenantSlug,
          pinnedDepartmentName: pinnedDepartmentName,
          showSyntheticPinnedDepartmentChip: showSyntheticPinnedDepartmentChip,
          onSelected: (slug) => _onTenantSelected(
            slug: slug,
            initialTenantSlug: initialTenantSlug,
            enterHierarchy:
                slug == 'all' || slug != initialTenantSlug || _hierarchyMode,
          ),
        ),
        if (resolvedNavigation.breadcrumbTenants.isNotEmpty) ...[
          const SizedBox(height: 8),
          const Divider(height: 1),
          const SizedBox(height: 8),
          _BreadcrumbBar(
            breadcrumbs: resolvedNavigation.breadcrumbTenants,
            selectedTenantSlug: selectedTenantSlug,
            onSelected: (slug) => _onTenantSelected(
              slug: slug,
              initialTenantSlug: initialTenantSlug,
              enterHierarchy: true,
            ),
          ),
        ],
        const SizedBox(height: 8),
        const _SectionDividerMarker(),
        const SizedBox(height: 12),
        _ViewSelector(
          selected: _view,
          onSelected: (value) => setState(() {
            _view = value;
            if (value != _DirectoryView.organization) {
              _hierarchyMode = false;
            }
          }),
        ),
        const SizedBox(height: 16),
        _SummaryRow(
          summaryText: showsHierarchy
              ? _hierarchySummaryText(
                  resolvedNavigation,
                  directEmployeeCount: directHierarchyEmployees.length,
                )
              : '검색 결과 ${visibleEmployees.length}명',
          favoritesCount: favoriteIds.length,
        ),
        const SizedBox(height: 8),
        Expanded(
          child: _DirectoryResultList(
            employees: visibleEmployees,
            directEmployees: directHierarchyEmployees,
            directSectionTitle: resolvedNavigation.selectedTenant == null
                ? null
                : '${resolvedNavigation.selectedTenant!.name} 조직 관리자',
            favoriteIds: favoriteIds,
            showsHierarchy: showsHierarchy,
            childTenants: resolvedNavigation.childTenants,
            view: _view,
            onTenantSelected: (slug) => _onTenantSelected(
              slug: slug,
              initialTenantSlug: initialTenantSlug,
              enterHierarchy: true,
            ),
            onFavoriteToggle: _toggleFavorite,
            onEmployeeTap: (employee) =>
                _showEmployeeDetail(employee, favoriteIds),
          ),
        ),
      ],
    );
  }

  List<Employee> _visibleEmployees(
    List<Employee> employees,
    Set<String> favoriteIds,
    String effectiveQuery,
  ) {
    final visible = employees.where((employee) {
      final matchesFavorites =
          _view != _DirectoryView.favorites ||
          favoriteIds.contains(employee.id);
      final matchesQuery =
          effectiveQuery.trim().isEmpty ||
          _matchesEmployeeSearch(employee, effectiveQuery);
      return matchesFavorites && matchesQuery;
    }).toList();
    visible.sort(_compareEmployeesForOrganization);
    return visible;
  }

  List<Employee> _directEmployeesForSelectedTenant(
    List<Employee> employees,
    TenantSummary? selectedTenant,
  ) {
    if (selectedTenant == null) {
      return const <Employee>[];
    }
    final employeeIdsWithOtherMemberships = employees
        .where((employee) => employee.tenantSlug != selectedTenant.slug)
        .map((employee) => employee.id)
        .where((id) => id.trim().isNotEmpty)
        .toSet();
    final directEmployees = employees.where((employee) {
      if (employee.tenantSlug != selectedTenant.slug) {
        return false;
      }
      if (employeeIdsWithOtherMemberships.contains(employee.id)) {
        return false;
      }
      final department = employee.department?.trim();
      return department == null ||
          department.isEmpty ||
          department == selectedTenant.name;
    }).toList();
    directEmployees.sort(_compareEmployeesForOrganization);
    return directEmployees;
  }

  Future<void> _toggleFavorite(String employeeId) async {
    final messenger = ScaffoldMessenger.of(context);
    try {
      await ref.read(favoritesRepositoryProvider).toggleFavorite(employeeId);
      ref.invalidate(favoriteEmployeeIdsProvider);
    } catch (_) {
      if (!mounted) {
        return;
      }
      messenger.showSnackBar(
        const SnackBar(content: Text('즐겨찾기를 저장할 수 없습니다.')),
      );
    }
  }

  bool _requiresRelogin(Object error) {
    return error is ApiException &&
        (error.statusCode == 401 || error.statusCode == 403);
  }

  String _errorMessage(Object error) {
    if (_requiresRelogin(error)) {
      return '로그인이 만료되었거나 권한을 확인할 수 없습니다.';
    }
    return '직원 정보를 불러오지 못했습니다.';
  }

  Future<void> _relogin() async {
    await ref.read(authRepositoryProvider).logout();
    if (!mounted) {
      return;
    }
    context.go(LoginScreen.routePath);
  }

  Future<void> _logout() async {
    await ref.read(authRepositoryProvider).logout();
    if (!mounted) {
      return;
    }
    context.go(LoginScreen.routePath);
  }

  void _showEmployeeDetail(Employee employee, Set<String> favoriteIds) {
    showModalBottomSheet<void>(
      context: context,
      showDragHandle: true,
      builder: (context) => _EmployeeDetailSheet(
        employee: employee,
        favoriteIds: favoriteIds,
        onFavoriteToggle: _toggleFavorite,
        onContactAction: _launchContactAction,
      ),
    );
  }

  Future<void> _launchContactAction(
    BuildContext context,
    Future<bool> action,
  ) async {
    final messenger = ScaffoldMessenger.of(context);
    final launched = await action;
    if (!mounted || launched) {
      return;
    }
    messenger.showSnackBar(const SnackBar(content: Text('연락 기능을 실행할 수 없습니다.')));
  }

  void _onTenantSelected({
    required String slug,
    required String initialTenantSlug,
    required bool enterHierarchy,
  }) {
    setState(() {
      _selectedTenantSlugOverride = slug;
      if (slug == 'all') {
        _hierarchyMode = true;
      } else if (slug == _syntheticPinnedDepartmentSlug) {
        _hierarchyMode = false;
      } else {
        _hierarchyMode = enterHierarchy;
      }
    });
  }

  DirectoryQuery _buildDirectoryQuery({
    required String query,
    required String selectedTenantSlug,
    required String initialTenantSlug,
    required TenantNavigationModel? navigation,
    required String? pinnedDepartmentName,
  }) {
    if (selectedTenantSlug == _syntheticPinnedDepartmentSlug) {
      return DirectoryQuery(
        query: query,
        tenantSlug: initialTenantSlug,
        department: pinnedDepartmentName,
      );
    }
    final selectedTenant = navigation?.selectedTenant;
    if (selectedTenant == null &&
        selectedTenantSlug != 'all' &&
        pinnedDepartmentName != null) {
      return DirectoryQuery(
        query: query,
        tenantSlug: initialTenantSlug,
        department: pinnedDepartmentName,
      );
    }
    if (selectedTenant != null) {
      return DirectoryQuery(
        query: query,
        tenantSlug: selectedTenant.slug,
        tenantSlugs:
            navigation?.selectedTenantSubtreeSlugs ?? [selectedTenant.slug],
      );
    }
    return DirectoryQuery(query: query, tenantSlug: selectedTenantSlug);
  }
}

String? _normalizedDepartmentName(String? value) {
  final normalized = value?.trim() ?? '';
  return normalized.isEmpty ? null : normalized;
}

String _hierarchySummaryText(
  TenantNavigationModel navigation, {
  required int directEmployeeCount,
}) {
  final directText = directEmployeeCount > 0
      ? ' · 조직 관리자 $directEmployeeCount명'
      : '';
  if (navigation.selectedTenant == null) {
    return '가족사 ${navigation.childTenants.length}개$directText';
  }
  return '하위 조직 ${navigation.childTenants.length}개$directText';
}

class _SearchField extends StatelessWidget {
  const _SearchField({
    required this.controller,
    required this.onChanged,
    this.helperText,
  });

  final TextEditingController controller;
  final ValueChanged<String> onChanged;
  final String? helperText;

  @override
  Widget build(BuildContext context) {
    return TextField(
      controller: controller,
      onChanged: onChanged,
      keyboardType: TextInputType.text,
      textInputAction: TextInputAction.search,
      autocorrect: false,
      enableSuggestions: false,
      smartDashesType: SmartDashesType.disabled,
      smartQuotesType: SmartQuotesType.disabled,
      decoration: InputDecoration(
        border: OutlineInputBorder(),
        hintText: '직원명 또는 전화번호 검색',
        helperText: helperText,
        prefixIcon: const Icon(Icons.search),
      ),
    );
  }
}

class _TenantNavigationBar extends StatelessWidget {
  const _TenantNavigationBar({
    required this.scrollController,
    required this.navigation,
    required this.selectedTenantSlug,
    required this.pinnedDepartmentName,
    required this.showSyntheticPinnedDepartmentChip,
    required this.onSelected,
  });

  final ScrollController scrollController;
  final TenantNavigationModel navigation;
  final String selectedTenantSlug;
  final String? pinnedDepartmentName;
  final bool showSyntheticPinnedDepartmentChip;
  final ValueChanged<String> onSelected;

  @override
  Widget build(BuildContext context) {
    final remainingChips = [
      for (final tenant in navigation.visibleChips)
        if (tenant.slug != navigation.pinnedTenant?.slug) tenant,
    ];
    final syntheticPinnedLabel = showSyntheticPinnedDepartmentChip
        ? pinnedDepartmentName
        : null;
    final showPinnedTenantChip =
        navigation.pinnedTenant != null &&
        navigation.pinnedTenant!.type != 'COMPANY' &&
        _normalizedChipLabel(navigation.pinnedTenant!.name) !=
            _normalizedChipLabel(syntheticPinnedLabel);
    return Scrollbar(
      controller: scrollController,
      thumbVisibility: true,
      scrollbarOrientation: ScrollbarOrientation.bottom,
      child: SingleChildScrollView(
        controller: scrollController,
        scrollDirection: Axis.horizontal,
        child: Row(
          children: [
            if (showPinnedTenantChip)
              _buildNavigationChip(
                context,
                label: navigation.pinnedTenant!.name,
                selected:
                    navigation.selectedTenant?.slug ==
                    navigation.pinnedTenant!.slug,
                onTap: () => onSelected(navigation.pinnedTenant!.slug),
              ),
            if (syntheticPinnedLabel != null)
              _buildNavigationChip(
                context,
                label: syntheticPinnedLabel,
                selected: selectedTenantSlug == _syntheticPinnedDepartmentSlug,
                onTap: () => onSelected(_syntheticPinnedDepartmentSlug),
              ),
            _buildNavigationChip(
              context,
              label: '전체',
              selected:
                  navigation.selectedTenant == null &&
                  selectedTenantSlug != _syntheticPinnedDepartmentSlug,
              onTap: () => onSelected('all'),
            ),
            for (final tenant in remainingChips)
              _buildNavigationChip(
                context,
                label: tenant.name,
                selected: navigation.selectedTenant?.slug == tenant.slug,
                onTap: () => onSelected(tenant.slug),
              ),
          ],
        ),
      ),
    );
  }

  Widget _buildNavigationChip(
    BuildContext context, {
    required String label,
    required bool selected,
    required VoidCallback onTap,
  }) {
    final colorScheme = Theme.of(context).colorScheme;
    return Padding(
      padding: const EdgeInsets.only(right: 8, bottom: 8),
      child: FilterChip(
        showCheckmark: false,
        label: Text(
          label,
          style: TextStyle(
            color: selected ? colorScheme.onPrimaryContainer : null,
            fontWeight: selected ? FontWeight.w700 : FontWeight.w500,
          ),
        ),
        selected: selected,
        selectedColor: colorScheme.primaryContainer,
        backgroundColor: colorScheme.surface,
        side: BorderSide(
          color: selected ? colorScheme.primary : colorScheme.outlineVariant,
          width: selected ? 1.5 : 1,
        ),
        onSelected: (_) => onTap(),
      ),
    );
  }
}

String _normalizedChipLabel(String? value) {
  return (value ?? '').trim().toLowerCase();
}

class _BreadcrumbBar extends StatelessWidget {
  const _BreadcrumbBar({
    required this.breadcrumbs,
    required this.selectedTenantSlug,
    required this.onSelected,
  });

  final List<TenantSummary> breadcrumbs;
  final String selectedTenantSlug;
  final ValueChanged<String> onSelected;

  @override
  Widget build(BuildContext context) {
    return Wrap(
      spacing: 8,
      runSpacing: 8,
      children: [
        for (final tenant in breadcrumbs)
          FilterChip(
            showCheckmark: false,
            avatar: Icon(
              tenant.slug == selectedTenantSlug
                  ? Icons.check_circle
                  : Icons.chevron_right,
              size: 18,
              color: tenant.slug == selectedTenantSlug
                  ? Theme.of(context).colorScheme.primary
                  : null,
            ),
            label: Text(tenant.name),
            selected: tenant.slug == selectedTenantSlug,
            selectedColor: Theme.of(context).colorScheme.primaryContainer,
            side: BorderSide(
              color: tenant.slug == selectedTenantSlug
                  ? Theme.of(context).colorScheme.primary
                  : Theme.of(context).colorScheme.outlineVariant,
            ),
            onSelected: (_) => onSelected(tenant.slug),
          ),
      ],
    );
  }
}

class _ViewSelector extends StatelessWidget {
  const _ViewSelector({required this.selected, required this.onSelected});

  final _DirectoryView selected;
  final ValueChanged<_DirectoryView> onSelected;

  @override
  Widget build(BuildContext context) {
    return SegmentedButton<_DirectoryView>(
      segments: const [
        ButtonSegment(
          value: _DirectoryView.employees,
          icon: Icon(Icons.people),
          label: Text('직원'),
        ),
        ButtonSegment(
          value: _DirectoryView.organization,
          icon: Icon(Icons.account_tree),
          label: Text('조직도'),
        ),
        ButtonSegment(
          value: _DirectoryView.favorites,
          icon: Icon(Icons.star),
          label: Text('즐겨찾기'),
        ),
      ],
      selected: {selected},
      onSelectionChanged: (values) => onSelected(values.first),
    );
  }
}

class _SectionDividerMarker extends StatelessWidget {
  const _SectionDividerMarker();

  @override
  Widget build(BuildContext context) {
    final colorScheme = Theme.of(context).colorScheme;
    return Divider(height: 1, thickness: 1, color: colorScheme.outlineVariant);
  }
}

class _SummaryRow extends StatelessWidget {
  const _SummaryRow({required this.summaryText, required this.favoritesCount});

  final String summaryText;
  final int favoritesCount;

  @override
  Widget build(BuildContext context) {
    return Row(
      children: [
        Expanded(child: Text(summaryText)),
        Icon(
          Icons.star,
          size: 18,
          color: Theme.of(context).colorScheme.primary,
        ),
        const SizedBox(width: 4),
        Text('즐겨찾기 $favoritesCount명'),
      ],
    );
  }
}

class _DirectoryResultList extends StatelessWidget {
  const _DirectoryResultList({
    required this.employees,
    required this.directEmployees,
    required this.directSectionTitle,
    required this.favoriteIds,
    required this.showsHierarchy,
    required this.childTenants,
    required this.view,
    required this.onTenantSelected,
    required this.onFavoriteToggle,
    required this.onEmployeeTap,
  });

  final List<Employee> employees;
  final List<Employee> directEmployees;
  final String? directSectionTitle;
  final Set<String> favoriteIds;
  final bool showsHierarchy;
  final List<TenantSummary> childTenants;
  final _DirectoryView view;
  final ValueChanged<String> onTenantSelected;
  final ValueChanged<String> onFavoriteToggle;
  final ValueChanged<Employee> onEmployeeTap;

  @override
  Widget build(BuildContext context) {
    if (showsHierarchy) {
      return ListView(
        padding: const EdgeInsets.only(bottom: 16),
        children: [
          if (directEmployees.isNotEmpty) ...[
            _DirectEmployeeSection(
              employees: directEmployees,
              title: directSectionTitle ?? '조직 관리자',
              favoriteIds: favoriteIds,
              onFavoriteToggle: onFavoriteToggle,
              onEmployeeTap: onEmployeeTap,
            ),
            const SizedBox(height: 12),
          ],
          _HierarchySection(
            tenants: childTenants,
            onSelected: onTenantSelected,
          ),
        ],
      );
    }

    if (view == _DirectoryView.organization) {
      return ListView(
        padding: const EdgeInsets.only(bottom: 16),
        children: [
          _OrganizationSection(
            employees: employees,
            favoriteIds: favoriteIds,
            onFavoriteToggle: onFavoriteToggle,
            onEmployeeTap: onEmployeeTap,
          ),
        ],
      );
    }

    if (employees.isEmpty) {
      return const _EmptyState();
    }

    return ListView.builder(
      padding: const EdgeInsets.only(bottom: 16),
      itemCount: employees.length,
      itemBuilder: (context, index) {
        final employee = employees[index];
        return _EmployeeTile(
          employee: employee,
          isFavorite: favoriteIds.contains(employee.id),
          onFavoriteToggle: () => onFavoriteToggle(employee.id),
          onTap: () => onEmployeeTap(employee),
        );
      },
    );
  }
}

class _DirectEmployeeSection extends StatelessWidget {
  const _DirectEmployeeSection({
    required this.employees,
    required this.title,
    required this.favoriteIds,
    required this.onFavoriteToggle,
    required this.onEmployeeTap,
  });

  final List<Employee> employees;
  final String title;
  final Set<String> favoriteIds;
  final ValueChanged<String> onFavoriteToggle;
  final ValueChanged<Employee> onEmployeeTap;

  @override
  Widget build(BuildContext context) {
    return Card(
      child: ExpansionTile(
        initiallyExpanded: true,
        leading: const Icon(Icons.badge_outlined),
        title: Text(title),
        subtitle: Text('${employees.length}명'),
        children: [
          for (final employee in employees)
            ListTile(
              leading: _EmployeeAvatar(employee: employee, radius: 16),
              title: Text(employee.name),
              subtitle: Text(_employeeTileSubtitle(employee)),
              trailing: IconButton(
                tooltip: favoriteIds.contains(employee.id)
                    ? '즐겨찾기 해제'
                    : '즐겨찾기 추가',
                onPressed: () => onFavoriteToggle(employee.id),
                icon: Icon(
                  favoriteIds.contains(employee.id)
                      ? Icons.star
                      : Icons.star_border,
                ),
              ),
              onTap: () => onEmployeeTap(employee),
            ),
        ],
      ),
    );
  }
}

class _HierarchySection extends StatelessWidget {
  const _HierarchySection({required this.tenants, required this.onSelected});

  final List<TenantSummary> tenants;
  final ValueChanged<String> onSelected;

  @override
  Widget build(BuildContext context) {
    if (tenants.isEmpty) {
      return const _EmptyState();
    }
    return Column(
      children: [
        for (final tenant in tenants)
          Card(
            child: ListTile(
              leading: Icon(
                tenant.type == 'COMPANY' ? Icons.business : Icons.folder_open,
              ),
              title: Text(tenant.name),
              subtitle: Text('${tenant.totalMemberCount}명'),
              trailing: const Icon(Icons.chevron_right),
              onTap: () => onSelected(tenant.slug),
            ),
          ),
      ],
    );
  }
}

class _EmployeeTile extends StatelessWidget {
  const _EmployeeTile({
    required this.employee,
    required this.isFavorite,
    required this.onFavoriteToggle,
    required this.onTap,
  });

  final Employee employee;
  final bool isFavorite;
  final VoidCallback onFavoriteToggle;
  final VoidCallback onTap;

  @override
  Widget build(BuildContext context) {
    return Card(
      child: ListTile(
        leading: _EmployeeAvatar(employee: employee),
        title: Text(employee.name),
        subtitle: Text(_employeeTileSubtitle(employee)),
        trailing: IconButton(
          tooltip: isFavorite ? '즐겨찾기 해제' : '즐겨찾기 추가',
          onPressed: onFavoriteToggle,
          icon: Icon(isFavorite ? Icons.star : Icons.star_border),
        ),
        onTap: onTap,
      ),
    );
  }
}

String _employeeAffiliationText(Employee employee) {
  final department = employee.department?.trim();
  if (department == null || department.isEmpty) {
    return employee.tenantName;
  }
  if (department == employee.tenantName.trim()) {
    return employee.tenantName;
  }
  return '${employee.tenantName} · $department';
}

String _employeeTileSubtitle(Employee employee) {
  final parts = <String>[
    _employeeAffiliationText(employee),
    if (employee.position != null && employee.position!.trim().isNotEmpty)
      employee.position!.trim(),
    if (employee.phoneDisplay != null &&
        employee.phoneDisplay!.trim().isNotEmpty)
      employee.phoneDisplay!.trim(),
  ];
  return parts.join(' · ');
}

class _OrganizationSection extends StatelessWidget {
  const _OrganizationSection({
    required this.employees,
    required this.favoriteIds,
    required this.onFavoriteToggle,
    required this.onEmployeeTap,
  });

  final List<Employee> employees;
  final Set<String> favoriteIds;
  final ValueChanged<String> onFavoriteToggle;
  final ValueChanged<Employee> onEmployeeTap;

  @override
  Widget build(BuildContext context) {
    final tenantNames =
        employees.map((employee) => employee.tenantName).toSet().toList()
          ..sort();
    if (employees.isEmpty) {
      return const _EmptyState();
    }
    return Column(
      children: [
        for (final tenantName in tenantNames)
          _OrganizationGroup(
            tenantName: tenantName,
            employees: employees
                .where((employee) => employee.tenantName == tenantName)
                .toList(),
            favoriteIds: favoriteIds,
            onFavoriteToggle: onFavoriteToggle,
            onEmployeeTap: onEmployeeTap,
          ),
      ],
    );
  }
}

class _OrganizationGroup extends StatelessWidget {
  const _OrganizationGroup({
    required this.tenantName,
    required this.employees,
    required this.favoriteIds,
    required this.onFavoriteToggle,
    required this.onEmployeeTap,
  });

  final String tenantName;
  final List<Employee> employees;
  final Set<String> favoriteIds;
  final ValueChanged<String> onFavoriteToggle;
  final ValueChanged<Employee> onEmployeeTap;

  @override
  Widget build(BuildContext context) {
    final departments = employees
        .map((employee) => employee.department ?? '미지정')
        .toSet()
        .toList();
    return Card(
      child: ExpansionTile(
        initiallyExpanded: true,
        leading: const Icon(Icons.account_tree),
        title: Text(tenantName),
        subtitle: Text('${employees.length}명'),
        children: [
          for (final department in _sortDepartments(departments))
            ExpansionTile(
              initiallyExpanded: true,
              title: Text(department),
              children: [
                for (final employee in _sortedEmployeesForDepartment(
                  employees,
                  department,
                ))
                  ListTile(
                    leading: _EmployeeAvatar(employee: employee, radius: 16),
                    title: Text(employee.name),
                    subtitle: Text(_roleText(employee)),
                    trailing: IconButton(
                      tooltip: favoriteIds.contains(employee.id)
                          ? '즐겨찾기 해제'
                          : '즐겨찾기 추가',
                      onPressed: () => onFavoriteToggle(employee.id),
                      icon: Icon(
                        favoriteIds.contains(employee.id)
                            ? Icons.star
                            : Icons.star_border,
                      ),
                    ),
                    onTap: () => onEmployeeTap(employee),
                  ),
              ],
            ),
        ],
      ),
    );
  }
}

class _EmployeeAvatar extends ConsumerStatefulWidget {
  const _EmployeeAvatar({required this.employee, this.radius = 20});

  final Employee employee;
  final double radius;

  @override
  ConsumerState<_EmployeeAvatar> createState() => _EmployeeAvatarState();
}

class _EmployeeAvatarState extends ConsumerState<_EmployeeAvatar> {
  late Future<String?> _imageUrlFuture;

  @override
  void initState() {
    super.initState();
    _imageUrlFuture = _resolveImageUrl();
  }

  @override
  void didUpdateWidget(covariant _EmployeeAvatar oldWidget) {
    super.didUpdateWidget(oldWidget);
    if (_avatarIdentity(oldWidget.employee) != _avatarIdentity(widget.employee)) {
      _imageUrlFuture = _resolveImageUrl();
    }
  }

  Future<String?> _resolveImageUrl() {
    return ref.read(profileImageApiClientProvider).resolveImageUrl(widget.employee);
  }

  String _avatarIdentity(Employee employee) {
    return [
      employee.id,
      employee.email ?? '',
      employee.tenantSlug,
      employee.tenantName,
      employee.profileImageUrl ?? '',
    ].join('|');
  }

  @override
  Widget build(BuildContext context) {
    final fallbackText = widget.employee.name.trim().isEmpty
        ? '?'
        : widget.employee.name.characters.first;

    return FutureBuilder<String?>(
      future: _imageUrlFuture,
      builder: (context, snapshot) {
        final imageUrl = snapshot.data?.trim() ?? '';
        return CircleAvatar(
          radius: widget.radius,
          foregroundImage: imageUrl.isEmpty ? null : NetworkImage(imageUrl),
          child: Text(fallbackText),
        );
      },
    );
  }
}

class _EmployeeDetailSheet extends ConsumerStatefulWidget {
  const _EmployeeDetailSheet({
    required this.employee,
    required this.favoriteIds,
    required this.onFavoriteToggle,
    required this.onContactAction,
  });

  final Employee employee;
  final Set<String> favoriteIds;
  final ValueChanged<String> onFavoriteToggle;
  final Future<void> Function(BuildContext context, Future<bool> action)
  onContactAction;

  @override
  ConsumerState<_EmployeeDetailSheet> createState() =>
      _EmployeeDetailSheetState();
}

class _EmployeeDetailSheetState extends ConsumerState<_EmployeeDetailSheet> {
  late Future<EmployeeDetail> _detailFuture;

  @override
  void initState() {
    super.initState();
    _detailFuture = _loadDetail();
  }

  Future<EmployeeDetail> _loadDetail() {
    return ref
        .read(employeeDetailRepositoryProvider)
        .loadEmployeeDetail(widget.employee.id);
  }

  @override
  Widget build(BuildContext context) {
    final launcher = ref.read(contactLauncherProvider);
    return FutureBuilder<EmployeeDetail>(
      future: _detailFuture,
      builder: (context, snapshot) {
        final detail = snapshot.data;
        final employee = detail ?? widget.employee;
        final hasPhone = employee.phoneNumber.trim().isNotEmpty;
        final canCall = hasPhone && (detail?.actions.call ?? true);
        final canSms = hasPhone && (detail?.actions.sms ?? true);
        final joinedTenants = detail == null
            ? ''
            : detail.joinedTenants
                  .map((tenant) => tenant.name.trim())
                  .where((name) => name.isNotEmpty)
                  .toSet()
                  .join(', ');

        return SafeArea(
          child: SingleChildScrollView(
            child: Padding(
              padding: const EdgeInsets.fromLTRB(20, 8, 20, 24),
              child: Column(
                mainAxisSize: MainAxisSize.min,
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  Row(
                    children: [
                      _EmployeeAvatar(employee: employee, radius: 22),
                      const SizedBox(width: 12),
                      Expanded(
                        child: Column(
                          crossAxisAlignment: CrossAxisAlignment.start,
                          children: [
                            Text(
                              employee.name,
                              style: Theme.of(context).textTheme.titleLarge,
                            ),
                            Text(
                              _employeeAffiliationText(employee),
                              style: Theme.of(context).textTheme.bodyMedium,
                            ),
                          ],
                        ),
                      ),
                      IconButton(
                        tooltip: widget.favoriteIds.contains(employee.id)
                            ? '즐겨찾기 해제'
                            : '즐겨찾기 추가',
                        onPressed: () {
                          Navigator.of(context).pop();
                          widget.onFavoriteToggle(employee.id);
                        },
                        icon: Icon(
                          widget.favoriteIds.contains(employee.id)
                              ? Icons.star
                              : Icons.star_border,
                        ),
                      ),
                    ],
                  ),
                  if (snapshot.connectionState == ConnectionState.waiting) ...[
                    const SizedBox(height: 12),
                    const LinearProgressIndicator(),
                  ],
                  const SizedBox(height: 16),
                  _DetailLine(label: '전화번호', value: employee.phoneDisplay ?? '-'),
                  _DetailLine(label: '이메일', value: employee.email ?? '-'),
                  _DetailLine(label: '직급/직위', value: _roleText(employee)),
                  _DetailLine(label: '직무', value: employee.jobTitle ?? '-'),
                  if (joinedTenants.isNotEmpty)
                    _DetailLine(label: '소속', value: joinedTenants),
                  const SizedBox(height: 16),
                  Row(
                    children: [
                      Expanded(
                        child: FilledButton.icon(
                          onPressed: canCall
                              ? () => widget.onContactAction(
                                  context,
                                  launcher.call(employee.phoneNumber),
                                )
                              : null,
                          icon: const Icon(Icons.call),
                          label: const Text('전화'),
                        ),
                      ),
                      const SizedBox(width: 8),
                      Expanded(
                        child: OutlinedButton.icon(
                          onPressed: canSms
                              ? () => widget.onContactAction(
                                  context,
                                  launcher.sms(employee.phoneNumber),
                                )
                              : null,
                          icon: const Icon(Icons.sms),
                          label: const Text('문자'),
                        ),
                      ),
                    ],
                  ),
                ],
              ),
            ),
          ),
        );
      },
    );
  }
}

class _DetailLine extends StatelessWidget {
  const _DetailLine({required this.label, required this.value});

  final String label;
  final String value;

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.symmetric(vertical: 4),
      child: Row(
        children: [
          SizedBox(
            width: 80,
            child: Text(label, style: Theme.of(context).textTheme.labelLarge),
          ),
          Expanded(child: Text(value)),
        ],
      ),
    );
  }
}

class _EmptyState extends StatelessWidget {
  const _EmptyState();

  @override
  Widget build(BuildContext context) {
    return const Padding(
      padding: EdgeInsets.symmetric(vertical: 48),
      child: Center(child: Text('검색 결과가 없습니다.')),
    );
  }
}

class _LoadingState extends StatelessWidget {
  const _LoadingState();

  @override
  Widget build(BuildContext context) {
    return const Padding(
      padding: EdgeInsets.symmetric(vertical: 48),
      child: Center(child: CircularProgressIndicator()),
    );
  }
}

class _ErrorState extends StatelessWidget {
  const _ErrorState({
    required this.message,
    required this.onRetry,
    this.onRelogin,
  });

  final String message;
  final VoidCallback onRetry;
  final Future<void> Function()? onRelogin;

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.symmetric(vertical: 48),
      child: Center(
        child: Column(
          children: [
            Text(message),
            const SizedBox(height: 12),
            if (onRelogin != null)
              FilledButton.icon(
                onPressed: onRelogin,
                icon: const Icon(Icons.login),
                label: const Text('다시 로그인'),
              )
            else
              OutlinedButton.icon(
                onPressed: onRetry,
                icon: const Icon(Icons.refresh),
                label: const Text('다시 시도'),
              ),
          ],
        ),
      ),
    );
  }
}

String _roleText(Employee employee) {
  return [
    employee.grade,
    employee.position,
    employee.jobTitle,
  ].whereType<String>().where((value) => value.isNotEmpty).join(' · ');
}

List<String> _sortDepartments(List<String> departments) {
  final sorted = [...departments];
  sorted.sort((left, right) {
    if (left == '미지정' && right != '미지정') {
      return -1;
    }
    if (left != '미지정' && right == '미지정') {
      return 1;
    }
    return left.compareTo(right);
  });
  return sorted;
}

List<Employee> _sortedEmployeesForDepartment(
  List<Employee> employees,
  String department,
) {
  final sorted = employees
      .where((employee) => (employee.department ?? '미지정') == department)
      .toList();
  sorted.sort(_compareEmployeesForOrganization);
  return sorted;
}

int _compareEmployeesForOrganization(Employee left, Employee right) {
  final leaderCompare = _leaderPriority(left).compareTo(_leaderPriority(right));
  if (leaderCompare != 0) {
    return leaderCompare;
  }

  final rankCompare = _rankPriority(left).compareTo(_rankPriority(right));
  if (rankCompare != 0) {
    return rankCompare;
  }

  final nameCompare = left.name.compareTo(right.name);
  if (nameCompare != 0) {
    return nameCompare;
  }

  return (left.sortOrder ?? 1 << 30).compareTo(right.sortOrder ?? 1 << 30);
}

int _leaderPriority(Employee employee) {
  if (employee.isManager) {
    return 0;
  }
  final position = employee.position?.trim() ?? '';
  return position.contains('팀장') ? 0 : 1;
}

int _rankPriority(Employee employee) {
  const priorityByToken = {
    '사장': 0,
    '부사장': 1,
    '수석': 2,
    '전무': 3,
    '상무': 4,
    '이사': 5,
    '책임': 6,
    '부장': 7,
    '선임': 8,
    '과장': 9,
    '대리': 10,
    '사원': 11,
  };
  final values = [
    employee.grade?.trim() ?? '',
    employee.position?.trim() ?? '',
    employee.jobTitle?.trim() ?? '',
  ];
  for (final value in values) {
    for (final entry in priorityByToken.entries) {
      if (value.contains(entry.key)) {
        return entry.value;
      }
    }
  }
  return priorityByToken.length + 1;
}

String _effectiveSearchQuery(String rawQuery) {
  final trimmed = rawQuery.trim();
  if (trimmed.isEmpty) {
    return '';
  }

  final hasHangul = RegExp(r'[가-힣]').hasMatch(trimmed);
  if (hasHangul) {
    return trimmed;
  }

  final digitOnly = RegExp(r'^[0-9\s-]+$').hasMatch(trimmed);
  if (digitOnly) {
    final digits = trimmed.replaceAll(RegExp(r'[^0-9]'), '');
    final normalizedDigits = digits.startsWith('010')
        ? digits.substring(3)
        : digits;
    return normalizedDigits.length >= 2 ? trimmed : '';
  }

  return trimmed.length >= 2 ? trimmed : '';
}

bool _matchesEmployeeSearch(Employee employee, String query) {
  final trimmed = query.trim();
  if (trimmed.isEmpty) {
    return true;
  }

  final digitOnly = RegExp(r'^[0-9\s-]+$').hasMatch(trimmed);
  if (digitOnly) {
    final normalizedQuery = _normalizedSearchDigits(trimmed);
    if (normalizedQuery.isEmpty) {
      return true;
    }

    final phoneCandidates = <String>{
      _normalizedSearchDigits(employee.phoneNumber),
      _normalizedSearchDigits(employee.phoneDisplay ?? ''),
    }..removeWhere((value) => value.isEmpty);

    return phoneCandidates.any((value) => value.contains(normalizedQuery));
  }

  final normalizedQuery = trimmed.toLowerCase();
  return employee.name.toLowerCase().contains(normalizedQuery);
}

String _normalizedSearchDigits(String value) {
  final digits = value.replaceAll(RegExp(r'[^0-9]'), '');
  if (digits.startsWith('8210')) {
    return digits.substring(4);
  }
  if (digits.startsWith('010')) {
    return digits.substring(3);
  }
  return digits;
}

String? _searchHelperText(String rawQuery) {
  final trimmed = rawQuery.trim();
  if (trimmed.isEmpty) {
    return null;
  }

  final hasHangul = RegExp(r'[가-힣]').hasMatch(trimmed);
  if (hasHangul) {
    return null;
  }

  final digitOnly = RegExp(r'^[0-9\s-]+$').hasMatch(trimmed);
  if (digitOnly) {
    final digits = trimmed.replaceAll(RegExp(r'[^0-9]'), '');
    final normalizedDigits = digits.startsWith('010')
        ? digits.substring(3)
        : digits;
    if (normalizedDigits.length < 2) {
      return '전화번호는 010 제외 2자리 이상 입력';
    }
    return null;
  }

  if (trimmed.length < 2) {
    return '직원명 1자 이상 또는 전화번호(010 제외) 2자리 이상 입력';
  }
  return null;
}
