/**
 * Centralized Date Utilities for Time Tracking
 *
 * This module provides consistent date handling across local and production environments.
 * It ensures proper timezone conversion between UTC and IST (Indian Standard Time).
 *
 * Key Features:
 * - Consistent UTC to IST conversion
 * - Proper handling of datetime-local inputs
 * - Environment-agnostic date parsing
 * - Reliable duration calculations
 */

import { parseISO, format, differenceInSeconds, isValid } from "date-fns";

// IST is UTC+5:30
const IST_OFFSET_MINUTES = 5 * 60 + 30; // 330 minutes

/**
 * Converts a UTC date to IST
 */
/**
 * Convert UTC → IST by adding +5:30 offset
 */
export const utcToIST = (date: Date): Date => {
  return new Date(date.getTime() + 5.5 * 60 * 60 * 1000);
};

/**
 * Converts an IST date to UTC
 */
export const istToUTC = (istDate: Date): Date => {
  const istTime = istDate.getTime();
  const utcTime = istTime - IST_OFFSET_MINUTES * 60 * 1000;
  return new Date(utcTime);
};

/**
 * Parses a date string consistently across environments
 * Handles both ISO strings and local datetime strings
 */
export const parseDateTime = (dateString: string): Date => {
  if (!dateString) {
    throw new Error("Date string is required");
  }

  try {
    // Check if it's a local datetime string (YYYY-MM-DDTHH:MM:SS without timezone)
    if (
      dateString.includes("T") &&
      !dateString.includes("Z") &&
      !dateString.includes("+") &&
      !dateString.includes("-", 10)
    ) {
      // Local datetime string - treat as IST and convert to proper Date object
      const date = new Date(dateString);
      if (!isValid(date)) {
        throw new Error(`Invalid local datetime: ${dateString}`);
      }
      return date;
    }

    // ISO string with timezone info - parse normally
    const date = parseISO(dateString);
    if (!isValid(date)) {
      throw new Error(`Invalid ISO date: ${dateString}`);
    }
    return date;
  } catch (error) {
    console.error("Error parsing date:", dateString, error);
    throw new Error(`Failed to parse date: ${dateString}`);
  }
};

/**
 * Formats a date for datetime-local input (always in local IST)
 */
export const formatForDateTimeLocal = (date: Date): string => {
  // Ensure we're working with IST
  const istDate =
    date.getTimezoneOffset() === -IST_OFFSET_MINUTES ? date : utcToIST(date);

  const year = istDate.getFullYear();
  const month = String(istDate.getMonth() + 1).padStart(2, "0");
  const day = String(istDate.getDate()).padStart(2, "0");
  const hours = String(istDate.getHours()).padStart(2, "0");
  const minutes = String(istDate.getMinutes()).padStart(2, "0");

  return `${year}-${month}-${day}T${hours}:${minutes}`;
};

/**
 * Formats a date for API submission (local datetime with seconds)
 */
export const formatForAPI = (dateTimeString: string): string => {
  if (!dateTimeString) return dateTimeString;

  // If already has seconds or timezone, return as is
  if (dateTimeString.includes(":") && dateTimeString.split(":").length > 2) {
    return dateTimeString;
  }

  // Add seconds (00) to make it YYYY-MM-DDTHH:MM:SS format
  return dateTimeString.length === 16 ? `${dateTimeString}:00` : dateTimeString;
};

/**
 * Formats time for display (HH:MM format) - Always shows IST time
 */
export const parseUtcDate = (dateString: string): Date => {
  return new Date(dateString);
};

/**
 * Display time (hh:mm AM/PM) in IST
 */
export const formatTimeDisplay = (dateString: string): string => {
  try {
    if (!dateString) return "12:00 AM";

    const date = parseISO(dateString);
    if (!isValid(date)) {
      // Try parsing as local datetime
      const localDate = new Date(dateString);
      if (isValid(localDate)) {
        return format(localDate, "hh:mm a");
      }
      return "12:00 AM";
    }

    return format(date, "hh:mm a");
  } catch (err) {
    return "12:00 AM";
  }
};

/**
 * Display date (MMM dd, yyyy) in IST
 */
