// Geo fájl import service — perzisztens verzió
//
// TÁRHELY STRATÉGIA:
//   Fájlrendszer → /files/layers/{id}.geojson|kml|kmz  (nyers forrás)
//   SQLite       → imported_layers tábla               (metadata)
//   Supabase     → Storage                             (megosztás, később)

import 'dart:io';
import 'dart:typed_data';
import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:path/path.dart' as p;
import 'package:path_provider/path_provider.dart';
import 'package:terepi_seged/enums/layer_import_source_type.dart';
import 'package:uuid/uuid.dart';

import '../models/imported_layer.dart';
import '../models/imported_layer_meta.dart';
import '../services/app_database.dart';
import '../controls/geojson_parser.dart';
import '../core/kml_parser.dart';
import '../services/project_service.dart';

class ImportedLayerStyle {
  final Color color;
  final double opacity;
  final double strokeWidth;

  const ImportedLayerStyle({
    required this.color,
    this.opacity = 0.35,
    this.strokeWidth = 2.5,
  });

  ImportedLayerStyle copyWith({
    Color? color,
    double? opacity,
    double? strokeWidth,
  }) =>
      ImportedLayerStyle(
        color: color ?? this.color,
        opacity: opacity ?? this.opacity,
        strokeWidth: strokeWidth ?? this.strokeWidth,
      );
}

class LayerImportService extends GetxService {
  static LayerImportService get to => Get.find();

  final layers = <ImportedLayer>[].obs;
  final isLoading = false.obs;
  final isSyncing = false.obs;
  final lastError = Rxn<String>();

  final _kmlParser = const KmlParser(
    defaultPolylineColor: Color(0xCC1565C0),
    defaultPolylineStroke: 3.0,
    defaultPolygonFillColor: Color(0x4D1565C0),
    defaultPolygonBorderColor: Color(0xCC1565C0),
    defaultPolygonBorderStroke: 1.5,
  );

  String? _layerDir;
  static const _uuid = Uuid();
  final layerStyles = <String, ImportedLayerStyle>{}.obs;

  // ── Inicializálás ──────────────────────────────────────────────────────────

  @override
  Future<void> onReady() async {
    super.onReady();
    await _initLayerDir();
    await _loadPersistedLayers();
  }

  Future<void> _initLayerDir() async {
    final ext = await getExternalStorageDirectory();
    final dir = Directory(p.join(ext!.path, 'layers'));
    if (!await dir.exists()) await dir.create(recursive: true);
    _layerDir = dir.path;
  }

  // ── Fájl import ───────────────────────────────────────────────────────────

  Future<ImportedLayer?> importFile() async {
    try {
      isLoading.value = true;
      lastError.value = null;

      final result = await FilePicker.platform.pickFiles(
        // type: FileType.custom,
        // allowedExtensions: ['geojson', 'json', 'kml', 'kmz'],
        type: FileType.any,
        withData: true,
      );
      if (result == null || result.files.isEmpty) return null;

      final file = result.files.first;
      final fileName = file.name;
      final ext = fileName.split('.').last.toLowerCase();
      final bytes = file.bytes!;
      final id = _uuid.v4();

      if (!['geojson', 'json', 'kml', 'kmz'].contains(ext)) {
        throw Exception('Nem támogatott formátum: .$ext');
      }

      // 1. Fájl mentése tartós tárhelyre
      final localPath = await _saveFile(id, ext, bytes);

      // 2. Parse
      final layer = _parse(bytes, fileName, id, ext);
      if (layer.isEmpty) {
        await File(localPath).delete();
        throw Exception('Nem tartalmaz feldolgozható geometriát.');
      }

      // 3. SQLite metadata
      final meta = ImportedLayerMeta(
        id: id,
        name: fileName,
        sourceType: _sourceType(ext),
        localPath: localPath,
        isVisible: true,
        projectId: ProjectService.to.activeProjectId,
        importedAt: DateTime.now(),
      );
      await AppDatabase.instance.insertImportedLayer(meta);

      layers.add(layer);
      return layer;
    } catch (e) {
      lastError.value = e.toString();
      Get.snackbar('Import hiba', e.toString(),
          snackPosition: SnackPosition.BOTTOM);
      return null;
    } finally {
      isLoading.value = false;
    }
  }

  // ── Perzisztens rétegek betöltése induláskor ──────────────────────────────

  Future<void> _loadPersistedLayers() async {
    try {
      final projectId = ProjectService.to.activeProjectId;
      final metas =
          await AppDatabase.instance.listImportedLayers(projectId: projectId);

      final loaded = <ImportedLayer>[];
      for (final meta in metas) {
        final file = File(meta.localPath);
        if (!await file.exists()) {
          // Fájl törlődött — SQLite rekord is törlendő
          await AppDatabase.instance.deleteImportedLayer(meta.id);
          continue;
        }
        try {
          final bytes = await file.readAsBytes();
          final ext = meta.localPath.split('.').last;
          loaded.add(_parse(bytes, meta.name, meta.id, ext,
              isVisible: meta.isVisible));

          final style = meta.getStyle();
          if (style != null) {
            layerStyles[meta.id] = style;
          }
        } catch (e) {
          debugPrint('Réteg betöltés hiba (${meta.name}): $e');
        }
      }
      layers.assignAll(loaded);
    } catch (e) {
      debugPrint('Perzisztens rétegek betöltési hiba: $e');
    }
  }

