/**
 * Utility functions for SchoolCompare
 */

import type { School, MetricDefinition } from './types';

// ============================================================================
// String Utilities
// ============================================================================

/**
 * Create a URL-friendly slug from a string
 */
export function slugify(text: string): string {
  return text
    .toLowerCase()
    .replace(/[^\w\s-]/g, '')
    .replace(/\s+/g, '-')
    .replace(/-+/g, '-')
    .trim();
}

/**
 * Escape HTML to prevent XSS
 */
export function escapeHtml(text: string): string {
  const div = document.createElement('div');
  div.textContent = text;
  return div.innerHTML;
}

/**
 * Truncate text to a maximum length
 */
export function truncate(text: string, maxLength: number): string {
  if (text.length <= maxLength) return text;
  return text.slice(0, maxLength).trim() + '...';
}

// ============================================================================
// Number Formatting
// ============================================================================

/**
 * Format a number as a percentage
 */
export function formatPercentage(value: number | null | undefined, decimals: number = 1): string {
  if (value === null || value === undefined) return 'N/A';
  return `${value.toFixed(decimals)}%`;
}

/**
 * Format a progress score (can be negative)
 */
export function formatProgress(value: number | null | undefined, decimals: number = 1): string {
  if (value === null || value === undefined) return 'N/A';
  const formatted = value.toFixed(decimals);
  return value > 0 ? `+${formatted}` : formatted;
}

/**
 * Format a score (e.g., test scores)
 */
export function formatScore(value: number | null | undefined, decimals: number = 1): string {
  if (value === null || value === undefined) return 'N/A';
  return value.toFixed(decimals);
}

/**
 * Format a metric value based on its type
 */
export function formatMetricValue(
  value: number | null | undefined,
  format: MetricDefinition['format']
): string {
  switch (format) {
    case 'percentage':
      return formatPercentage(value);
    case 'progress':
      return formatProgress(value);
    case 'score':
      return formatScore(value);
    default:
      return value?.toString() || 'N/A';
  }
}

// ============================================================================
// Trend Analysis
// ============================================================================

/**
 * Calculate the trend between two values
 */
export function calculateTrend(
  current: number | null | undefined,
  previous: number | null | undefined
): 'up' | 'down' | 'stable' {
  if (current === null || current === undefined || previous === null || previous === undefined) {
    return 'stable';
  }

  const diff = current - previous;

  if (Math.abs(diff) < 0.5) return 'stable'; // Within 0.5% is considered stable
  return diff > 0 ? 'up' : 'down';
}

/**
 * Calculate percentage change
 */
export function calculateChange(
  current: number | null | undefined,
  previous: number | null | undefined
): number | null {
  if (current === null || current === undefined || previous === null || previous === undefined) {
    return null;
  }

  return current - previous;
}

// ============================================================================
// Statistical Functions
// ============================================================================

/**
 * Calculate the average of an array of numbers
 */
export function average(values: (number | null)[]): number | null {
  const validValues = values.filter((v): v is number => v !== null && !isNaN(v));
  if (validValues.length === 0) return null;

  const sum = validValues.reduce((acc, val) => acc + val, 0);
  return sum / validValues.length;
}

/**
 * Calculate the standard deviation
 */
export function standardDeviation(values: (number | null)[]): number | null {
  const validValues = values.filter((v): v is number => v !== null && !isNaN(v));
  if (validValues.length === 0) return null;

  const avg = average(validValues);
  if (avg === null) return null;

  const squareDiffs = validValues.map((value) => Math.pow(value - avg, 2));
  const avgSquareDiff = average(squareDiffs);

  return avgSquareDiff !== null ? Math.sqrt(avgSquareDiff) : null;
}

/**
 * Calculate variability label based on standard deviation
 */
export function getVariabilityLabel(stdDev: number | null): string {
  if (stdDev === null) return 'Unknown';
  if (stdDev < 2) return 'Very Stable';
  if (stdDev < 5) return 'Stable';
  if (stdDev < 10) return 'Moderate';
  return 'Variable';
}

// ============================================================================
// Validation
// ============================================================================

/**
 * Validate UK postcode format
 */
export function isValidPostcode(postcode: string): boolean {
  const postcodeRegex = /^[A-Z]{1,2}[0-9][A-Z0-9]?\s*[0-9][A-Z]{2}$/i;
  return postcodeRegex.test(postcode.trim());
}

