import 'package:get/get.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:supabase_flutter/supabase_flutter.dart';
import 'package:terepi_seged/services/app_database.dart';

import 'stakeout_service.dart';

/// Kitűzési pontok Supabase-szinkronja — a meglévő outbox-mintára.
///
/// A TsSyncService hívja minden ciklus végén (mint a LayerSyncService-t),
/// de kézzel (syncNow) is futtatható. PUSH: a 'pending' sorok uuid-ra
/// upserttel (idempotens); PULL: projektenként inkrementálisan
/// (updated_at > kurzor). A csak-lokális projektek kizárását az
/// AppDatabase.pendingStakeoutPoints() végzi SQL-szinten, a pull pedig
/// csak a nem-lokális projekteken iterál.
///
/// SAJÁT kurzort használ (SharedPreferences, projektenként), mert a
/// TsSyncService a projects.last_pulled_at kurzort a saját pullja végén
/// már előretolta — közös kurzorral a kitűzés-sorok kimaradhatnának.
///
/// Csapat-láthatóság: a letöltött sorok a lokális DB-be kerülnek, a
/// StakeoutService.load() frissíti a listát, így a MÁS ESZKÖZÖN kitűzött
/// pont a térképen magától zöldre vált.
class StakeoutSyncService extends GetxService {
  static StakeoutSyncService get to => Get.find();

  final isSyncing = false.obs;
  final lastError = ''.obs;

  SupabaseClient get _client => Supabase.instance.client;
  AppDatabase get _db => AppDatabase.instance;

  Future<void> sync() async {
    if (_client.auth.currentUser == null) return;
    if (isSyncing.value) return;
    isSyncing.value = true;
    lastError.value = '';
    try {
      final pulledAny = await _pushThenPull();
      // Ha aktív a kitűzés mód és jött új adat, frissítjük a nézetet —
      // itt válik láthatóvá a többiek munkája (zöld markerek).
      if (pulledAny &&
          Get.isRegistered<StakeoutService>() &&
          StakeoutService.to.active.value) {
        await StakeoutService.to.load();
      }
    } catch (e) {
      lastError.value = e.toString();
    } finally {
      isSyncing.value = false;
    }
  }

  Future<bool> _pushThenPull() async {
    await _push();
    return _pull();
  }

  // ── PUSH ─────────────────────────────────────────────────────────

  Future<void> _push() async {
    final rows = await _db.pendingStakeoutPoints();
    if (rows.isEmpty) return;

    final payload = rows
        .map((r) => {
              'id': r['uuid'],
              'project_id': r['project_uuid'],
              'line_id': r['line_id'],
              'station': r['station'],
              'name': r['name'],
              'point_type': r['point_type'],
              'source': r['source'],
              'plan_eov_y': r['plan_eov_y'],
              'plan_eov_x': r['plan_eov_x'],
              'plan_eov_z': r['plan_eov_z'],
              'plan_lat': r['plan_lat'],
              'plan_lon': r['plan_lon'],
              'status': r['status'],
              'measured_eov_y': r['measured_eov_y'],
              'measured_eov_x': r['measured_eov_x'],
              'measured_eov_z': r['measured_eov_z'],
              'measured_lat': r['measured_lat'],
              'measured_lon': r['measured_lon'],
              'dev_inline': r['dev_inline'],
              'dev_crossline': r['dev_crossline'],
              'dev_dz': r['dev_dz'],
              'fix_quality': r['fix_quality'],
              'accuracy': r['accuracy'],
              'tilt_deg': r['tilt_deg'],
              'staked_at': _toUtc(r['staked_at'] as String?),
              'is_offset': (r['is_offset'] as int? ?? 0) == 1,
              'parent_uuid': r['parent_uuid'],
              'offset_dist': r['offset_dist'],
              'offset_bearing': r['offset_bearing'],
              'device_id': r['device_id'],
              'deleted_at': _toUtc(r['deleted_at'] as String?),
            })
        .toList();

    await _client.from('terepi_seged_stakeout_points').upsert(payload);
    await _db.markSyncedByUuid(
        'stakeout_points', rows.map((r) => r['uuid'] as String).toList());
  }

  // ── PULL ─────────────────────────────────────────────────────────

  Future<bool> _pull() async {
    final prefs = await SharedPreferences.getInstance();
    var pulledAny = false;

    final projects = await _db.listProjects();
    for (final project in projects.where((p) => !p.isLocalOnly)) {
      final cursorKey = 'stakeout_cursor_${project.uuid}';
      final cursor = prefs.getString(cursorKey) ?? '1970-01-01T00:00:00Z';
      var maxSeen = cursor;

      final rows = await _client
          .from('terepi_seged_stakeout_points')
          .select()
          .eq('project_id', project.uuid)
          .gt('updated_at', cursor)
          .order('updated_at', ascending: true)
          .limit(1000);

      for (final r in rows) {
        final ts = r['updated_at'] as String?;
        if (ts != null && DateTime.parse(ts).isAfter(DateTime.parse(maxSeen))) {
          maxSeen = ts;
        }

        // Az AppDatabase generikus applyRemoteRow-ja: uuid alapján
        // beszúr/frissít, a lokális 'pending' sort nem írja felül
        // (a push előbb futott).
        await _db.applyRemoteRow('stakeout_points', {
          'uuid': r['id'],
          'project_id': project.id,
          'line_id': r['line_id'] ?? '',
          'station': r['station'],
          'name': r['name'] ?? '${r['station']}',
          'point_type': r['point_type'] ?? 'geofon',
          'source': r['source'] ?? 'supabase',
          'plan_eov_y': r['plan_eov_y'],
          'plan_eov_x': r['plan_eov_x'],
          'plan_eov_z': r['plan_eov_z'],
          'plan_lat': r['plan_lat'],
          'plan_lon': r['plan_lon'],
          'status': r['status'] ?? 'pending',
          'measured_eov_y': r['measured_eov_y'],
          'measured_eov_x': r['measured_eov_x'],
          'measured_eov_z': r['measured_eov_z'],
          'measured_lat': r['measured_lat'],
          'measured_lon': r['measured_lon'],
          'dev_inline': r['dev_inline'],
          'dev_crossline': r['dev_crossline'],
          'dev_dz': r['dev_dz'],
          'fix_quality': r['fix_quality'],
          'accuracy': r['accuracy'],
          'tilt_deg': r['tilt_deg'],
          'staked_at': r['staked_at'],
          'is_offset': (r['is_offset'] == true) ? 1 : 0,
          'parent_uuid': r['parent_uuid'],
          'offset_dist': r['offset_dist'],
          'offset_bearing': r['offset_bearing'],
          'created_by': r['created_by'],
          'device_id': r['device_id'],
          'created_at': r['created_at'] ?? DateTime.now().toIso8601String(),
          'updated_at': r['updated_at'],
          'deleted_at': r['deleted_at'],
          'sync_status': 'synced',
        });
        pulledAny = true;
      }

      if (maxSeen != cursor) {
        await prefs.setString(cursorKey, maxSeen);
      }
    }
    return pulledAny;
  }

  /// A lokális, zóna nélküli időbélyeg a Postgres-ben UTC-nek számítana
  /// → küldés előtt explicit UTC-re konvertálunk.
  String? _toUtc(String? localIso) {
    if (localIso == null || localIso.isEmpty) return null;
    return DateTime.parse(localIso).toUtc().toIso8601String();
  }
}
