//
//  LunarDatePickerCoordinator.swift
//  LunarDatePicker
//
//  Coordinator for managing lunar date picker presentation flow
//

import UIKit

/// Coordinator responsible for managing the lunar date picker presentation flow
final class LunarDatePickerCoordinator {

  // MARK: - Properties

  private let configurationBuilder: ConfigurationProviding
  private let presenter: PickerPresenter
  private let dateConverter: DateConverting
  private var globalConfig: LDP_ConfigParams?
  private var currentRangeController: PickerController<PickerRange>?
  private var currentDateController: PickerController<Date>?
  
  // Store the onMonthVisible callback
  private var onMonthVisibleCallback: ((String) -> Void)?

  // MARK: - Initialization

  init(
    configurationBuilder: ConfigurationProviding = ConfigurationBuilder(),
    dateConverter: DateConverting = DateConverter(),
    presenter: PickerPresenter? = nil
  ) {
    self.configurationBuilder = configurationBuilder
    self.dateConverter = dateConverter
    self.presenter =
      presenter
      ?? PickerPresenter(
        configurationBuilder: configurationBuilder,
        dateConverter: dateConverter
      )
  }
  
  deinit {
    cleanup()
  }

  // MARK: - Public Methods

  /// Configures the global settings for the date picker
  /// - Parameter config: Global configuration parameters
  func configure(with config: LDP_ConfigParams) {
    self.globalConfig = config
  }

  /// Updates prices for the currently displayed calendar
  /// - Parameter params: Price update parameters with mode and data
  /// - Throws: An error if update fails
  func updatePrices(_ params: LDP_PriceUpdateParams) throws {
    if let controller = currentRangeController {
      controller.updatePrices(params)
    } else if let controller = currentDateController {
      controller.updatePrices(params)
    } else {
      throw LunarDatePickerError.presentationFailed
    }
  }
  
  /// Called when a month becomes visible in the calendar
  /// - Parameter month: Month in format "YYYY-MM"
  func notifyMonthVisible(_ month: String) {
    onMonthVisibleCallback?(month)
  }
  
  /// Cleanup method to clear all references and prevent memory leaks
  func cleanup() {
    onMonthVisibleCallback = nil
    currentRangeController?.cleanup()
    currentDateController?.cleanup()
    currentRangeController = nil
    currentDateController = nil
  }

  /// Presents the date picker with the specified parameters
  /// - Parameter params: Presentation parameters
  /// - Throws: An error if the presentation fails
  func present(with params: LDP_PresentParams) throws {
    guard let rootViewController = getCurrentRootViewController() else {
      throw LunarDatePickerError.noRootViewController
    }

    // Store the onMonthVisible callback
    onMonthVisibleCallback = params.onMonthVisible

    let pickerController = presenter.createPickerController(
      from: params,
      globalConfig: globalConfig,
      onMonthVisible: params.onMonthVisible != nil ? { [weak self] month in
        self?.notifyMonthVisible(month)
      } : nil
    )

    switch pickerController {
    case .range(let controller):
      currentRangeController = controller
      currentDateController = nil
      configureAndPresent(
        rangeController: controller,
        params: params,
        from: rootViewController
      )
    case .date(let controller):
      currentDateController = controller
      currentRangeController = nil
      configureAndPresent(
        dateController: controller,
        params: params,
        from: rootViewController
      )
    }
  }

  // MARK: - Private Methods

  private func getCurrentRootViewController() -> UIViewController? {
    guard
      let windowScene = UIApplication.shared.connectedScenes.first
        as? UIWindowScene,
      let rootViewController = windowScene.windows.first?.rootViewController
    else {
      return nil
    }
    return rootViewController
  }

  private func configureAndPresent(
    rangeController: PickerController<PickerRange>,
    params: LDP_PresentParams,
    from viewController: UIViewController
  ) {
    rangeController.doneHandler = { [weak self] selected in
      guard let selected = selected,
        let self = self
      else { return }

      // Get timezone from config, fallback to current timezone
      let timeZone = self.globalConfig.flatMap { config in
        TimeZone(secondsFromGMT: Int(config.timeZoneOffset * 3600))
      }

      let result = self.dateConverter.rangeFromDates(
        from: selected.fromDate,
        to: selected.toDate,
        timeZone: timeZone
      )
      
      // Call completion BEFORE clearing callbacks
      params.onDone(result)
      
      // Clear callback after completion
      self.onMonthVisibleCallback = nil
      self.currentRangeController = nil
    }
    
    // Set onSelectFromDate callback
    rangeController.onSelectFromDate = params.onSelectFromDate
    
    rangeController.cancelHandler = { [weak self] in
      self?.cleanup()
    }
    rangeController.present(above: viewController)
  }

  private func configureAndPresent(
    dateController: PickerController<Date>,
    params: LDP_PresentParams,
    from viewController: UIViewController
  ) {
    dateController.doneHandler = { [weak self] selected in
      guard let selected = selected,
        let self = self
      else { return }

      // Get timezone from config, fallback to current timezone
      let timeZone = self.globalConfig.flatMap { config in
        TimeZone(secondsFromGMT: Int(config.timeZoneOffset * 3600))
      }

      let result = self.dateConverter.rangeFromDate(selected, timeZone: timeZone)
      
      // Call completion BEFORE clearing callbacks
      params.onDone(result)
      
      // Clear callback after completion
      self.onMonthVisibleCallback = nil
      self.currentDateController = nil
    }
    
    dateController.cancelHandler = { [weak self] in
      self?.cleanup()
    }
    dateController.present(above: viewController)
  }
}

// MARK: - Error Types

enum LunarDatePickerError: LocalizedError {
  case noRootViewController
  case invalidConfiguration
  case presentationFailed

  var errorDescription: String? {
    switch self {
    case .noRootViewController:
      return "Unable to find root view controller for presentation"
    case .invalidConfiguration:
      return "Invalid configuration parameters provided"
    case .presentationFailed:
      return "Failed to present the date picker"
    }
  }
}
