//
//  PickerController.swift
//  LunarDatePicker
//
//  Main picker controller for displaying calendar view
//

import Foundation
import JTAppleCalendar
import UIKit

/// Generic picker controller that handles both single date and date range selection
/// - Parameter Value: The type of value being selected (Date or PickerRange)
final class PickerController<Value: PickerValue>: UIViewController {

  // MARK: - UI Components

  /// Cancel button in navigation bar
  private lazy var cancelBarButtonItem: UIBarButtonItem = {
    let barButtonItem = UIBarButtonItem(
      title: self.config.controller.cancelButtonTitle,
      style: .plain,
      target: self,
      action: #selector(self.cancel)
    )
    barButtonItem.tintColor = self.config.controller.cancelColor.toUIColor()
    return barButtonItem
  }()

  /// Title label displayed in navigation bar
  private lazy var titleUI: UIView = {
    let titleLabel = UILabel()
    titleLabel.text = self.config.controller.title
    titleLabel.font = UIFont.systemFont(
      ofSize: Constants.FontSize.title,
      weight: .semibold
    )
    titleLabel.textAlignment = .center
    titleLabel.contentMode = .center
    titleLabel.textColor = self.config.controller.titleColor.toUIColor()

    return titleLabel
  }()

  /// Main calendar view
  private lazy var calendarView: JTACMonthView = {
    let configManager = CalendarConfigurationManager(
      config: self.config,
      minimumDate: self.privateMinimumDate,
      maximumDate: self.privateMaximumDate,
      shouldShowPrices: self.shouldShowPrices
    )
    let monthView = configManager.createCalendarView(for: Value.self)
    monthView.ibCalendarDelegate = self
    monthView.ibCalendarDataSource = self
    return monthView
  }()

  /// Week day names header view
  private lazy var weekView: WeekView = {
    let view = WeekView(
      calendar: self.config.calendar,
      config: self.config.weekView
    )
    view.translatesAutoresizingMaskIntoConstraints = false
    return view
  }()

  // MARK: - Properties

  /// Configuration for the picker
  internal let config: PickerConfig

  /// Reuse identifiers for collection view cells
  internal let dayCellReuseIdentifier = "DayCellReuseIdentifier"
  internal let monthHeaderReuseIdentifier = "MonthHeaderReuseIdentifier"

  /// Cache for cell view configurations with LRU eviction
  internal var viewConfigs: [String: DayCell.ViewConfig] = [:]
  private var cacheAccessOrder: [String] = []

  /// Optimized price lookup using date-based key for O(1) access
  private var pricesByDate: [String: LDP_PriceData] = [:]
  
  /// Reusable calendar instance for better performance
  private var calendar: Calendar {
    return self.config.calendar
  }

  /// Flag to control whether price labels should be shown
  internal var shouldShowPrices: Bool = false
  
  /// Callback for month visibility detection
  internal var onMonthVisible: ((String) -> Void)?

  /// Date constraints
  internal var privateMinimumDate: Date?
  internal var privateMaximumDate: Date?

  /// Date formatter for day labels
  private var dayFormatter = DateFormatter()
  
  /// Date formatter for price keys (reusable)
  private lazy var dateKeyFormatter: DateFormatter = {
    let formatter = DateFormatter()
    formatter.dateFormat = "yyyy-MM-dd"
    formatter.locale = Locale(identifier: "en_US_POSIX")
    formatter.timeZone = self.config.calendar.timeZone
    return formatter
  }()
  
  /// Date formatter for month keys (reusable)
  internal lazy var monthKeyFormatter: DateFormatter = {
    let formatter = DateFormatter()
    formatter.dateFormat = "yyyy-MM"
    formatter.locale = Locale(identifier: "en_US_POSIX")
    formatter.timeZone = self.config.calendar.timeZone
    return formatter
  }()
  
  /// Cached today date to avoid creating Date() repeatedly
  private lazy var todayDate: Date = {
    return self.config.calendar.startOfDay(for: Date())
  }()
  
  /// Timer to update today date at midnight
  private var todayUpdateTimer: Timer?
  
