
import { ActivityStats } from '../types';

interface GpxAnalysisResult {
  stats: ActivityStats;
  richSummary: string;
}

export const parseGpxFile = async (file: File): Promise<GpxAnalysisResult> => {
  return new Promise((resolve, reject) => {
    const reader = new FileReader();

    reader.onload = (e) => {
      try {
        const text = e.target?.result as string;
        const parser = new DOMParser();
        const xmlDoc = parser.parseFromString(text, 'text/xml');

        // Obsługa różnych wariantów tagu 'trkpt' (z namespace lub bez)
        let trkpts = Array.from(xmlDoc.getElementsByTagName('trkpt'));
        if (trkpts.length === 0) {
            // Spróbuj znaleźć po localName, jeśli namespace'y przeszkadzają
            const allElements = xmlDoc.getElementsByTagName('*');
            trkpts = Array.from(allElements).filter(el => el.localName === 'trkpt');
        }
        
        if (trkpts.length === 0) {
           throw new Error('Nie znaleziono punktów śledzenia (trkpt) w pliku GPX.');
        }

        // --- ZMIENNE AGREGUJĄCE ---
        let totalDistanceKm = 0;
        let totalElevationGain = 0;
        let maxElevation = -Infinity;
        let minElevation = Infinity;
        
        let startTime: Date | null = null;
        let endTime: Date | null = null;
        
        // Heart Rate & Cadence
        let totalHr = 0;
        let hrCount = 0;
        let maxHr = 0;
        let totalCadence = 0;
        let cadenceCount = 0;

        // Splits (KM) Logic
        const splits: { km: number; timeMs: number; avgHr: number; elevGain: number }[] = [];
        let currentSplitDistance = 0;
        let currentSplitTimeStart = 0;
        let currentSplitHrSum = 0;
        let currentSplitHrCount = 0;
        let currentSplitElevGain = 0;
        let splitIndex = 1;

        let lastLat: number | null = null;
        let lastLon: number | null = null;
        let lastEle: number | null = null;
        
        // Helper to get text content from child nodes loosely
        const getChildText = (parent: Element, tagName: string): string | null => {
            let el = parent.getElementsByTagName(tagName)[0];
            if (!el) {
                // Try finding by localName if strictly not found
                for(let i=0; i<parent.children.length; i++) {
                    if(parent.children[i].localName === tagName) {
                        el = parent.children[i];
                        break;
                    }
                }
            }
            return el ? el.textContent : null;
        };

        // --- ITERACJA PO PUNKTACH ---
        for (let i = 0; i < trkpts.length; i++) {
          const pt = trkpts[i];
          const lat = parseFloat(pt.getAttribute('lat') || '0');
          const lon = parseFloat(pt.getAttribute('lon') || '0');
          
          const eleStr = getChildText(pt, 'ele');
          const ele = eleStr ? parseFloat(eleStr) : NaN;
          
          const timeStr = getChildText(pt, 'time');
          
          // Extensions search for HR/Cadence
          // Przeszukujemy całe poddrzewo punktu w poszukiwaniu tagów hr/cad, bo struktura extensions bywa różna
          const allChilds = pt.getElementsByTagName('*');
          let hr = 0;
          let cad = 0;

          for (let j = 0; j < allChilds.length; j++) {
              const node = allChilds[j];
              const name = node.localName.toLowerCase();
              if (name === 'hr' || name === 'heartrate') {
                  const val = parseInt(node.textContent || '0');
                  if (!isNaN(val) && val > 0) hr = val;
              }
              if (name === 'cad' || name === 'cadence') {
                  const val = parseInt(node.textContent || '0');
                  if (!isNaN(val) && val > 0) cad = val;
              }
          }

          if (hr > 0) {
              totalHr += hr;
              hrCount++;
              if (hr > maxHr) maxHr = hr;
              currentSplitHrSum += hr;
              currentSplitHrCount++;
          }
          if (cad > 0) {
              totalCadence += cad;
              cadenceCount++;
          }

          let currentTime = 0;
          if (timeStr) {
            const d = new Date(timeStr);
            currentTime = d.getTime();
            if (!startTime) {
                startTime = d;
                currentSplitTimeStart = currentTime;
            }
            endTime = d;
          }

          // Elevation Min/Max
          if (!isNaN(ele)) {
              if (ele > maxElevation) maxElevation = ele;
              if (ele < minElevation) minElevation = ele;
          }

          // Distance & Calculation
          if (lastLat !== null && lastLon !== null) {
            const distKm = getDistanceFromLatLonInKm(lastLat, lastLon, lat, lon);
            totalDistanceKm += distKm;
            currentSplitDistance += distKm;

            // Elevation Gain
            if (lastEle !== null && !isNaN(ele) && ele > lastEle) {
                const gain = ele - lastEle;
                // Ignorujemy mikro-zmiany (szum GPS) poniżej 0.5m
                if (gain > 0.2) {
                    totalElevationGain += gain;
                    currentSplitElevGain += gain;
                }
            }

            // --- SPLIT LOGIC (Co 1 KM) ---
            if (currentSplitDistance >= 1.0) {
                const splitTimeMs = currentTime - currentSplitTimeStart;
                splits.push({
                    km: splitIndex,
                    timeMs: splitTimeMs,
                    avgHr: currentSplitHrCount > 0 ? Math.round(currentSplitHrSum / currentSplitHrCount) : 0,
                    elevGain: Math.round(currentSplitElevGain)
                });
                
                // Reset Split
                splitIndex++;
                currentSplitDistance = currentSplitDistance - 1.0; 
                currentSplitTimeStart = currentTime;
                currentSplitHrSum = 0;
                currentSplitHrCount = 0;
                currentSplitElevGain = 0;
            }
          }

          lastLat = lat;
          lastLon = lon;
          lastEle = isNaN(ele) ? lastEle : ele;
        }

        // --- PODSUMOWANIE ---
        const totalDurationMs = startTime && endTime ? (endTime.getTime() - startTime.getTime()) : 0;
        const avgHr = hrCount > 0 ? Math.round(totalHr / hrCount) : 0;
        const avgCadence = cadenceCount > 0 ? Math.round(totalCadence / cadenceCount) : 0;
        const avgPaceMs = totalDistanceKm > 0 ? totalDurationMs / totalDistanceKm : 0; // ms per km

        // Formatowanie statystyk do UI
        const uiStats: ActivityStats = {
          distance: `${totalDistanceKm.toFixed(2)} km`,
          duration: msToTime(totalDurationMs),
          elevation: `${Math.round(totalElevationGain)}m (Max: ${Math.round(maxElevation === -Infinity ? 0 : maxElevation)}m)`
        };

        // --- BUDOWANIE BOGATEGO RAPORTU DLA AI ---
        let report = `=== SZCZEGÓŁOWY RAPORT Z PLIKU GPX ===\n`;
        report += `PODSUMOWANIE:\n`;
        report += `- Dystans Całkowity: ${totalDistanceKm.toFixed(2)} km\n`;
        report += `- Czas Całkowity: ${msToTime(totalDurationMs)}\n`;
        report += `- Średnie Tempo: ${formatPace(avgPaceMs)} min/km\n`;
        report += `- Przewyższenia: +${Math.round(totalElevationGain)}m / Max Wysokość: ${Math.round(maxElevation === -Infinity ? 0 : maxElevation)}m\n`;
        
        if (avgHr > 0) {
            report += `- Tętno: Średnie ${avgHr} bpm / Max ${maxHr} bpm\n`;
        }
        if (avgCadence > 0) {
            report += `- Kadencja: ${avgCadence} spm\n`;
        }

        if (splits.length > 0) {
            report += `\nANALIZA MIĘDZYCZASÓW (SPLITS - CO 1 KM):\n`;
            splits.forEach(s => {
                const pace = formatPace(s.timeMs); // time for 1km = pace
                const hrStr = s.avgHr > 0 ? `, HR: ${s.avgHr} bpm` : '';
                const elevStr = s.elevGain > 5 ? `, Elev: +${s.elevGain}m` : ''; // show only significant gain
                report += `Km ${s.km}: ${pace} min/km${hrStr}${elevStr}\n`;
            });
        } else {
             report += `\n(Brak pełnych kilometrów do analizy międzyczasów)\n`;
        }

        // Zakończenie
        report += `\nWSKAZÓWKI DLA AI: Wykorzystaj te dane do opisu walki na trasie. Zauważ momenty kryzysu (zwolnienie tempa, wysokie tętno) oraz momenty "puszczenia" (zbiegi, przyspieszenie).`;

        resolve({
          stats: uiStats,
          richSummary: report
        });

      } catch (error) {
        reject(error);
      }
    };

    reader.onerror = () => reject(new Error('Błąd odczytu pliku'));
    reader.readAsText(file);
  });
};

