/**
 * Data Format Library
 * Centralized formatting functions for time, distance, pace, HR, and other fitness metrics
 */

/**
 * Converts speed from meters/second to minutes/km.
 * @param ms Speed in meters/second
 * @returns Pace in minutes/km (as a string "mm:ss")
 */
export function msToMinPerKm(ms: number | string): string {
  if (typeof ms === "string") {
    ms = parseFloat(ms);
  }
  if (ms <= 0 || isNaN(ms)) return "-";
  const pace = 1000 / (60 * ms); // minutes per km
  const minutes = Math.floor(pace);
  const seconds = Math.round((pace - minutes) * 60);
  return `${minutes}:${seconds.toString().padStart(2, "0")}`;
}

/**
 * Converts distance from meters to kilometers or meters based on value.
 * @param meters Distance in meters
 * @returns Formatted distance string with unit
 */
export function mToKm(meters: number | string): string {
  if (typeof meters === "string") {
    meters = parseFloat(meters);
  }
  if (meters <= 0 || isNaN(meters)) return "-";
  if (meters >= 1000) {
    return `${(meters / 1000).toFixed(1)} km`;
  }
  return `${Math.round(meters)} m`;
}

/**
 * Formats duration from seconds to various time formats.
 * @param seconds Duration in seconds
 * @param format Format type: 'hh:mm:ss', 'mm:ss', 'humanReadable'
 * @returns Formatted duration string
 */
export function formatDuration(seconds: number | string, format: 'hh:mm:ss' | 'mm:ss' | 'humanReadable' = 'hh:mm:ss'): string {
  if (typeof seconds === "string") {
    seconds = parseFloat(seconds);
  }
  if (seconds <= 0 || isNaN(seconds)) return "-";
  
  const hours = Math.floor(seconds / 3600);
  const minutes = Math.floor((seconds % 3600) / 60);
  const secs = Math.round(seconds % 60);
  
  switch (format) {
    case 'mm:ss':
      const totalMinutes = Math.floor(seconds / 60);
      return `${totalMinutes}:${secs.toString().padStart(2, '0')}`;
    
    case 'humanReadable':
      const parts: string[] = [];
      if (hours > 0) parts.push(`${hours}h`);
      if (minutes > 0) parts.push(`${minutes}m`);
      if (secs > 0 || parts.length === 0) parts.push(`${secs}s`);
      return parts.join(' ');
    
    case 'hh:mm:ss':
    default:
      if (hours > 0) {
        return `${hours}:${minutes.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`;
      }
      return `${minutes}:${secs.toString().padStart(2, '0')}`;
  }
}

/**
 * Formats heart rate value.
 * @param hr Heart rate value
 * @param includeUnit Whether to include 'bpm' unit
 * @returns Formatted heart rate string
 */
export function formatHeartRate(hr: number | string, includeUnit: boolean = true): string {
  if (typeof hr === "string") {
    hr = parseFloat(hr);
  }
  if (hr <= 0 || isNaN(hr)) return "-";
  
  const rounded = Math.round(hr);
  return includeUnit ? `${rounded} bpm` : `${rounded}`;
}

/**
 * Calculates and formats heart rate zones based on max HR.
 * @param currentHR Current heart rate
 * @param maxHR Maximum heart rate (if not provided, uses 220 - age)
 * @param age User's age (used if maxHR not provided)
 * @returns Object with zone information
 */
export function getHeartRateZone(currentHR: number, maxHR?: number, age?: number): {
  zone: number;
  zoneName: string;
  percentage: number;
  description: string;
} {
  // Calculate max HR if not provided
  if (!maxHR && age) {
    maxHR = 220 - age;
  }
  
  if (!maxHR || currentHR <= 0) {
    return {
      zone: 0,
      zoneName: 'Invalid',
      percentage: 0,
      description: 'Invalid heart rate data'
    };
  }
  
  const percentage = (currentHR / maxHR) * 100;
  
  if (percentage < 50) {
    return { zone: 0, zoneName: 'Rest', percentage, description: 'Very light activity' };
  } else if (percentage < 60) {
    return { zone: 1, zoneName: 'Warm-up', percentage, description: 'Light activity' };
  } else if (percentage < 70) {
    return { zone: 2, zoneName: 'Fat Burn', percentage, description: 'Moderate activity' };
  } else if (percentage < 80) {
    return { zone: 3, zoneName: 'Cardio', percentage, description: 'Hard activity' };
  } else if (percentage < 90) {
    return { zone: 4, zoneName: 'Peak', percentage, description: 'Very hard activity' };
  } else {
    return { zone: 5, zoneName: 'Maximum', percentage, description: 'Maximum effort' };
  }
}

/**
 * Formats speed value to km/h or m/s.
 * @param metersPerSecond Speed in m/s
 * @param unit Output unit: 'kmh' or 'ms'
 * @returns Formatted speed string with unit
 */
export function formatSpeed(metersPerSecond: number | string, unit: 'kmh' | 'ms' = 'kmh'): string {
  if (typeof metersPerSecond === "string") {
    metersPerSecond = parseFloat(metersPerSecond);
  }
  if (metersPerSecond <= 0 || isNaN(metersPerSecond)) return "-";
  
  if (unit === 'kmh') {
    const kmh = metersPerSecond * 3.6;
    return `${kmh.toFixed(1)} km/h`;
  }
  
  return `${metersPerSecond.toFixed(1)} m/s`;
}

/**
 * Formats elevation/altitude values.
 * @param meters Elevation in meters
 * @param unit Output unit: 'm' or 'ft'
 * @returns Formatted elevation string with unit
 */