  /// Track visible months to avoid duplicate callbacks
  internal var visibleMonths: Set<String> = []
  
  /// Single debounce work item for checking all visible months
  private var visibilityCheckWorkItem: DispatchWorkItem?

  /// Currently selected value
  private var value: Value?

  /// The block to execute after "Done" button will be tapped
  public var doneHandler: ((Value?) -> Void)?
  
  /// The block to execute when "Cancel" button will be tapped
  public var cancelHandler: (() -> Void)?

  /// Callback for when user selects from date in range mode
  public var onSelectFromDate: ((String, [String]) -> Void)?

  /// And initial value which will be selected by default
  public var initialValue: Value?

  /// Minimal selection date. Dates less then current will be marked as unavailable
  public var minimumDate: Date? {
    get {
      self.privateMinimumDate
    }
    set {
      self.privateMinimumDate = newValue?.startOfDay()
    }
  }

  /// Maximum selection date. Dates greater then current will be marked as unavailable
  public var maximumDate: Date? {
    get {
      self.privateMaximumDate
    }
    set {
      self.privateMaximumDate = newValue?.endOfDay()
    }
  }

  // MARK: - Lifecycle

  public init(config: PickerConfig = .default) {
    self.config = config
    self.dayFormatter.locale = config.calendar.locale
    self.dayFormatter.dateFormat = "d"
    super.init(nibName: nil, bundle: nil)
  }

  public convenience init(
    mode: PickerModeRange,
    config: PickerConfig = .default
  ) where Value == PickerRange {
    self.init(config: config)
  }

  public convenience init(
    mode: PickerModeSingle,
    config: PickerConfig = .default
  ) where Value == Date {
    self.init(config: config)
  }

  @available(*, unavailable)
  public required init?(coder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
  }

  override public func viewDidLoad() {
    super.viewDidLoad()
    self.configureUI()
    self.configureSubviews()
    self.configureConstraints()
    self.configureInitialState()
    self.setupCalendarContentInsets()
    self.setupTodayUpdateTimer()
  }
  
  deinit {
    cleanup()
    cleanupTodayTimer()
  }

  // MARK: - Public Methods

  public func present(
    above viewController: UIViewController,
    animated flag: Bool = true,
    completion: (() -> Void)? = nil
  ) {
    let navVc = UINavigationController(rootViewController: self)
    navVc.modalPresentationStyle = .formSheet
    if viewController.preferredContentSize != .zero {
      navVc.preferredContentSize = viewController.preferredContentSize
    } else {
      navVc.preferredContentSize = Constants.UI.defaultPickerSize
    }

    viewController.present(navVc, animated: flag, completion: completion)
  }

  /// Triggers a debounced check for all visible months
  /// Only calls onMonthVisible for months actually visible on screen after user stops scrolling
  internal func triggerVisibilityCheck() {
    // Cancel any pending visibility check
    self.visibilityCheckWorkItem?.cancel()
    
    // Create new debounced check for all visible months
    let workItem = DispatchWorkItem { [weak self] in
      self?.checkAndNotifyVisibleMonths()
      self?.visibilityCheckWorkItem = nil
    }
    
    // Store and schedule the check
    self.visibilityCheckWorkItem = workItem
    DispatchQueue.main.asyncAfter(deadline: .now() + self.config.monthVisibleDebounceDelaySeconds, execute: workItem)
  }

  /// Checks all currently visible months and calls onMonthVisible for new ones
  private func checkAndNotifyVisibleMonths() {
    var currentlyVisibleMonths: Set<String> = []
    
    // Get all visible months using JTAppleCalendar's visibleDates
    self.calendarView.visibleDates { visibleDateSegment in
      // Process month headers (which represent months)
      for monthDate in visibleDateSegment.monthDates {
        let monthKey = self.monthKeyFormatter.string(from: monthDate.date)
        currentlyVisibleMonths.insert(monthKey)
      }
    }
    
    // Call onMonthVisible for newly visible months
    for monthKey in currentlyVisibleMonths {
      if !self.visibleMonths.contains(monthKey) {
        self.visibleMonths.insert(monthKey)
        self.onMonthVisible?(monthKey)
      }
    }
  }