// --- POMOCNICZE FUNKCJE MATEMATYCZNE ---

function getDistanceFromLatLonInKm(lat1: number, lon1: number, lat2: number, lon2: number) {
  const R = 6371; // Radius of the earth in km
  const dLat = deg2rad(lat2 - lat1);
  const dLon = deg2rad(lon2 - lon1);
  const a =
    Math.sin(dLat / 2) * Math.sin(dLat / 2) +
    Math.cos(deg2rad(lat1)) * Math.cos(deg2rad(lat2)) *
    Math.sin(dLon / 2) * Math.sin(dLon / 2);
  const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
  const d = R * c; 
  return d;
}

function deg2rad(deg: number) {
  return deg * (Math.PI / 180);
}

function msToTime(duration: number) {
    if (duration <= 0) return "0h 00m";
    const minutes = Math.floor((duration / (1000 * 60)) % 60);
    const hours = Math.floor((duration / (1000 * 60 * 60)) % 24);
    
    // Jeśli mniej niż godzina, pokaż minuty i sekundy (opcjonalnie, tu zostawiamy format h m)
    if (hours === 0) return `${minutes}m`;
    return `${hours}h ${minutes}m`;
}

function formatPace(msPerKm: number): string {
    if (msPerKm <= 0 || !isFinite(msPerKm)) return "-:--";
    const totalSeconds = Math.floor(msPerKm / 1000);
    const minutes = Math.floor(totalSeconds / 60);
    const seconds = totalSeconds % 60;
    return `${minutes}:${seconds.toString().padStart(2, '0')}`;
}
