import 'package:uuid/uuid.dart';

/// Kitűzési pont állapota.
enum StakeoutStatus { pending, staked, skipped }

/// Kitűzendő pont — szeizmikus line/station modellel.
///
/// A pont természetes azonosítója a (lineId, station) páros. A rekord a
/// TERV-koordinátákat ÉS a kitűzéskori MÉRT pozíciót is tárolja, az
/// inline/crossline eltérésekkel együtt — így az export nemcsak azt
/// dokumentálja, MIT tűztünk ki, hanem azt is, MILYEN pontossággal.
///
/// A séma szinkron-kész (uuid, updated_at, deleted_at, sync_status,
/// created_by, device_id) — a Supabase-oldal a 4. ütemben épül rá.
class StakeoutPoint {
  final int? id;
  final String uuid;
  final int projectId;

  // ── Identitás ────────────────────────────────────────────────────
  final String lineId; // szeizmikus vonal azonosító ('' = vonal nélküli)
  final int station; // numerikus állomásszám (kötelező!)
  final String name; // megjelenítendő név (alapból a station szövege)
  final String pointType; // geofon / forras / egyeb — SPS-re előkészítve
  final String source; // csv / geojson / supabase / offset / kezi

  // ── Terv-koordináták (mindkét rendszerben tárolva) ───────────────
  final double planEovY;
  final double planEovX;
  final double? planEovZ;
  final double planLat;
  final double planLon;

  // ── Állapot + mért adatok ────────────────────────────────────────
  final StakeoutStatus status;
  final double? measuredEovY;
  final double? measuredEovX;
  final double? measuredEovZ;
  final double? measuredLat;
  final double? measuredLon;
  final double?
      devInline; // vonal menti eltérés (m, + = station-növekedés felé)
  final double? devCrossline; // vonalra merőleges eltérés (m, + = jobbra)
  final double? devDz; // magassági eltérés (m, terv − mért)
  final int? fixQuality; // GGA quality tároláskor (4 = RTK fixed)
  final double? accuracy; // vízszintes hiba tároláskor (m)
  final double? tiltDeg; // rúd-dőlés tároláskor (később: libella-modul)
  final DateTime? stakedAt;

  // ── Eltolt (transzverzális) pont ─────────────────────────────────
  final bool isOffset;
  final String? parentUuid; // az eredeti tervpont uuid-ja
  final double? offsetDist; // eltolás nagysága (m)
  final double? offsetBearing; // eltolás iránya (fok, EOV-észak = 0)

  // ── Szinkron / audit ─────────────────────────────────────────────
  final String? createdBy;
  final String? deviceId;
  final DateTime createdAt;
  final DateTime updatedAt;
  final DateTime? deletedAt;
  final String syncStatus;

  StakeoutPoint({
    this.id,
    String? uuid,
    required this.projectId,
    this.lineId = '',
    required this.station,
    String? name,
    this.pointType = 'geofon',
    this.source = 'csv',
    required this.planEovY,
    required this.planEovX,
    this.planEovZ,
    required this.planLat,
    required this.planLon,
    this.status = StakeoutStatus.pending,
    this.measuredEovY,
    this.measuredEovX,
    this.measuredEovZ,
    this.measuredLat,
    this.measuredLon,
    this.devInline,
    this.devCrossline,
    this.devDz,
    this.fixQuality,
    this.accuracy,
    this.tiltDeg,
    this.stakedAt,
    this.isOffset = false,
    this.parentUuid,
    this.offsetDist,
    this.offsetBearing,
    this.createdBy,
    this.deviceId,
    DateTime? createdAt,
    DateTime? updatedAt,
    this.deletedAt,
    this.syncStatus = 'pending',
  })  : uuid = uuid ?? const Uuid().v4(),
        name = name ?? station.toString(),
        createdAt = createdAt ?? DateTime.now(),
        updatedAt = updatedAt ?? DateTime.now();

  /// Teljes azonosító megjelenítésre: "L1024 · 105" vagy csak "105".
  String get displayId => lineId.isEmpty ? name : '$lineId · $name';

  StakeoutPoint copyWith({
    int? id,
    StakeoutStatus? status,
    double? measuredEovY,
    double? measuredEovX,
    double? measuredEovZ,
    double? measuredLat,
    double? measuredLon,
    double? devInline,
    double? devCrossline,
    double? devDz,
    int? fixQuality,
    double? accuracy,
    double? tiltDeg,
    DateTime? stakedAt,
    DateTime? updatedAt,
    String? syncStatus,
  }) {
    return StakeoutPoint(
      id: id ?? this.id,
      uuid: uuid,
      projectId: projectId,
      lineId: lineId,
      station: station,
      name: name,
      pointType: pointType,
      source: source,
      planEovY: planEovY,
      planEovX: planEovX,
      planEovZ: planEovZ,
      planLat: planLat,
      planLon: planLon,
      status: status ?? this.status,
      measuredEovY: measuredEovY ?? this.measuredEovY,
      measuredEovX: measuredEovX ?? this.measuredEovX,
      measuredEovZ: measuredEovZ ?? this.measuredEovZ,
      measuredLat: measuredLat ?? this.measuredLat,
      measuredLon: measuredLon ?? this.measuredLon,
      devInline: devInline ?? this.devInline,
      devCrossline: devCrossline ?? this.devCrossline,
      devDz: devDz ?? this.devDz,
      fixQuality: fixQuality ?? this.fixQuality,
      accuracy: accuracy ?? this.accuracy,
      tiltDeg: tiltDeg ?? this.tiltDeg,
      stakedAt: stakedAt ?? this.stakedAt,
      isOffset: isOffset,
      parentUuid: parentUuid,
      offsetDist: offsetDist,
      offsetBearing: offsetBearing,
      createdBy: createdBy,
      deviceId: deviceId,
      createdAt: createdAt,
      updatedAt: updatedAt ?? DateTime.now(),
      deletedAt: deletedAt,
      syncStatus: syncStatus ?? 'pending',
    );
  }