  /// Gets currently visible months (not accumulated history)
  /// Returns array of month strings in YYYY-MM format, sorted
  private func getCurrentlyVisibleMonths() -> [String] {
    var currentlyVisibleMonths: Set<String> = []
    
    // Get all visible months using JTAppleCalendar's visibleDates
    self.calendarView.visibleDates { visibleDateSegment in
      // Process month headers (which represent months)
      for monthDate in visibleDateSegment.monthDates {
        let monthKey = self.monthKeyFormatter.string(from: monthDate.date)
        currentlyVisibleMonths.insert(monthKey)
      }
    }
    
    return Array(currentlyVisibleMonths).sorted()
  }

  /// Cleanup method to cancel all pending month visibility callbacks
  /// Should be called when the controller is being destroyed to prevent memory leaks
  internal func cleanup() {
    // Cancel visibility check
    self.visibilityCheckWorkItem?.cancel()
    self.visibilityCheckWorkItem = nil
    self.visibleMonths.removeAll()
    
    // Clear caches to prevent memory leaks
    viewConfigs.removeAll()
    cacheAccessOrder.removeAll()
  }
  
  // MARK: - Today Date Management
  
  /// Setup timer to update today date at midnight
  private func setupTodayUpdateTimer() {
    // Calculate seconds until next midnight
    let calendar = self.config.calendar
    let now = Date()
    
    guard let tomorrow = calendar.date(byAdding: .day, value: 1, to: calendar.startOfDay(for: now)) else {
      return
    }
    
    let timeUntilMidnight = tomorrow.timeIntervalSince(now)
    
    // Setup timer to fire at midnight and then every 24 hours
    todayUpdateTimer = Timer.scheduledTimer(withTimeInterval: timeUntilMidnight, repeats: false) { [weak self] _ in
      self?.updateTodayDate()
      self?.setupDailyTimer()
    }
    
    // Add observer for app becoming active (when returning from background)
    NotificationCenter.default.addObserver(
      self,
      selector: #selector(handleAppBecameActive),
      name: UIApplication.didBecomeActiveNotification,
      object: nil
    )
  }
  
  /// Setup daily timer that repeats every 24 hours
  private func setupDailyTimer() {
    todayUpdateTimer = Timer.scheduledTimer(withTimeInterval: 24 * 60 * 60, repeats: true) { [weak self] _ in
      self?.updateTodayDate()
    }
  }
  
  /// Update today date and refresh visible cells
  private func updateTodayDate() {
    let oldToday = todayDate
    todayDate = self.config.calendar.startOfDay(for: Date())
    
    // Only refresh if date actually changed
    if !self.config.calendar.isDate(oldToday, inSameDayAs: todayDate) {
      DispatchQueue.main.async { [weak self] in
        // Clear cache to ensure today styling is updated
        self?.viewConfigs.removeAll()
        self?.calendarView.reloadData()
      }
    }
  }
  
  /// Cleanup today timer
  private func cleanupTodayTimer() {
    todayUpdateTimer?.invalidate()
    todayUpdateTimer = nil
    
    // Remove notification observer
    NotificationCenter.default.removeObserver(
      self,
      name: UIApplication.didBecomeActiveNotification,
      object: nil
    )
  }

  /// LRU cache management for view configurations
  private func getCachedConfig(for cacheKey: String) -> DayCell.ViewConfig? {
    guard let config = viewConfigs[cacheKey] else { return nil }
    
    // Move to front of access order (most recently used)
    if let existingIndex = cacheAccessOrder.firstIndex(of: cacheKey) {
      cacheAccessOrder.remove(at: existingIndex)
    }
    cacheAccessOrder.append(cacheKey)
    
    return config
  }
  
