import 'package:anyway/domain/entities/landmark.dart';
import 'package:anyway/domain/entities/preferences.dart';
import 'package:anyway/domain/entities/trip.dart';
import 'package:anyway/domain/repositories/trip_repository.dart';
import 'package:anyway/data/datasources/trip_remote_datasource.dart';

class BackendTripRepository implements TripRepository {
  final TripRemoteDataSource remote;

  BackendTripRepository({required this.remote});

  @override
  Future<Trip> getTrip({Preferences? preferences, String? tripUUID, List<Landmark>? landmarks}) async {
    try {
      Map<String, dynamic> json;

      if (tripUUID != null) {
        json = await remote.fetchTrip(tripUUID);
      } else {
        if (preferences == null) {
          throw ArgumentError('Either preferences or tripUUID must be provided');
        }

        final Map<String, dynamic> prefsPayload = _buildPreferencesPayload(preferences);
        List<Map<String, dynamic>> landmarkBodies = landmarks != null
            ? landmarks.map((lm) => lm.toJson()).toList()
            : await _fetchLandmarkPayloads(prefsPayload, preferences.startLocation);

        // TODO: remove
        // restrict the landmark list to 30 to iterate quickly
        landmarkBodies = landmarkBodies.take(30).toList();
        // change the json key because of backend inconsistency
        for (var lm in landmarkBodies) {
          if (lm.containsKey('type')) {
            lm['type'] = "sightseeing";
          }
          lm['osm_type'] = 'node';
          lm['osm_id'] = 1;
        }

        final Map<String, dynamic> body = {
          'preferences': prefsPayload,
          'landmarks': landmarkBodies,
          'start': preferences.startLocation,
        };
        if (preferences.endLocation != null) {
          body['end'] = preferences.endLocation;
        }
        if (preferences.detourToleranceMinutes != null) {
          body['detour_tolerance_minute'] = preferences.detourToleranceMinutes;
        }

        json = await remote.createTrip(body);
      }

      return Trip.fromJson(json);
    } catch (e) {
      throw Exception('Failed to fetch trip: ${e.toString()}');
    }
  }


  // TODO - maybe shorten this
  @override
  Future<List<Landmark>> searchLandmarks(Preferences preferences) async {
    final Map<String, dynamic> prefsPayload = _buildPreferencesPayload(preferences);
    final List<Map<String, dynamic>> rawLandmarks = await _fetchLandmarkPayloads(prefsPayload, preferences.startLocation);
    return rawLandmarks.map((lmJson) => Landmark.fromJson(lmJson)).toList();
  }

  Future<List<Map<String, dynamic>>> _fetchLandmarkPayloads(
      Map<String, dynamic> prefsPayload, List<double> startLocation) async {
    final Map<String, dynamic> landmarkRequest = {
      'preferences': prefsPayload,
      'start': startLocation,
    };

    return await remote.fetchLandmarks(landmarkRequest);
  }

  Map<String, dynamic> _buildPreferencesPayload(Preferences preferences) {
    final Map<String, dynamic> prefsPayload = {};
    preferences.scores.forEach((type, score) {
      prefsPayload[type] = {'type': type, 'score': score};
    });
    prefsPayload['max_time_minute'] = preferences.maxTimeMinutes;
    return prefsPayload;
  }
}
