// KML és KMZ → flutter_map objektumok
//
// FONTOS: KML koordináta sorrend: lon,lat,alt
// Flutter LatLng(lat, lon) — tehát fordítva kell olvasni!
//
// KMZ = ZIP archív, benne doc.kml

import 'dart:typed_data';
import 'package:archive/archive.dart';
import 'package:flutter/material.dart';
import 'package:flutter_map/flutter_map.dart';
import 'package:latlong2/latlong.dart';
import 'package:terepi_seged/enums/layer_import_source_type.dart';
import 'package:uuid/uuid.dart';
import 'package:xml/xml.dart';

import '../models/imported_layer.dart';

class KmlParser {
  // Alapértelmezett stílusok — felülírhatók konstruktorban
  final Color defaultPolylineColor;
  final double defaultPolylineStroke;
  final Color defaultPolygonFillColor;
  final Color defaultPolygonBorderColor;
  final double defaultPolygonBorderStroke;
  final Color defaultMarkerColor;

  const KmlParser({
    this.defaultPolylineColor = const Color(0xCC1565C0), // kék
    this.defaultPolylineStroke = 3.0,
    this.defaultPolygonFillColor = const Color(0x4D1565C0),
    this.defaultPolygonBorderColor = const Color(0xCC1565C0),
    this.defaultPolygonBorderStroke = 1.5,
    this.defaultMarkerColor = Colors.red,
  });

  // ── KMZ (ZIP → KML) ──────────────────────────────────────────────

  ImportedLayer parseKmz(Uint8List bytes, String fileName,
      {String? id, bool isVisible = true}) {
    final archive = ZipDecoder().decodeBytes(bytes);

    ArchiveFile? kmlFile = archive.findFile('doc.kml');
    kmlFile ??= archive.files.firstWhere(
      (f) => f.name.toLowerCase().endsWith('.kml'),
      orElse: () => throw Exception('Nem található .kml fájl a KMZ-ben'),
    );

    final kmlString = String.fromCharCodes(kmlFile.content as List<int>);
    return _parse(kmlString, fileName, LayerImportSourceType.kmz,
        id: id, isVisible: isVisible);
  }

  // ── KML (szöveg) ──────────────────────────────────────────────────

  ImportedLayer parseKml(String kmlString, String fileName,
          {String? id, bool isVisible = true}) =>
      _parse(kmlString, fileName, LayerImportSourceType.kml,
          id: id, isVisible: isVisible);

  // ── Belső parse ───────────────────────────────────────────────────

  ImportedLayer _parse(
      String kmlString, String fileName, LayerImportSourceType source,
      {String? id, bool isVisible = true}) {
    final doc = XmlDocument.parse(kmlString);
    final markers = <Marker>[];
    final polylines = <Polyline>[];
    final polygons = <Polygon>[];

    for (final pm in doc.findAllElements('Placemark')) {
      final style = _style(pm);
      final label = pm.findElements('name').firstOrNull?.innerText.trim() ?? '';

      // Point
      final point = pm.findElements('Point').firstOrNull;
      if (point != null) {
        final pt = _singleCoord(point);
        if (pt != null) {
          markers.add(Marker(
            point: pt,
            width: label.isNotEmpty ? 120 : 24,
            height: label.isNotEmpty ? 40 : 24,
            alignment: Alignment.bottomCenter,
            child: _markerWidget(label, style.lineColor),
          ));
        }
      }

      // LineString
      final line = pm.findElements('LineString').firstOrNull;
      if (line != null) {
        final pts = _coordList(line);
        if (pts.isNotEmpty) {
          polylines.add(Polyline(
            points: pts,
            color: style.lineColor,
            strokeWidth: style.lineWidth,
            borderColor: style.lineColor.withOpacity(0.3),
            borderStrokeWidth: 1.0,
          ));
        }
      }

      // Polygon
      final poly = pm.findElements('Polygon').firstOrNull;
      if (poly != null) {
        final rings = _polygonRings(poly);
        if (rings.isNotEmpty) {
          polygons.add(Polygon(
            points: rings[0],
            holePointsList: rings.length > 1 ? rings.sublist(1) : null,
            color: style.fillColor,
            borderColor: style.lineColor,
            borderStrokeWidth: style.lineWidth,
            label: label.isNotEmpty ? label : null,
          ));
        }
      }

      // MultiGeometry — rekurzív feldolgozás az első egyszerű elemre
      final multi = pm.findElements('MultiGeometry').firstOrNull;
      if (multi != null) {
        for (final ls in multi.findElements('LineString')) {
          final pts = _coordList(ls);
          if (pts.isNotEmpty) {
            polylines.add(Polyline(
              points: pts,
              color: style.lineColor,
              strokeWidth: style.lineWidth,
            ));
          }
        }
      }
    }

    return ImportedLayer(
      id: id ?? const Uuid().v4(),
      isVisible: isVisible,
      name: fileName,
      sourceType: source,
      markers: markers,
      polylines: polylines,
      polygons: polygons,
      importedAt: DateTime.now(),
    );
  }