  // ── Réteg kezelés ─────────────────────────────────────────────────────────

  Future<void> toggleLayer(String id) async {
    final idx = layers.indexWhere((l) => l.id == id);
    if (idx < 0) return;
    final newVisible = !layers[idx].isVisible;
    layers[idx] = layers[idx].copyWith(isVisible: newVisible);
    layers.refresh();

    // SQLite szinkron
    final metas = await AppDatabase.instance.listImportedLayers();
    final meta = metas.firstWhereOrNull((m) => m.id == id);
    if (meta != null) {
      await AppDatabase.instance
          .updateImportedLayer(meta.copyWith(isVisible: newVisible));
    }
  }

  Future<void> removeLayer(String id) async {
    final metas = await AppDatabase.instance.listImportedLayers();
    final meta = metas.firstWhereOrNull((m) => m.id == id);
    if (meta != null) {
      final f = File(meta.localPath);
      if (await f.exists()) await f.delete();
      await AppDatabase.instance.deleteImportedLayer(id);
    }
    layers.removeWhere((l) => l.id == id);
  }

  Future<void> removeAll() async {
    for (final l in List.from(layers)) {
      await removeLayer(l.id);
    }
  }

  List<ImportedLayer> get visibleLayers =>
      layers.where((l) => l.isVisible).toList();

  // ── Supabase megosztás (előkészítve) ──────────────────────────────────────
  //
  // Implementáció a megosztási feladatnál:
  //
  // Future<void> syncLayerToSupabase(String id) async {
  //   final meta   = ...
  //   final bytes  = await File(meta.localPath).readAsBytes();
  //   final ext    = meta.localPath.split('.').last;
  //   final path   = 'layers/$projectUuid/${meta.id}.$ext';
  //
  //   await supabase.storage.from('geo_layers').uploadBinary(path, bytes);
  //
  //   await AppDatabase.instance.updateImportedLayer(
  //     meta.copyWith(storagePath: path, syncedAt: DateTime.now()));
  //
  //   await supabase.from('terepi_seged_shared_layers').upsert({
  //     'id': meta.id, 'name': meta.name, 'project_uuid': projectUuid,
  //     'storage_path': path, 'source_type': meta.sourceType.name,
  //     'created_at': DateTime.now().toIso8601String(),
  //   });
  // }

  // ── Belső segédek ─────────────────────────────────────────────────────────

  Future<String> _saveFile(String id, String ext, Uint8List bytes) async {
    final path = p.join(_layerDir!, '$id.$ext');
    await File(path).writeAsBytes(bytes);
    return path;
  }

  ImportedLayer _parse(Uint8List bytes, String name, String id, String ext,
      {bool isVisible = true}) {
    switch (ext.toLowerCase()) {
      case 'geojson':
      case 'json':
        return _parseGeoJson(String.fromCharCodes(bytes), name, id,
            isVisible: isVisible);
      case 'kml':
        return _kmlParser.parseKml(String.fromCharCodes(bytes), name,
            id: id, isVisible: isVisible);
      case 'kmz':
        return _kmlParser.parseKmz(bytes, name, id: id, isVisible: isVisible);
      default:
        throw Exception('Nem támogatott formátum: .$ext');
    }
  }

  ImportedLayer _parseGeoJson(String content, String name, String id,
      {bool isVisible = true}) {
    final parser = GeoJsonParser(
      defaultMarkerColor: Colors.red.withOpacity(0.9),
      defaultMarkerIcon: Icons.location_pin,
      defaultPolylineColor: const Color(0xCC1565C0),
      defaultPolylineStroke: 3.0,
      defaultPolygonFillColor: const Color(0x4D1565C0),
      defaultPolygonBorderColor: const Color(0xCC1565C0),
      defaultPolygonBorderStroke: 1.5,
      defaultPolygonIsFilled: true,
      onMarkerTapCallback: (props) {
        final label = props['name'] ?? props['title'] ?? '';
        if (label.toString().isNotEmpty) {
          Get.snackbar(label.toString(), props['description']?.toString() ?? '',
              snackPosition: SnackPosition.BOTTOM,
              duration: const Duration(seconds: 3));
        }
      },
    );
    parser.parseGeoJsonAsString(content);

    return ImportedLayer(
      id: id,
      name: name,
      sourceType: LayerImportSourceType.geoJson,
      markers: parser.markers,
      polylines: parser.polylines,
      polygons: parser.polygons,
      isVisible: isVisible,
      importedAt: DateTime.now(),
    );
  }

  LayerImportSourceType _sourceType(String ext) => switch (ext.toLowerCase()) {
        'kml' => LayerImportSourceType.kml,
        'kmz' => LayerImportSourceType.kmz,
        _ => LayerImportSourceType.geoJson,
      };

  void setLayerStyle(String id, ImportedLayerStyle style) async {
    layerStyles[id] = style;
    layerStyles.refresh();
    layers.refresh();

    final metas = await AppDatabase.instance.listImportedLayers();
    final meta = metas.firstWhereOrNull((m) => m.id == id);

    if (meta != null) {
      final hex =
          '#${style.color.value.toRadixString(16).padLeft(8, '0').substring(2).toUpperCase()}';

      await AppDatabase.instance.updateImportedLayer(meta.copyWith(
          colorHex: hex,
          opacity: style.opacity,
          strokeWidth: style.strokeWidth));
    }
  }

  ImportedLayerStyle? getLayerStyle(String id) => layerStyles[id];
}