  Map<String, dynamic> toMap() => {
        if (id != null) 'id': id,
        'uuid': uuid,
        'project_id': projectId,
        'line_id': lineId,
        'station': station,
        'name': name,
        'point_type': pointType,
        'source': source,
        'plan_eov_y': planEovY,
        'plan_eov_x': planEovX,
        'plan_eov_z': planEovZ,
        'plan_lat': planLat,
        'plan_lon': planLon,
        'status': status.name,
        'measured_eov_y': measuredEovY,
        'measured_eov_x': measuredEovX,
        'measured_eov_z': measuredEovZ,
        'measured_lat': measuredLat,
        'measured_lon': measuredLon,
        'dev_inline': devInline,
        'dev_crossline': devCrossline,
        'dev_dz': devDz,
        'fix_quality': fixQuality,
        'accuracy': accuracy,
        'tilt_deg': tiltDeg,
        'staked_at': stakedAt?.toIso8601String(),
        'is_offset': isOffset ? 1 : 0,
        'parent_uuid': parentUuid,
        'offset_dist': offsetDist,
        'offset_bearing': offsetBearing,
        'created_by': createdBy,
        'device_id': deviceId,
        'created_at': createdAt.toIso8601String(),
        'updated_at': updatedAt.toIso8601String(),
        'deleted_at': deletedAt?.toIso8601String(),
        'sync_status': syncStatus,
      };

  factory StakeoutPoint.fromMap(Map<String, dynamic> m) => StakeoutPoint(
        id: m['id'] as int?,
        uuid: m['uuid'] as String,
        projectId: m['project_id'] as int,
        lineId: (m['line_id'] as String?) ?? '',
        station: m['station'] as int,
        name: (m['name'] as String?) ?? '${m['station']}',
        pointType: (m['point_type'] as String?) ?? 'geofon',
        source: (m['source'] as String?) ?? 'csv',
        planEovY: (m['plan_eov_y'] as num).toDouble(),
        planEovX: (m['plan_eov_x'] as num).toDouble(),
        planEovZ: (m['plan_eov_z'] as num?)?.toDouble(),
        planLat: (m['plan_lat'] as num).toDouble(),
        planLon: (m['plan_lon'] as num).toDouble(),
        status: StakeoutStatus.values.firstWhere((s) => s.name == m['status'],
            orElse: () => StakeoutStatus.pending),
        measuredEovY: (m['measured_eov_y'] as num?)?.toDouble(),
        measuredEovX: (m['measured_eov_x'] as num?)?.toDouble(),
        measuredEovZ: (m['measured_eov_z'] as num?)?.toDouble(),
        measuredLat: (m['measured_lat'] as num?)?.toDouble(),
        measuredLon: (m['measured_lon'] as num?)?.toDouble(),
        devInline: (m['dev_inline'] as num?)?.toDouble(),
        devCrossline: (m['dev_crossline'] as num?)?.toDouble(),
        devDz: (m['dev_dz'] as num?)?.toDouble(),
        fixQuality: m['fix_quality'] as int?,
        accuracy: (m['accuracy'] as num?)?.toDouble(),
        tiltDeg: (m['tilt_deg'] as num?)?.toDouble(),
        stakedAt: m['staked_at'] != null
            ? DateTime.tryParse(m['staked_at'] as String)
            : null,
        isOffset: (m['is_offset'] as int? ?? 0) == 1,
        parentUuid: m['parent_uuid'] as String?,
        offsetDist: (m['offset_dist'] as num?)?.toDouble(),
        offsetBearing: (m['offset_bearing'] as num?)?.toDouble(),
        createdBy: m['created_by'] as String?,
        deviceId: m['device_id'] as String?,
        createdAt: DateTime.tryParse((m['created_at'] as String?) ?? '') ??
            DateTime.now(),
        updatedAt: DateTime.tryParse((m['updated_at'] as String?) ?? '') ??
            DateTime.now(),
        deletedAt: m['deleted_at'] != null
            ? DateTime.tryParse(m['deleted_at'] as String)
            : null,
        syncStatus: (m['sync_status'] as String?) ?? 'pending',
      );
}