  private func setCachedConfig(_ config: DayCell.ViewConfig, for cacheKey: String) {
    // Add new config
    viewConfigs[cacheKey] = config
    
    // Update access order
    if let existingIndex = cacheAccessOrder.firstIndex(of: cacheKey) {
      cacheAccessOrder.remove(at: existingIndex)
    }
    cacheAccessOrder.append(cacheKey)
    
    // Evict oldest if cache is full
    while viewConfigs.count > Constants.Performance.maxCacheSize {
      guard let oldestCacheKey = cacheAccessOrder.first else { break }
      cacheAccessOrder.removeFirst()
      viewConfigs.removeValue(forKey: oldestCacheKey)
    }
  }

  /// Updates prices for the calendar with optimized O(1) lookup
  /// - Parameter params: Price update parameters with month and prices data
  public func updatePrices(_ params: LDP_PriceUpdateParams) {
    updatePricesForMonth(params)
  }
  
  /// Update prices for specific month (partial update)
  private func updatePricesForMonth(_ params: LDP_PriceUpdateParams) {
    // Remove old prices for this month
    let monthPrefix = params.month
    pricesByDate = pricesByDate.filter { !$0.key.hasPrefix(monthPrefix) }
    
    // Add new prices
    for priceData in params.prices {
      pricesByDate[priceData.date] = priceData
    }
    
    // Clear ALL cache to ensure fresh data (more aggressive approach)
    viewConfigs.removeAll()
    
    // Reload calendar on main thread with weak self to prevent retain cycles
    DispatchQueue.main.async { [weak self] in
      self?.calendarView.reloadData()
    }
  }
  


  // MARK: - Private Methods

  private func configureUI() {
    self.view.backgroundColor = self.config.controller.backgroundColor.toUIColor()
    self.navigationItem.largeTitleDisplayMode = .never
    self.navigationItem.titleView = self.titleUI
    self.navigationItem.leftBarButtonItem = self.cancelBarButtonItem
  }

  private func configureSubviews() {
    self.calendarView.register(
      DayCell.self,
      forCellWithReuseIdentifier: self.dayCellReuseIdentifier
    )
    self.calendarView.register(
      MonthHeader.self,
      forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader,
      withReuseIdentifier: self.monthHeaderReuseIdentifier
    )

    self.view.addSubview(self.weekView)
    self.view.addSubview(self.calendarView)
  }

  private func configureConstraints() {
    NSLayoutConstraint.activate([
      self.weekView.topAnchor.constraint(
        equalTo: self.view.safeAreaLayoutGuide.topAnchor
      ),
      self.weekView.leftAnchor.constraint(
        equalTo: self.view.leftAnchor,
        constant: Constants.Layout.weekViewSidePadding
      ),
      self.weekView.rightAnchor.constraint(
        equalTo: self.view.rightAnchor,
        constant: -Constants.Layout.weekViewSidePadding
      ),
    ])

    NSLayoutConstraint.activate([
      self.calendarView.topAnchor.constraint(
        equalTo: self.weekView.bottomAnchor
      ),
      self.calendarView.leftAnchor.constraint(
        equalTo: self.view.leftAnchor,
        constant: Constants.Layout.calendarHorizontalPadding
      ),
      self.calendarView.rightAnchor.constraint(
        equalTo: self.view.rightAnchor,
        constant: -Constants.Layout.calendarHorizontalPadding
      ),
    ])

    NSLayoutConstraint.activate([
      self.calendarView.bottomAnchor.constraint(
        equalTo: self.view.bottomAnchor
      )
    ])
  }
  
  private func setupCalendarContentInsets() {
    self.calendarView.contentInset.bottom = 20
    self.calendarView.verticalScrollIndicatorInsets.bottom = 20
  }

  private func configureInitialState() {
    self.value = self.initialValue
    if let date = self.value as? Date {
      self.calendarView.selectDates([date])
      self.calendarView.scrollToHeaderForDate(date)
    } else if let rangeValue = self.value as? PickerRange {
      self.selectRange(rangeValue, in: self.calendarView)
      self.calendarView.scrollToHeaderForDate(rangeValue.fromDate)
    } else {
      let nowDate = Date()
      let targetDate = self.privateMaximumDate ?? nowDate
      let scrollDate = targetDate < nowDate ? targetDate : nowDate
      self.calendarView.scrollToHeaderForDate(scrollDate)
    }
  }

