import 'dart:convert';
import 'package:flutter/services.dart';
import 'package:sigap/src/features/auth/data/models/administrative_division.dart';

/// Service responsible for loading administrative division data from JSON files
class AdministrativeRepository {
  // Singleton pattern
  static final AdministrativeRepository _instance = AdministrativeRepository._internal();
  factory AdministrativeRepository() => _instance;
  AdministrativeRepository._internal();

  // Cache for data
  List<AdministrativeDivision>? _provinces;
  List<AdministrativeDivision>? _regencies;
  List<AdministrativeDivision>? _districts;
  List<AdministrativeDivision>? _villages;

  // Getters for cached data
  Future<List<AdministrativeDivision>> get provinces async {
    if (_provinces == null) {
      await _loadProvinces();
    }
    return _provinces!;
  }

  Future<List<AdministrativeDivision>> get regencies async {
    if (_regencies == null) {
      await _loadRegencies();
    }
    return _regencies!;
  }

  Future<List<AdministrativeDivision>> get districts async {
    if (_districts == null) {
      await _loadDistricts();
    }
    return _districts!;
  }

  Future<List<AdministrativeDivision>> get villages async {
    if (_villages == null) {
      await _loadVillages();
    }
    return _villages!;
  }

  /// Load provinces from JSON file
  Future<void> _loadProvinces() async {
    final jsonString = await rootBundle.loadString('public/jsons/provinsi.json');
    final List<dynamic> jsonList = json.decode(jsonString);
    
    _provinces = jsonList
        .map((json) => AdministrativeDivision.fromProvinceJson(json))
        .toList();
  }

  /// Load regencies/cities from JSON file
  Future<void> _loadRegencies() async {
    final jsonString = await rootBundle.loadString('public/jsons/kabupaten_kota.json');
    final List<dynamic> jsonList = json.decode(jsonString);
    
    _regencies = jsonList
        .map((json) => AdministrativeDivision.fromRegencyJson(json))
        .toList();
  }

  /// Load districts from JSON file
  Future<void> _loadDistricts() async {
    final jsonString = await rootBundle.loadString('public/jsons/kecamatan.json');
    final List<dynamic> jsonList = json.decode(jsonString);
    
    _districts = jsonList
        .map((json) => AdministrativeDivision.fromDistrictJson(json))
        .toList();
  }

  /// Load villages from JSON file
  Future<void> _loadVillages() async {
    final jsonString = await rootBundle.loadString('public/jsons/desa_kelurahan.json');
    final List<dynamic> jsonList = json.decode(jsonString);
    
    _villages = jsonList
        .map((json) => AdministrativeDivision.fromVillageJson(json))
        .toList();
  }

  /// Get provinces with pagination
  Future<List<AdministrativeDivision>> getProvinces({int page = 1, int limit = 20}) async {
    final allProvinces = await provinces;
    
    final startIndex = (page - 1) * limit;
    if (startIndex >= allProvinces.length) {
      return [];
    }
    
    final endIndex = startIndex + limit > allProvinces.length
        ? allProvinces.length
        : startIndex + limit;
    
    return allProvinces.sublist(startIndex, endIndex);
  }

  /// Get regencies/cities by province ID with pagination
  Future<List<AdministrativeDivision>> getRegenciesByProvince(
    String provinceId, {
    int page = 1,
    int limit = 20,
  }) async {
    final allRegencies = await regencies;
    
    final filteredRegencies = allRegencies
        .where((regency) => regency.parentId == provinceId)
        .toList();
    
    final startIndex = (page - 1) * limit;
    if (startIndex >= filteredRegencies.length) {
      return [];
    }
    
    final endIndex = startIndex + limit > filteredRegencies.length
        ? filteredRegencies.length
        : startIndex + limit;
    
    return filteredRegencies.sublist(startIndex, endIndex);
  }

  /// Get districts by regency ID with pagination
  Future<List<AdministrativeDivision>> getDistrictsByRegency(
    String regencyId, {
    int page = 1,
    int limit = 20,
  }) async {
    final allDistricts = await districts;
    
    final filteredDistricts = allDistricts
        .where((district) => district.parentId == regencyId)
        .toList();
    
    final startIndex = (page - 1) * limit;
    if (startIndex >= filteredDistricts.length) {
      return [];
    }
    
    final endIndex = startIndex + limit > filteredDistricts.length
        ? filteredDistricts.length
        : startIndex + limit;
    
    return filteredDistricts.sublist(startIndex, endIndex);
  }

  /// Get villages by district ID with pagination
  Future<List<AdministrativeDivision>> getVillagesByDistrict(
    String districtId, {
    int page = 1,
    int limit = 20,
  }) async {
    final allVillages = await villages;
    
    final filteredVillages = allVillages
        .where((village) => village.parentId == districtId)
        .toList();
    
    final startIndex = (page - 1) * limit;
    if (startIndex >= filteredVillages.length) {
      return [];
    }
    
    final endIndex = startIndex + limit > filteredVillages.length
        ? filteredVillages.length
        : startIndex + limit;
    
    return filteredVillages.sublist(startIndex, endIndex);
  }