  // ── Koordináta parserek ───────────────────────────────────────────

  LatLng? _singleCoord(XmlElement geom) {
    final raw = geom.findElements('coordinates').firstOrNull?.innerText.trim();
    if (raw == null) return null;
    return _oneCoord(raw.split(RegExp(r'\s+')).first);
  }

  List<LatLng> _coordList(XmlElement geom) {
    final raw = geom.findElements('coordinates').firstOrNull?.innerText.trim();
    if (raw == null) return [];
    return raw
        .split(RegExp(r'\s+'))
        .map(_oneCoord)
        .whereType<LatLng>()
        .toList();
  }

  List<List<LatLng>> _polygonRings(XmlElement polygon) {
    final rings = <List<LatLng>>[];
    final outer = polygon
        .findElements('outerBoundaryIs')
        .firstOrNull
        ?.findElements('LinearRing')
        .firstOrNull;
    if (outer != null) rings.add(_coordList(outer));
    for (final inner in polygon.findElements('innerBoundaryIs')) {
      final ring = inner.findElements('LinearRing').firstOrNull;
      if (ring != null) rings.add(_coordList(ring));
    }
    return rings.where((r) => r.isNotEmpty).toList();
  }

  /// KML: lon,lat,alt → LatLng(lat, lon)
  LatLng? _oneCoord(String s) {
    final p = s.split(',');
    if (p.length < 2) return null;
    try {
      return LatLng(
        double.parse(p[1].trim()), // lat
        double.parse(p[0].trim()), // lon
      );
    } catch (_) {
      return null;
    }
  }

  // ── Stílus kiolvasás ──────────────────────────────────────────────

  _Style _style(XmlElement pm) {
    final style = pm.findElements('Style').firstOrNull;
    if (style == null) return _Style.defaults(this);

    Color lineColor = defaultPolylineColor;
    double lineWidth = defaultPolylineStroke;
    Color fillColor = defaultPolygonFillColor;

    final ls = style.findElements('LineStyle').firstOrNull;
    if (ls != null) {
      final c = ls.findElements('color').firstOrNull?.innerText;
      if (c != null) lineColor = _kmlColor(c);
      final w = ls.findElements('width').firstOrNull?.innerText;
      if (w != null) lineWidth = double.tryParse(w) ?? lineWidth;
    }

    final ps = style.findElements('PolyStyle').firstOrNull;
    if (ps != null) {
      final c = ps.findElements('color').firstOrNull?.innerText;
      if (c != null) fillColor = _kmlColor(c);
    }

    return _Style(
        lineColor: lineColor, lineWidth: lineWidth, fillColor: fillColor);
  }

  /// KML szín formátum: aabbggrr → Flutter Color(argb)
  Color _kmlColor(String s) {
    if (s.length != 8) return defaultPolylineColor;
    try {
      final a = int.parse(s.substring(0, 2), radix: 16);
      final b = int.parse(s.substring(2, 4), radix: 16);
      final g = int.parse(s.substring(4, 6), radix: 16);
      final r = int.parse(s.substring(6, 8), radix: 16);
      return Color.fromARGB(a, r, g, b);
    } catch (_) {
      return defaultPolylineColor;
    }
  }

  // ── Marker widget ─────────────────────────────────────────────────

  Widget _markerWidget(String label, Color color) {
    if (label.isEmpty) {
      return Icon(Icons.location_pin, color: color, size: 24);
    }
    return Column(mainAxisSize: MainAxisSize.min, children: [
      Container(
        padding: const EdgeInsets.symmetric(horizontal: 5, vertical: 2),
        decoration:
            BoxDecoration(color: color, borderRadius: BorderRadius.circular(4)),
        child: Text(label,
            style: const TextStyle(
                color: Colors.white, fontSize: 10, fontWeight: FontWeight.w600),
            overflow: TextOverflow.ellipsis),
      ),
      Icon(Icons.location_pin, color: color, size: 16),
    ]);
  }
}

class _Style {
  final Color lineColor;
  final double lineWidth;
  final Color fillColor;
  const _Style(
      {required this.lineColor,
      required this.lineWidth,
      required this.fillColor});
  factory _Style.defaults(KmlParser p) => _Style(
        lineColor: p.defaultPolylineColor,
        lineWidth: p.defaultPolylineStroke,
        fillColor: p.defaultPolygonFillColor,
      );
}