  internal func configureCell(
    _ cell: JTACDayCell,
    forItemAt date: Date,
    cellState: CellState,
    indexPath: IndexPath
  ) {
    guard let cell = cell as? DayCell else { return }
    
    cell.applyConfig(self.config)
    
    // Create cache key that includes selection state to avoid stale cache
    let cacheKey = createCacheKey(for: indexPath, cellState: cellState, date: date)
    
    if let cachedConfig = getCachedConfig(for: cacheKey) {
      cell.configure(for: cachedConfig)
    } else {
      let newConfig = createCellConfig(for: cellState, date: date)
      setCachedConfig(newConfig, for: cacheKey)
      cell.configure(for: newConfig)
    }
  }
  
  /// Creates cell configuration with optimized price lookup
  private func createCellConfig(for cellState: CellState, date: Date) -> DayCell.ViewConfig {
    var config = DayCell.makeViewConfig(
      for: cellState,
      minimumDate: self.privateMinimumDate,
      maximumDate: self.privateMaximumDate,
      rangeValue: self.value as? PickerRange,
      calendar: self.config.calendar
    )

    // Only configure content for dates that belong to current month
    if cellState.dateBelongsTo == .thisMonth {
      // Configure date label
      if config.dateLabelText != nil {
        config.dateLabelText = self.dayFormatter.string(from: date)
      }

      // Check if this is today
      let isToday = self.config.calendar.isDate(date, inSameDayAs: todayDate)
      
      // Configure date label color and font weight - today gets blue color and medium weight
      if isToday {
        config.dateLabelColor = ColorWrapper.systemBlue
        config.dateLabelFontWeight = .medium
      } else {
        config.dateLabelColor = self.config.calendar.isDateInWeekend(date)
          ? self.config.dayCell.weekendLabelColor
          : self.config.dayCell.dateLabelColor
        config.dateLabelFontWeight = .regular
      }

      // Configure lunar date
      configureLunarDate(for: &config, date: date)
      
      // Configure price information if needed
      if shouldShowPrices {
        configurePriceInfo(for: &config, date: date)
      } else {
        // Ensure price is cleared when prices are disabled
        config.priceLabelText = ""
      }
    }

    return config
  }
  
  /// Configures lunar date information for the cell
  private func configureLunarDate(for config: inout DayCell.ViewConfig, date: Date) {
    let lunarDate = getVietnameseLunarDate(date, self.config.calendar.timeZone)
    
    config.lunarDateLabelText = lunarDate.day == 1
      ? "\(lunarDate.day)/\(lunarDate.month)"
      : "\(lunarDate.day)"
    
    config.lunarDateLabelColor = lunarDate.day == 1
      ? self.config.dayCell.specialDateLabelColor
      : self.config.dayCell.lunarDateLabelColor
  }
  
  /// Configures price information with O(1) lookup
  private func configurePriceInfo(for config: inout DayCell.ViewConfig, date: Date) {
    let normalizedDate = date.startOfDay(in: self.config.calendar)
    let dateKey = dateKeyFormatter.string(from: normalizedDate)
    
    if let priceData = pricesByDate[dateKey] {
      config.priceLabelText = formatPrice(priceData.price)
      config.priceLabelColor = priceData.isCheapest == true
        ? self.config.dayCell.cheapestPriceLabelColor
        : self.config.dayCell.priceLabelColor
    } else {
      config.priceLabelText = ""
    }
  }

  // MARK: - Actions

  @objc
  private func cancel() {
    // Notify cancel handler
    self.cancelHandler?()
    // Cleanup before dismissing to prevent memory leaks
    cleanup()
    self.navigationController?.dismiss(animated: true)
  }

  @objc
  private func done() {
    self.doneHandler?(self.value)
    // Cleanup before dismissing to prevent memory leaks
    cleanup()
    self.navigationController?.dismiss(animated: true)
  }

