import 'dart:io';

import 'package:get/get.dart';
import 'package:path/path.dart' as p;
import 'package:path_provider/path_provider.dart';
import 'package:supabase_flutter/supabase_flutter.dart';
import 'package:uuid/uuid.dart';

import 'app_database.dart';
import '../models/imported_layer_meta.dart';

/// Megosztott referencia-rétegek (pl. gázhálózat nyomvonala) kezelése.
///
/// Modell: a réteg egy FÁJL (GeoJSON) a Supabase Storage-ban + egy
/// metaadat-sor a ts_layers táblában, verziószámmal.
///
///  * PULL: minden tagsági projekt rétegeinél a verziószámot hasonlítjuk a
///    lokálishoz — csak akkor töltünk le, ha nőtt. A letöltött fájl a
///    meglévő imported_layers mechanizmusba kerül, így a térkép ugyanúgy
///    jeleníti meg, mint a kézzel importált rétegeket.
///  * OFFLINE: a lokálisan cache-elt fájl net nélkül is ott van — a réteg
///    mindig a legutóbb letöltött állapotában látszik.
///  * PUBLISH: egy lokálisan importált réteg megosztása = fájl feltöltése
///    a Storage-ba + ts_layers sor (verzió +1 újrapublikáláskor).
class LayerSyncService extends GetxService {
  static LayerSyncService get to => Get.find();

  static const bucket = 'terepi_seged-layers';

  final isSyncing = false.obs;

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

  /// A rétegfájlok lokális cache-könyvtára.
  Future<Directory> _layerDir() async {
    final base = await getExternalStorageDirectory() ??
        await getApplicationDocumentsDirectory();
    final dir = Directory(p.join(base.path, 'layers'));
    if (!await dir.exists()) await dir.create(recursive: true);
    return dir;
  }

  // ═════════════════════════════════════════════════════════════════
  // PULL — a TsSyncService hívja minden szinkron végén
  // ═════════════════════════════════════════════════════════════════

  Future<void> pullAll() async {
    if (_client.auth.currentUser == null) return;
    if (isSyncing.value) return;
    isSyncing.value = true;

    try {
      // Csak a szinkronizált (nem lokális) projektek rétegei érdekelnek.
      final projects = await _db.listProjects();
      for (final project in projects.where((pr) => !pr.isLocalOnly)) {
        await _pullProjectLayers(project.id!, project.uuid);
      }
    } finally {
      isSyncing.value = false;
    }
  }

  Future<void> _pullProjectLayers(
      int localProjectId, String projectUuid) async {
    final rows = await _client
        .from('terepi_seged_layers')
        .select()
        .eq('project_id', projectUuid)
        .filter('deleted_at', 'is', null);

    for (final r in rows) {
      final id = r['id'] as String;
      final remoteVersion = (r['version'] as int?) ?? 1;
      final localVersion = await _db.getImportedLayerVersion(id);

      // Már megvan a legfrissebb verzió → nincs teendő.
      if (localVersion >= remoteVersion) continue;

      // Letöltés a Storage-ból, lokális cache-be.
      final storagePath = r['storage_path'] as String;
      final bytes = await _client.storage.from(bucket).download(storagePath);

      final dir = await _layerDir();
      final localPath = p.join(dir.path, '$id.geojson');
      await File(localPath).writeAsBytes(bytes, flush: true);

      // Metaadat upsert a meglévő imported_layers táblába — a térkép
      // rétegkezelője innentől ugyanúgy látja, mint a kézi importot.
      await _db.upsertImportedLayerRaw({
        'id': id,
        'name': r['name'],
        'source_type': r['source_type'] ?? 'geojson',
        'local_path': localPath,
        'storage_path': storagePath,
        'is_visible': 1,
        'project_id': localProjectId,
        'imported_at': r['created_at'] ?? DateTime.now().toIso8601String(),
        'synced_at': DateTime.now().toIso8601String(),
        'version': remoteVersion,
      });
    }
  }

  // ═════════════════════════════════════════════════════════════════
  // PUBLISH — lokális réteg megosztása a projekt tagjaival
  // ═════════════════════════════════════════════════════════════════

  /// Feltölti a réteg fájlját és létrehozza/frissíti a ts_layers sorát.
  /// Újrapublikáláskor a verziószám nő → a többi eszköz a következő
  /// szinkronnál automatikusan frissíti a példányát.
  Future<void> publishLayer({
    required ImportedLayerMeta layer,
    required String projectUuid,
  }) async {
    final id = layer.id.isNotEmpty ? layer.id : const Uuid().v4();
    final fileName = p.basename(layer.localPath);
    final storagePath = '$projectUuid/$id/$fileName';

    // 1. Fájl a Storage-ba (upsert: újrapublikálásnál felülírja).
    await _client.storage.from(bucket).upload(
          storagePath,
          File(layer.localPath),
          fileOptions: const FileOptions(upsert: true),
        );

    // 2. Verzió léptetése, ha már létezik a sor.
    final existing = await _client
        .from('terepi_seged_layers')
        .select('version')
        .eq('id', id)
        .maybeSingle();
    final newVersion = ((existing?['version'] as int?) ?? 0) + 1;

    await _client.from('terepi_seged_layers').upsert({
      'id': id,
      'project_id': projectUuid,
      'name': layer.name,
      'source_type': layer.sourceType.name,
      'storage_path': storagePath,
      'version': newVersion,
    });

    // 3. Lokális meta frissítése, hogy a saját eszköz ne töltse le újra.
    await _db.upsertImportedLayerRaw({
      ...layer.toMap(),
      'id': id,
      'storage_path': storagePath,
      'synced_at': DateTime.now().toIso8601String(),
      'version': newVersion,
    });
  }

  /// Réteg visszavonása a megosztásból (soft delete a szerveren;
  /// a lokális cache-példányok megmaradnak az eszközökön).
  Future<void> unpublishLayer(String layerId) async {
    await _client.from('terepi_seged_layers').update({
      'deleted_at': DateTime.now().toUtc().toIso8601String(),
    }).eq('id', layerId);
  }
}