export function formatElevation(meters: number | string, unit: 'm' | 'ft' = 'm'): string {
  if (typeof meters === "string") {
    meters = parseFloat(meters);
  }
  if (isNaN(meters)) return "-";
  
  if (unit === 'ft') {
    const feet = meters * 3.28084;
    return `${Math.round(feet)} ft`;
  }
  
  return `${Math.round(meters)} m`;
}

/**
 * Formats power output (for cycling).
 * @param watts Power in watts
 * @param includeUnit Whether to include 'W' unit
 * @returns Formatted power string
 */
export function formatPower(watts: number | string, includeUnit: boolean = true): string {
  if (typeof watts === "string") {
    watts = parseFloat(watts);
  }
  if (watts < 0 || isNaN(watts)) return "-";
  
  const rounded = Math.round(watts);
  return includeUnit ? `${rounded} W` : `${rounded}`;
}

/**
 * Formats cadence (steps/min for running, rpm for cycling).
 * @param cadence Cadence value
 * @param type Activity type: 'running' or 'cycling'
 * @returns Formatted cadence string with appropriate unit
 */
export function formatCadence(cadence: number | string, type: 'running' | 'cycling' = 'running'): string {
  if (typeof cadence === "string") {
    cadence = parseFloat(cadence);
  }
  if (cadence <= 0 || isNaN(cadence)) return "-";
  
  const rounded = Math.round(cadence);
  const unit = type === 'running' ? 'spm' : 'rpm';
  return `${rounded} ${unit}`;
}

/**
 * Formats temperature values.
 * @param celsius Temperature in Celsius
 * @param unit Output unit: 'C' or 'F'
 * @returns Formatted temperature string with unit
 */
export function formatTemperature(celsius: number | string, unit: 'C' | 'F' = 'C'): string {
  if (typeof celsius === "string") {
    celsius = parseFloat(celsius);
  }
  if (isNaN(celsius)) return "-";
  
  if (unit === 'F') {
    const fahrenheit = (celsius * 9/5) + 32;
    return `${Math.round(fahrenheit)}°F`;
  }
  
  return `${Math.round(celsius)}°C`;
}

/**
 * Formats date to various formats.
 * @param date Date string or Date object
 * @param format Format type: 'short', 'long', 'iso', 'relative'
 * @returns Formatted date string
 */
export function formatDate(date: string | Date, format: 'short' | 'long' | 'iso' | 'relative' = 'short'): string {
  if (!date) return '-';
  
  try {
    const dateObj = typeof date === 'string' ? new Date(date) : date;
    
    if (isNaN(dateObj.getTime())) return '-';
    
    switch (format) {
      case 'short':
        return dateObj.toLocaleDateString('en-GB', { 
          day: '2-digit',
          month: '2-digit',
          year: '2-digit'
        });
      
      case 'long':
        return dateObj.toLocaleDateString('en-US', { 
          weekday: 'long',
          year: 'numeric',
          month: 'long',
          day: 'numeric'
        });
      
      case 'iso':
        return dateObj.toISOString();
      
      case 'relative':
        const now = new Date();
        const diffMs = now.getTime() - dateObj.getTime();
        const diffDays = Math.floor(diffMs / (1000 * 60 * 60 * 24));
        
        if (diffDays === 0) return 'Today';
        if (diffDays === 1) return 'Yesterday';
        if (diffDays < 7) return `${diffDays} days ago`;
        if (diffDays < 30) return `${Math.floor(diffDays / 7)} weeks ago`;
        if (diffDays < 365) return `${Math.floor(diffDays / 30)} months ago`;
        return `${Math.floor(diffDays / 365)} years ago`;
      
      default:
        return dateObj.toLocaleDateString();
    }
  } catch (error) {
    return '-';
  }
}

/**
 * Formats calories burned.
 * @param calories Calories value
 * @param includeUnit Whether to include 'cal' unit
 * @returns Formatted calories string
 */
export function formatCalories(calories: number | string, includeUnit: boolean = true): string {
  if (typeof calories === "string") {
    calories = parseFloat(calories);
  }
  if (calories < 0 || isNaN(calories)) return "-";
  
  const rounded = Math.round(calories);
  return includeUnit ? `${rounded} cal` : `${rounded}`;
}

/**
 * Converts and formats pace between different units.
 * @param value Pace value
 * @param fromUnit Input unit: 'min/km', 'min/mi', 's/m'
 * @param toUnit Output unit: 'min/km', 'min/mi', 's/m'
 * @returns Formatted pace string
 */
export function convertPace(value: number, fromUnit: 'min/km' | 'min/mi' | 's/m', toUnit: 'min/km' | 'min/mi' | 's/m'): string {
  // First convert to seconds per meter (common unit)
  let secondsPerMeter: number;
  
  switch (fromUnit) {
    case 'min/km':
      secondsPerMeter = (value * 60) / 1000;
      break;
    case 'min/mi':
      secondsPerMeter = (value * 60) / 1609.344;
      break;
    case 's/m':
      secondsPerMeter = value;
      break;
  }
  
  // Then convert to target unit
  switch (toUnit) {
    case "min/km": {
      const minPerKm = (secondsPerMeter * 1000) / 60;
      const minutes = Math.floor(minPerKm);
      const seconds = Math.round((minPerKm - minutes) * 60);
      return `${minutes}:${seconds.toString().padStart(2, "0")}`;
    }
    case "min/mi": {
      const minPerMi = (secondsPerMeter * 1609.344) / 60;
      const minutes = Math.floor(minPerMi);
      const seconds = Math.round((minPerMi - minutes) * 60);
      return `${minutes}:${seconds.toString().padStart(2, "0")}`;
    }
    case "s/m":
      return secondsPerMeter.toFixed(2);
  }
}