/**
 * Validate URN (Unique Reference Number)
 */
export function isValidUrn(urn: number | string): boolean {
  const urnNumber = typeof urn === 'string' ? parseInt(urn, 10) : urn;
  return !isNaN(urnNumber) && urnNumber >= 100000 && urnNumber <= 999999;
}

// ============================================================================
// Debounce
// ============================================================================

/**
 * Debounce a function call
 */
export function debounce<T extends (...args: any[]) => any>(
  func: T,
  wait: number
): (...args: Parameters<T>) => void {
  let timeout: NodeJS.Timeout | null = null;

  return function executedFunction(...args: Parameters<T>) {
    const later = () => {
      timeout = null;
      func(...args);
    };

    if (timeout) {
      clearTimeout(timeout);
    }

    timeout = setTimeout(later, wait);
  };
}

// ============================================================================
// Color Utilities
// ============================================================================

/**
 * Chart color palette (consistent with vanilla JS app)
 */
export const CHART_COLORS = [
  'rgb(75, 192, 192)',
  'rgb(255, 99, 132)',
  'rgb(54, 162, 235)',
  'rgb(255, 206, 86)',
  'rgb(153, 102, 255)',
  'rgb(255, 159, 64)',
  'rgb(201, 203, 207)',
  'rgb(255, 0, 255)',
];

/**
 * Get a color from the palette by index
 */
export function getChartColor(index: number): string {
  return CHART_COLORS[index % CHART_COLORS.length];
}

/**
 * Convert RGB color to RGBA with opacity
 */
export function rgbToRgba(rgb: string, alpha: number): string {
  return rgb.replace('rgb', 'rgba').replace(')', `, ${alpha})`);
}

/**
 * Get trend color based on direction
 */
export function getTrendColor(trend: 'up' | 'down' | 'stable'): string {
  switch (trend) {
    case 'up':
      return '#22c55e'; // green
    case 'down':
      return '#ef4444'; // red
    case 'stable':
      return '#6b7280'; // gray
  }
}

// ============================================================================
// Local Storage Utilities
// ============================================================================

/**
 * Safely get item from localStorage
 */
export function getFromLocalStorage<T>(key: string, defaultValue: T): T {
  if (typeof window === 'undefined') return defaultValue;

  try {
    const item = window.localStorage.getItem(key);
    return item ? JSON.parse(item) : defaultValue;
  } catch (error) {
    console.error(`Error reading from localStorage key "${key}":`, error);
    return defaultValue;
  }
}

/**
 * Safely set item in localStorage
 */
export function setToLocalStorage<T>(key: string, value: T): void {
  if (typeof window === 'undefined') return;

  try {
    window.localStorage.setItem(key, JSON.stringify(value));
  } catch (error) {
    console.error(`Error writing to localStorage key "${key}":`, error);
  }
}

/**
 * Remove item from localStorage
 */
export function removeFromLocalStorage(key: string): void {
  if (typeof window === 'undefined') return;

  try {
    window.localStorage.removeItem(key);
  } catch (error) {
    console.error(`Error removing from localStorage key "${key}":`, error);
  }
}

// ============================================================================
// URL Utilities
// ============================================================================

/**
 * Build URL with query parameters
 */
export function buildUrl(base: string, params: Record<string, any>): string {
  const url = new URL(base, window.location.origin);

  Object.entries(params).forEach(([key, value]) => {
    if (value !== undefined && value !== null && value !== '') {
      url.searchParams.set(key, String(value));
    }
  });

  return url.pathname + url.search;
}

/**
 * Parse query string into object
 */
export function parseQueryString(search: string): Record<string, string> {
  const params = new URLSearchParams(search);
  const result: Record<string, string> = {};

  params.forEach((value, key) => {
    result[key] = value;
  });

  return result;
}

// ============================================================================
// Date Utilities
// ============================================================================

/**
 * Format academic year (e.g., 2023 -> "2023/24")
 */
export function formatAcademicYear(year: number): string {
  const nextYear = (year + 1).toString().slice(-2);
  return `${year}/${nextYear}`;
}

/**
 * Get current academic year
 */
export function getCurrentAcademicYear(): number {
  const now = new Date();
  const year = now.getFullYear();
  const month = now.getMonth();

  // Academic year starts in September (month 8)
  return month >= 8 ? year : year - 1;
}