  private func selectValue(_ value: Value?, in calendar: JTACMonthView) {
    if let date = value as? Date {
      calendar.selectDates([date])
    } else if let range = value as? PickerRange {
      self.selectRange(range, in: calendar)
    }
  }

  // MARK: - Range Selection Logic
  
  internal func handleDateTap(in calendar: JTACMonthView, date: Date) {
    switch Value.mode {
    case .single:
      handleSingleDateSelection(date: date, calendar: calendar)
    case .range:
      handleRangeDateSelection(date: date, calendar: calendar)
    }
  }
  
  private func handleSingleDateSelection(date: Date, calendar: JTACMonthView) {
    self.value = date as? Value
    self.selectValue(date as? Value, in: calendar)
    done()
  }
  
  private func handleRangeDateSelection(date: Date, calendar: JTACMonthView) {
    let dateHelper = DateRangeHelper(calendar: self.config.calendar)
    let currentRange = self.value as? PickerRange
    let newRange = dateHelper.calculateNewRange(
      currentRange: currentRange,
      selectedDate: date,
      hasInitialValue: self.initialValue != nil
    )
    
    // Check if this is a new from date selection
    // This happens when:
    // 1. No current range (first selection)
    // 2. Current range exists but we're starting a new range (different from date)
    let isNewFromDateSelection = !newRange.shouldComplete && (
      currentRange == nil || 
      !newRange.range.fromDate.isInSameDay(in: self.config.calendar, date: currentRange!.fromDate)
    )
    
    // Call onSelectFromDate callback if this is a new from date selection
    if isNewFromDateSelection, let onSelectFromDate = self.onSelectFromDate {
      let dateConverter = DateConverter()
      let timeZone = self.config.calendar.timeZone
      
      let fromDateString = dateConverter.stringFromDate(newRange.range.fromDate, timeZone: timeZone)
      let currentlyVisibleMonths = getCurrentlyVisibleMonths()
      
      onSelectFromDate(fromDateString, currentlyVisibleMonths)
    }
    
    if newRange.shouldComplete {
      // Range is complete - update value and dismiss
      self.value = newRange.range as? Value
      done()
    } else {
      // Range not complete - update UI for continued selection
      if isNewFromDateSelection {
        // Clear all selections first to avoid visual conflicts
        calendar.deselectAllDates(triggerSelectionDelegate: false)
        // Clear cache to ensure fresh rendering for new from date
        viewConfigs.removeAll()
        // Force reload and then update value + selection
        DispatchQueue.main.async { [weak self] in
          calendar.reloadData()
          // Update value after reload to ensure UI sync
          self?.value = newRange.range as? Value
          // Small delay to ensure reload completes before selection
          DispatchQueue.main.asyncAfter(deadline: .now() + 0.01) {
            self?.selectValue(newRange.range as? Value, in: calendar)
          }
        }
      } else {
        // Normal case - update value immediately
        self.value = newRange.range as? Value
        self.selectValue(newRange.range as? Value, in: calendar)
      }
    }
  }

  private func formatPrice(_ price: Double) -> String {
    let millions = price / 1_000_000
    if millions > 1 {
      return String(format: "%.2ftr", millions)
    }

    let k = price / 1_000
    return String(format: "%.0fk", k)
  }

  private func selectRange(_ range: PickerRange, in calendar: JTACMonthView) {
    calendar.deselectAllDates(triggerSelectionDelegate: false)
    calendar.selectDates(
      from: range.fromDate,
      to: range.toDate,
      triggerSelectionDelegate: true,
      keepSelectionIfMultiSelectionAllowed: false
    )
    // Remove reloadItems() call as it overrides the selection state set by selectDates()
    // The triggerSelectionDelegate: true above will properly configure the cells
  }

  // Note: Selective cache invalidation methods removed as they are no longer needed.
  // Cache key now includes selection state, so cache automatically misses when selection changes.