export const formatDateDisplay = (dateString: string): string => {
  try {
    if (!dateString || dateString.trim() === "") return "Invalid Date";

    // Handle date-only strings (YYYY-MM-DD)
    if (dateString.match(/^\d{4}-\d{2}-\d{2}$/)) {
      const date = parseISO(dateString);
      if (isValid(date)) {
        return format(date, "MMM dd, yyyy");
      }
    }

    // Handle datetime strings with T separator
    if (dateString.includes("T")) {
      const isoDate = parseISO(dateString);
      if (isValid(isoDate)) {
        const istDate = utcToIST(isoDate);
        return format(istDate, "MMM dd, yyyy");
      }
    }

    // Handle datetime strings
    const utcDate = parseUtcDate(dateString);
    if (isValid(utcDate)) {
      const istDate = utcToIST(utcDate);
      return format(istDate, "MMM dd, yyyy");
    }

    // Try parsing as ISO as last resort
    const isoDate = parseISO(dateString);
    if (isValid(isoDate)) {
      return format(isoDate, "MMM dd, yyyy");
    }

    return "Invalid Date";
  } catch (err) {
    console.warn("Date formatting error:", dateString, err);
    return "Invalid Date";
  }
};

/**
 * Calculate duration in seconds between start & end time
 * Works correctly for UTC timestamps
 */
export const calculateDurationSeconds = (
  startTime: string,
  endTime?: string,
): number => {
  try {
    const startUtc = parseUtcDate(startTime);
    const endUtc = endTime ? parseUtcDate(endTime) : new Date();

    if (!isValid(startUtc) || !isValid(endUtc)) return 0;

    const diffMs = endUtc.getTime() - startUtc.getTime();
    return Math.max(0, Math.floor(diffMs / 1000));
  } catch (err) {
    return 0;
  }
};

/**
 * Formats duration in HH:MM format
 */
export const formatDuration = (seconds: number): string => {
  const s = Math.max(0, Math.floor(Number(seconds) || 0));
  const hours = Math.floor(s / 3600);
  const minutes = Math.floor((s % 3600) / 60);
  return `${hours.toString().padStart(2, "0")}:${minutes
    .toString()
    .padStart(2, "0")}`;
};

/**
 * Extracts time in hh:MM AM/PM format from backend time string
 * Handles:
 * - Already in HH:MM format (e.g., "17:15") - converts to 12-hour format
 * - Full datetime (e.g., "2024-01-18T17:15:00") - extracts and converts to 12-hour format
 * - ISO datetime with timezone - extracts and converts to 12-hour format
 * - Invalid/null/empty - returns empty string or original value
 */
export const extractTimeOnly = (
  timeString: string | null | undefined,
): string => {
  if (!timeString) return "";

  // If already in HH:MM format (exactly 5 characters with colon at position 2)
  if (
    typeof timeString === "string" &&
    timeString.length === 5 &&
    timeString[2] === ":"
  ) {
    const parts = timeString.split(":");
    if (parts.length === 2) {
      const hours = parseInt(parts[0], 10);
      const minutes = parseInt(parts[1], 10);
      if (
        !isNaN(hours) &&
        !isNaN(minutes) &&
        hours >= 0 &&
        hours < 24 &&
        minutes >= 0 &&
        minutes < 60
      ) {
        // Convert to 12-hour format with AM/PM
        const period = hours >= 12 ? "PM" : "AM";
        const displayHours = hours % 12 === 0 ? 12 : hours % 12;
        return `${displayHours.toString().padStart(2, "0")}:${minutes.toString().padStart(2, "0")} ${period}`;
      }
    }
  }

  // If it's a datetime string (contains T or space)
  if (timeString.includes("T") || timeString.includes(" ")) {
    try {
      // Try to parse as ISO or datetime
      const date = parseISO(timeString);
      if (isValid(date)) {
        return format(date, "hh:mm a");
      }

      // Try parsing as local datetime
      const localDate = new Date(timeString);
      if (isValid(localDate)) {
        return format(localDate, "hh:mm a");
      }
    } catch (err) {
      // If parsing fails, try to extract manually
      // Look for HH:MM pattern in the string
      const timeMatch = timeString.match(/(\d{1,2}):(\d{2})/);
      if (timeMatch) {
        const hours = parseInt(timeMatch[1], 10);
        const minutes = parseInt(timeMatch[2], 10);
        if (!isNaN(hours) && !isNaN(minutes)) {
          // Convert to 12-hour format with AM/PM
          const period = hours >= 12 ? "PM" : "AM";
          const displayHours = hours % 12 === 0 ? 12 : hours % 12;
          return `${displayHours.toString().padStart(2, "0")}:${minutes.toString().padStart(2, "0")} ${period}`;
        }
      }
    }
  }

  // If no pattern matches, return as is (might be already in correct format)
  return timeString;
};

