import gpxParserBuilder from 'gpx-parser-builder'

export const parseGPXFile = async (file) => {
  return new Promise((resolve, reject) => {
    const reader = new FileReader()
    
    reader.onload = (e) => {
      try {
        const gpxContent = e.target.result
        const gpx = gpxParserBuilder.parse(gpxContent)
        
        console.log('Parsed GPX structure:', gpx)
        
        // Extract track data - handle both tracks and routes
        let tracks = []
        
        // Handle tracks (trk property)
        if (gpx.trk && Array.isArray(gpx.trk)) {
          tracks = gpx.trk.map(track => {
            const segments = track.trkseg?.map(segment => {
              const points = segment.trkpt?.map(point => ({
                lat: parseFloat(point.$.lat),
                lng: parseFloat(point.$.lon),
                elevation: point.ele ? parseFloat(point.ele) : null,
                time: point.time ? point.time : null
              })) || []
              return { points }
            }) || []
            
            return {
              name: track.name || `Track ${tracks.length + 1}`,
              segments,
              metadata: {
                fileName: file.name,
                fileSize: file.size,
                uploadDate: new Date().toISOString()
              }
            }
          })
        }
        
        // Handle routes if no tracks (rte property)
        if (tracks.length === 0 && gpx.rte && Array.isArray(gpx.rte)) {
          tracks = gpx.rte.map(route => {
            const points = route.rtept?.map(point => ({
              lat: parseFloat(point.$.lat),
              lng: parseFloat(point.$.lon),
              elevation: point.ele ? parseFloat(point.ele[0]) : null,
              time: point.time ? point.time[0] : null
            })) || []
            
            return {
              name: route.name || file.name,
              segments: [{ points }],
              metadata: {
                fileName: file.name,
                fileSize: file.size,
                uploadDate: new Date().toISOString()
              }
            }
          })
        }
        
        // Handle waypoints if no tracks or routes (wpt property)
        if (tracks.length === 0 && gpx.wpt && Array.isArray(gpx.wpt)) {
          const points = gpx.wpt.map(waypoint => ({
            lat: parseFloat(waypoint.$.lat),
            lng: parseFloat(waypoint.$.lon),
            elevation: waypoint.ele ? parseFloat(waypoint.ele[0]) : null,
            time: waypoint.time ? waypoint.time[0] : null
          }))
          
          tracks = [{
            name: gpx.metadata?.name || file.name,
            segments: [{ points }],
            metadata: {
              fileName: file.name,
              fileSize: file.size,
              uploadDate: new Date().toISOString()
            }
          }]
        }
        
        // Calculate track statistics
        const processedTracks = tracks.map(track => {
          const stats = calculateTrackStats(track)
          return { ...track, stats }
        })
        
        resolve(processedTracks)
      } catch (error) {
        reject(new Error(`Failed to parse GPX file: ${error.message}`))
      }
    }
    
    reader.onerror = () => {
      reject(new Error('Failed to read file'))
    }
    
    reader.readAsText(file)
  })
}

const calculateTrackStats = (track) => {
  if (!track.segments || track.segments.length === 0) {
    return {
      distance: 0,
      duration: 0,
      avgSpeed: 0,
      elevationGain: 0,
      startTime: null,
      endTime: null
    }
  }
  
  let totalDistance = 0
  let totalElevationGain = 0
  let startTime = null
  let endTime = null
  let prevPoint = null
  
  track.segments.forEach(segment => {
    segment.points.forEach(point => {
      // Track time bounds
      if (point.time) {
        const pointTime = point.time instanceof Date ? point.time : new Date(point.time)
        if (!startTime || pointTime < startTime) startTime = pointTime
        if (!endTime || pointTime > endTime) endTime = pointTime
      }
      
      // Calculate distance and elevation
      if (prevPoint) {
        totalDistance += calculateDistance(prevPoint, point)
        
        if (point.elevation && prevPoint.elevation) {
          const elevDiff = point.elevation - prevPoint.elevation
          if (elevDiff > 0) totalElevationGain += elevDiff
        }
      }
      
      prevPoint = point
    })
  })
  
  const duration = startTime && endTime ? 
    (endTime.getTime() - startTime.getTime()) / 1000 : 0 // seconds
  
  const avgSpeed = duration > 0 ? (totalDistance / duration) * 3.6 : 0 // km/h
  
  return {
    distance: Math.round(totalDistance), // meters
    duration: Math.round(duration), // seconds
    avgSpeed: Math.round(avgSpeed * 10) / 10, // km/h
    elevationGain: Math.round(totalElevationGain), // meters
    startTime: startTime ? startTime.toISOString() : null,
    endTime: endTime ? endTime.toISOString() : null
  }
}

// Haversine formula for distance between two points
const calculateDistance = (point1, point2) => {
  const R = 6371e3 // Earth's radius in meters
  const φ1 = point1.lat * Math.PI / 180
  const φ2 = point2.lat * Math.PI / 180
  const Δφ = (point2.lat - point1.lat) * Math.PI / 180
  const Δλ = (point2.lng - point1.lng) * Math.PI / 180

  const a = Math.sin(Δφ/2) * Math.sin(Δφ/2) +
          Math.cos(φ1) * Math.cos(φ2) *
          Math.sin(Δλ/2) * Math.sin(Δλ/2)
  const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a))

  return R * c
}