  /// Creates a unique cache key that includes selection state to avoid stale cache
  private func createCacheKey(for indexPath: IndexPath, cellState: CellState, date: Date) -> String {
    let selectionState = cellState.isSelected ? "S" : "U"
    
    // Use timeIntervalSince1970 for performance (faster than dateFormatter)
    let dateKey = String(format: "%.0f", date.timeIntervalSince1970)
    
    // Check if this date is today for cache key
    let isToday = self.config.calendar.isDate(date, inSameDayAs: todayDate)
    let todayState = isToday ? "T" : "N"
    
    // Enhanced cache key: date + selection state + belongs to current month + today state
    return "\(dateKey)|\(selectionState)|\(cellState.dateBelongsTo == .thisMonth ? "M" : "O")|\(todayState)"
  }

  @objc
  private func handleAppBecameActive() {
    updateTodayDate()
  }
}

// MARK: - Configuration Extension

extension PickerConfig {
  public struct PickerController {
    public var title = ""
    public var cancelButtonTitle = "Cancel"
    public var cancelColor = ColorWrapper.systemBlue
    public var titleColor = ColorWrapper.customBlack
    public var backgroundColor = ColorWrapper.customWhite
  }
}

// MARK: - Helper Classes

/// Helper class to manage date range selection logic
private struct DateRangeHelper {
  let calendar: Calendar
  
  struct RangeResult {
    let range: PickerRange
    let shouldComplete: Bool
  }
  
  func calculateNewRange(
    currentRange: PickerRange?,
    selectedDate: Date,
    hasInitialValue: Bool
  ) -> RangeResult {
    
    guard let currentValue = currentRange else {
      return RangeResult(
        range: .from(
          selectedDate.startOfDay(in: calendar),
          to: selectedDate.startOfDay(in: calendar)
        ),
        shouldComplete: false
      )
    }
    
    let rangeSelected = !currentValue.fromDate.isInSameDay(date: currentValue.toDate)
    
    // Handle initial value case
    if !rangeSelected && hasInitialValue {
      return handleInitialValueCase(currentValue: currentValue, selectedDate: selectedDate)
    }
    
    // Handle range already selected case
    if rangeSelected {
      return RangeResult(
        range: .from(
          selectedDate.startOfDay(in: calendar),
          to: selectedDate.endOfDay(in: calendar)
        ),
        shouldComplete: false
      )
    }
    
    // Handle same day selections
    return handleSameDaySelection(currentValue: currentValue, selectedDate: selectedDate)
  }
  
  private func handleInitialValueCase(currentValue: PickerRange, selectedDate: Date) -> RangeResult {
    if selectedDate < currentValue.fromDate {
      return RangeResult(
        range: .from(
          selectedDate.startOfDay(in: calendar),
          to: selectedDate.startOfDay(in: calendar)
        ),
        shouldComplete: false
      )
    } else {
      return RangeResult(
        range: .from(
          currentValue.fromDate,
          to: selectedDate.endOfDay(in: calendar)
        ),
        shouldComplete: true
      )
    }
  }
  
  private func handleSameDaySelection(currentValue: PickerRange, selectedDate: Date) -> RangeResult {
    // Same day as from date
    if selectedDate.isInSameDay(in: calendar, date: currentValue.fromDate) {
      return RangeResult(
        range: .from(
          currentValue.fromDate,
          to: selectedDate.endOfDay(in: calendar)
        ),
        shouldComplete: true
      )
    }
    
    // Same day as to date
    if selectedDate.isInSameDay(in: calendar, date: currentValue.toDate) {
      return RangeResult(
        range: .from(
          selectedDate.startOfDay(in: calendar),
          to: currentValue.toDate
        ),
        shouldComplete: true
      )
    }
    
    // Date before from date
    if selectedDate < currentValue.fromDate {
      return RangeResult(
        range: .from(
          selectedDate.startOfDay(in: calendar),
          to: selectedDate.endOfDay(in: calendar)
        ),
        shouldComplete: false
      )
    }
    
    // Date after from date
    return RangeResult(
      range: .from(
        currentValue.fromDate,
        to: selectedDate.endOfDay(in: calendar)
      ),
      shouldComplete: true
    )
  }
}