  /// Search for locations across all levels
  Future<List<AdministrativeDivision>> searchLocations(
    String query, {
    int limit = 40,
  }) async {
    if (query.length < 3) {
      return [];
    }

    final normalizedQuery = query.toLowerCase();
    final results = <AdministrativeDivision>[];
    
    // Search through provinces
    final allProvinces = await provinces;
    for (final province in allProvinces) {
      if (province.name.toLowerCase().contains(normalizedQuery)) {
        results.add(province);
        if (results.length >= limit) break;
      }
    }
    
    // Search through regencies
    if (results.length < limit) {
      final allRegencies = await regencies;
      for (final regency in allRegencies) {
        if (regency.name.toLowerCase().contains(normalizedQuery)) {
          results.add(regency);
          if (results.length >= limit) break;
        }
      }
    }
    
    // Search through districts
    if (results.length < limit) {
      final allDistricts = await districts;
      for (final district in allDistricts) {
        if (district.name.toLowerCase().contains(normalizedQuery)) {
          results.add(district);
          if (results.length >= limit) break;
        }
      }
    }
    
    // Search through villages
    if (results.length < limit) {
      final allVillages = await villages;
      for (final village in allVillages) {
        if (village.name.toLowerCase().contains(normalizedQuery)) {
          results.add(village);
          if (results.length >= limit) break;
        }
      }
    }
    
    return results;
  }

  /// Get the full location string (Province, Regency, District, Village)
  Future<String> getFullLocationString(AdministrativeDivision division) async {
    final parts = <String>[division.name];
    
    switch (division.type) {
      case DivisionType.village:
        // Add district
        final allDistricts = await districts;
        final district = allDistricts.firstWhere(
          (d) => d.id == division.parentId,
          orElse: () => AdministrativeDivision(
            id: '', name: '', type: DivisionType.district),
        );
        if (district.id.isNotEmpty) {
          parts.add(district.name);
          
          // Add regency
          final allRegencies = await regencies;
          final regency = allRegencies.firstWhere(
            (r) => r.id == district.parentId,
            orElse: () => AdministrativeDivision(
              id: '', name: '', type: DivisionType.regency),
          );
          
          if (regency.id.isNotEmpty) {
            parts.add(regency.name);
            
            // Add province
            final allProvinces = await provinces;
            final province = allProvinces.firstWhere(
              (p) => p.id == regency.parentId,
              orElse: () => AdministrativeDivision(
                id: '', name: '', type: DivisionType.province),
            );
            
            if (province.id.isNotEmpty) {
              parts.add(province.name);
            }
          }
        }
        break;
        
      case DivisionType.district:
        // Add regency
        final allRegencies = await regencies;
        final regency = allRegencies.firstWhere(
          (r) => r.id == division.parentId,
          orElse: () => AdministrativeDivision(
            id: '', name: '', type: DivisionType.regency),
        );
        
        if (regency.id.isNotEmpty) {
          parts.add(regency.name);
          
          // Add province
          final allProvinces = await provinces;
          final province = allProvinces.firstWhere(
            (p) => p.id == regency.parentId,
            orElse: () => AdministrativeDivision(
              id: '', name: '', type: DivisionType.province),
          );
          
          if (province.id.isNotEmpty) {
            parts.add(province.name);
          }
        }
        break;
        
      case DivisionType.regency:
        // Add province
        final allProvinces = await provinces;
        final province = allProvinces.firstWhere(
          (p) => p.id == division.parentId,
          orElse: () => AdministrativeDivision(
            id: '', name: '', type: DivisionType.province),
        );
        
        if (province.id.isNotEmpty) {
          parts.add(province.name);
        }
        break;
        
      case DivisionType.province:
        // Already has the province name
        break;
    }
    
    return parts.join(', ');
  }
  
  /// Get division ID from name and type (for navigation)
  Future<String?> getIdFromName(String name, DivisionType type) async {
    switch (type) {
      case DivisionType.province:
        final allProvinces = await provinces;
        final province = allProvinces.firstWhere(
          (p) => p.name == name,
          orElse: () => AdministrativeDivision(
            id: '', name: '', type: DivisionType.province),
        );
        return province.id.isEmpty ? null : province.id;
        
      case DivisionType.regency:
        final allRegencies = await regencies;
        final regency = allRegencies.firstWhere(
          (r) => r.name == name,
          orElse: () => AdministrativeDivision(
            id: '', name: '', type: DivisionType.regency),
        );
        return regency.id.isEmpty ? null : regency.id;
        
      case DivisionType.district:
        final allDistricts = await districts;
        final district = allDistricts.firstWhere(
          (d) => d.name == name,
          orElse: () => AdministrativeDivision(
            id: '', name: '', type: DivisionType.district),
        );
        return district.id.isEmpty ? null : district.id;
        
      case DivisionType.village:
        final allVillages = await villages;
        final village = allVillages.firstWhere(
          (v) => v.name == name,
          orElse: () => AdministrativeDivision(
            id: '', name: '', type: DivisionType.village),
        );
        return village.id.isEmpty ? null : village.id;
    }
  }
}