/**
 * Formats duration string to HH:MM format
 * Handles various input formats:
 * - Already in HH:MM format (e.g., "08:30") - returns as is
 * - Decimal hours (e.g., 8.5 or "8.5") - converts to HH:MM
 * - Seconds (e.g., 30600) - converts to HH:MM
 * - Invalid/null/empty - returns "00:00"
 */
export const formatDurationString = (
  duration: string | number | null | undefined,
): string => {
  if (duration == null || duration === "") return "00:00";

  const str = String(duration);

  if (!str.includes(".")) {
    return `${str}:00`;
  }

  const [hours, minutes] = str.split(".");

  return `${hours}:${minutes}`;
};

/**
 * Gets current IST time as a local datetime string for API
 * This sends IST time to backend which will convert it to UTC
 */
export const getCurrentISTForAPI = (): string => {
  const now = new Date();

  const year = now.getFullYear();
  const month = String(now.getMonth() + 1).padStart(2, "0");
  const day = String(now.getDate()).padStart(2, "0");
  const hours = String(now.getHours()).padStart(2, "0");
  const minutes = String(now.getMinutes()).padStart(2, "0");
  const seconds = String(now.getSeconds()).padStart(2, "0");

  return `${year}-${month}-${day}T${hours}:${minutes}:${seconds}`;
};

/**
 * Converts a UTC datetime string from backend to IST for display
 * This is specifically for backend responses that are in UTC
 */
export const convertUTCToISTForDisplay = (utcDateString: string): string => {
  try {
    if (!utcDateString) return "";

    // Parse the UTC date string
    const utcDate = new Date(utcDateString);

    if (!isValid(utcDate)) {
      console.warn("Invalid UTC date string:", utcDateString);
      return utcDateString;
    }

    // Convert to IST
    const istDate = utcToIST(utcDate);

    // Return as local datetime string (YYYY-MM-DDTHH:MM:SS)
    const year = istDate.getFullYear();
    const month = String(istDate.getMonth() + 1).padStart(2, "0");
    const day = String(istDate.getDate()).padStart(2, "0");
    const hours = String(istDate.getHours()).padStart(2, "0");
    const minutes = String(istDate.getMinutes()).padStart(2, "0");
    const seconds = String(istDate.getSeconds()).padStart(2, "0");

    return `${year}-${month}-${day}T${hours}:${minutes}:${seconds}`;
  } catch (error) {
    console.warn("Error converting UTC to IST:", utcDateString, error);
    return utcDateString;
  }
};

/**
 * Formats a date for editing in the form
 * Handles both ISO strings and local datetime strings
 */
export const formatForFormEdit = (dateTimeString: string): string => {
  if (!dateTimeString) return "";

  try {
    const date = parseDateTime(dateTimeString);
    return formatForDateTimeLocal(date);
  } catch (error) {
    console.warn(
      "Error formatting datetime for form edit:",
      dateTimeString,
      error,
    );
    return "";
  }
};

/**
 * Validates if a date string is valid
 */
export const isValidDateString = (dateString: string): boolean => {
  if (!dateString) return false;

  try {
    const date = parseDateTime(dateString);
    return isValid(date);
  } catch {
    return false;
  }
};

/**
 * Transforms time entry data from backend (UTC) to frontend (IST) for display
 * This function converts UTC timestamps from backend to IST for proper display
 */
export const transformTimeEntryForDisplay = (timeEntry: any): any => {
  if (!timeEntry) return timeEntry;

  const transformed = { ...timeEntry };

  // Convert UTC timestamps to IST for display
  if (transformed.start_time) {
    transformed.start_time = convertUTCToISTForDisplay(transformed.start_time);
  }

  if (transformed.end_time) {
    transformed.end_time = convertUTCToISTForDisplay(transformed.end_time);
  }

  if (transformed.created_at) {
    transformed.created_at = convertUTCToISTForDisplay(transformed.created_at);
  }

  if (transformed.updated_at) {
    transformed.updated_at = convertUTCToISTForDisplay(transformed.updated_at);
  }

  return transformed;
};

/**
 * Transforms an array of time entries from backend (UTC) to frontend (IST) for display
 */
export const transformTimeEntriesForDisplay = (timeEntries: any[]): any[] => {
  if (!Array.isArray(timeEntries)) return timeEntries;

  return timeEntries.map(transformTimeEntryForDisplay);
};
