import 'dart:convert';

import 'package:http/http.dart' as http;

import 'clinic.dart';
import 'geo_utils.dart';

/// Consultórios dentários encontrados perto de uma morada, já separados
/// pela regra de negócio "menos de 5km" vs "mais distantes" — a única
/// classe que conhece esse limite; os ecrãs só mostram o que recebem.
typedef NearbyClinics = ({List<Clinic> near, List<Clinic> far});

/// Pesquisa de consultórios dentários via Overpass API (dados do
/// OpenStreetMap). `overpass-api.de` é um servidor público e comunitário,
/// sem SLA — se se tornar um problema em produção, a solução é apontar
/// para outra instância (ex.: overpass.kumi.systems), não redesenhar isto.
class OverpassService {
  const OverpassService._();

  static const String _endpoint = 'https://overpass-api.de/api/interpreter';
  static const String _userAgent = 'CheckTeethKids/1.0 (caducorreia740@gmail.com)';

  /// Raio de pesquisa efetivo — mais largo que os 5km usados para
  /// distinguir "perto"/"longe", para haver o que mostrar em "mais
  /// distantes" mesmo em zonas menos densas.
  static const int _searchRadiusMeters = 18000;
  static const double _nearThresholdKm = 5;

  static Future<NearbyClinics> fetchNearbyClinics({
    required double lat,
    required double lon,
  }) async {
    const overpassTimeoutSeconds = 25;
    final query =
        '[out:json][timeout:$overpassTimeoutSeconds];'
        '('
        'node["amenity"="dentist"](around:$_searchRadiusMeters,$lat,$lon);'
        'way["amenity"="dentist"](around:$_searchRadiusMeters,$lat,$lon);'
        'node["healthcare"="dentist"](around:$_searchRadiusMeters,$lat,$lon);'
        'way["healthcare"="dentist"](around:$_searchRadiusMeters,$lat,$lon);'
        ');'
        'out center tags;';

    // O timeout do lado do cliente fica acima do [timeout:] pedido à
    // Overpass — assim é sempre a própria Overpass a desistir primeiro
    // (com uma resposta 5xx tratável abaixo), em vez de uma corrida entre
    // os dois a cortar a ligação a meio de uma resposta que já vinha a
    // caminho.
    final response = await http
        .post(
          Uri.parse(_endpoint),
          headers: {'User-Agent': _userAgent},
          body: {'data': query},
        )
        .timeout(const Duration(seconds: overpassTimeoutSeconds + 10));

    if (response.statusCode != 200) {
      throw Exception(
        'Overpass respondeu com status ${response.statusCode}: '
        '${response.body.substring(0, response.body.length.clamp(0, 300))}',
      );
    }

    final decoded = jsonDecode(response.body);
    final elements = (decoded is Map ? decoded['elements'] : null) as List?;
    if (elements == null) {
      throw Exception('Resposta inesperada da Overpass API');
    }

    final clinics = elements
        .cast<Map<String, dynamic>>()
        .map(Clinic.fromOverpassElement)
        .toList();

    final withDistance = clinics
        .map((c) => c.copyWithDistance(haversineDistanceKm(lat, lon, c.lat, c.lon)))
        .toList()
      ..sort((a, b) => a.distanceKm!.compareTo(b.distanceKm!));

    final near = withDistance.where((c) => c.distanceKm! < _nearThresholdKm).toList();
    final far = withDistance.where((c) => c.distanceKm! >= _nearThresholdKm).toList();

    return (near: near, far: far);
  }
}
