import 'package:flutter/foundation.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';

import '../../organization/data/org_context_api_client.dart';
import 'directory_api_client.dart';
import '../domain/employee.dart';

class DirectoryQuery {
  const DirectoryQuery({
    required this.query,
    required this.tenantSlug,
    this.department,
    this.tenantSlugs = const <String>[],
  });

  final String query;
  final String tenantSlug;
  final String? department;
  final List<String> tenantSlugs;

  String? get apiQuery => query.trim().isEmpty ? null : query.trim();
  String? get apiTenantSlug => tenantSlug == 'all' ? null : tenantSlug;
  List<String>? get apiTenantSlugs {
    if (tenantSlugs.isNotEmpty) {
      return tenantSlugs;
    }
    final slug = apiTenantSlug;
    return slug == null ? null : [slug];
  }

  String? get apiDepartment {
    if (department == null) {
      return null;
    }
    final normalized = department!.trim();
    return normalized.isEmpty ? null : normalized;
  }

  @override
  bool operator ==(Object other) {
    return identical(this, other) ||
        other is DirectoryQuery &&
            other.query == query &&
            other.tenantSlug == tenantSlug &&
            other.department == department &&
            listEquals(other.tenantSlugs, tenantSlugs);
  }

  @override
  int get hashCode =>
      Object.hash(query, tenantSlug, department, Object.hashAll(tenantSlugs));
}

abstract class DirectoryRepository {
  const DirectoryRepository();

  Future<List<Employee>> loadEmployees(DirectoryQuery query);
}

abstract class EmployeeDetailRepository {
  const EmployeeDetailRepository();

  Future<EmployeeDetail> loadEmployeeDetail(String employeeId);
}

class RemoteDirectoryRepository implements DirectoryRepository {
  const RemoteDirectoryRepository({required this.orgContextApiClient});

  final OrgContextApiClient orgContextApiClient;

  @override
  Future<List<Employee>> loadEmployees(DirectoryQuery query) async {
    final snapshot = await orgContextApiClient.fetchOrgContext(
      tenantSlug: query.apiTenantSlug,
    );
    final employees = snapshot.employees.where((employee) {
      return _matchesTenant(employee, query.apiTenantSlugs) &&
          _matchesDepartment(employee, query.apiDepartment) &&
          _matchesQuery(employee, query.apiQuery);
    }).toList();

    employees.sort(_compareEmployees);
    return employees;
  }

  bool _matchesTenant(Employee employee, List<String>? tenantSlugs) {
    return tenantSlugs == null || tenantSlugs.contains(employee.tenantSlug);
  }

  bool _matchesDepartment(Employee employee, String? department) {
    if (department == null) {
      return true;
    }
    return employee.department == department ||
        employee.tenantName == department;
  }

  bool _matchesQuery(Employee employee, String? query) {
    if (query == null) {
      return true;
    }
    final normalizedQuery = query.toLowerCase();
    final queryDigits = query.replaceAll(RegExp(r'[^0-9]'), '');
    final fields = [
      employee.name,
      employee.phoneNumber,
      employee.phoneDisplay,
      employee.email,
      employee.tenantName,
      employee.department,
      employee.grade,
      employee.position,
      employee.jobTitle,
    ].whereType<String>();

    return fields.any((field) {
      final normalizedField = field.toLowerCase();
      if (normalizedField.contains(normalizedQuery)) {
        return true;
      }
      if (queryDigits.isEmpty) {
        return false;
      }
      final fieldDigits = field.replaceAll(RegExp(r'[^0-9]'), '');
      return fieldDigits.contains(queryDigits);
    });
  }
}

class RemoteEmployeeDetailRepository implements EmployeeDetailRepository {
  const RemoteEmployeeDetailRepository({required this.directoryApiClient});

  final DirectoryApiClient directoryApiClient;

  @override
  Future<EmployeeDetail> loadEmployeeDetail(String employeeId) {
    return directoryApiClient.getEmployee(employeeId);
  }
}

int _compareEmployees(Employee left, Employee right) {
  final tenantCompare = left.tenantName.compareTo(right.tenantName);
  if (tenantCompare != 0) {
    return tenantCompare;
  }

  final departmentCompare = (left.department ?? '').compareTo(
    right.department ?? '',
  );
  if (departmentCompare != 0) {
    return departmentCompare;
  }

  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;
}

final directoryRepositoryProvider = Provider<DirectoryRepository>((ref) {
  return RemoteDirectoryRepository(
    orgContextApiClient: ref.watch(orgContextApiClientProvider),
  );
});

final employeeDetailRepositoryProvider = Provider<EmployeeDetailRepository>((ref) {
  return RemoteEmployeeDetailRepository(
    directoryApiClient: ref.watch(directoryApiClientProvider),
  );
});

final directoryEmployeesProvider = FutureProvider.autoDispose
    .family<List<Employee>, DirectoryQuery>((ref, query) {
      return ref.watch(directoryRepositoryProvider).loadEmployees(query);
    });
