// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

import FirebaseAuth
import SwiftUI
import UIKit

// MARK: - Extending a `Firebase User` to conform to `DataSourceProvidable`

extension User: DataSourceProvidable {
  private var infoSection: Section {
    let items = [Item(title: providerID, detailTitle: "Provider ID"),
                 Item(title: uid, detailTitle: "UUID"),
                 Item(title: displayName ?? "––", detailTitle: "Display Name", isEditable: true),
                 Item(
                   title: photoURL?.absoluteString ?? "––",
                   detailTitle: "Photo URL",
                   isEditable: true
                 ),
                 Item(title: email ?? "––", detailTitle: "Email", isEditable: true),
                 Item(title: phoneNumber ?? "––", detailTitle: "Phone Number", isEditable: true)]
    return Section(headerDescription: "Info", items: items)
  }

  private var metaDataSection: Section {
    let metadataRows = [
      Item(title: metadata.lastSignInDate?.description, detailTitle: "Last Sign-in Date"),
      Item(title: metadata.creationDate?.description, detailTitle: "Creation Date"),
    ]
    return Section(headerDescription: "Firebase Metadata", items: metadataRows)
  }

  private var otherSection: Section {
    let otherRows = [Item(title: isAnonymous ? "Yes" : "No", detailTitle: "Is User Anonymous?"),
                     Item(title: isEmailVerified ? "Yes" : "No", detailTitle: "Is Email Verified?")]
    return Section(headerDescription: "Other", items: otherRows)
  }

  private var actionSection: Section {
    let actionsRows = [
      Item(title: UserAction.refreshUserInfo.rawValue, textColor: .systemBlue),
      Item(title: UserAction.signOut.rawValue, textColor: .systemBlue),
      Item(title: UserAction.link.rawValue, textColor: .systemBlue, hasNestedContent: true),
      Item(title: UserAction.requestVerifyEmail.rawValue, textColor: .systemBlue),
      Item(title: UserAction.updatePassword.rawValue, textColor: .systemBlue),
      Item(title: UserAction.tokenRefresh.rawValue, textColor: .systemBlue),
      Item(title: UserAction.tokenRefreshAsync.rawValue, textColor: .systemBlue),
      Item(title: UserAction.delete.rawValue, textColor: .systemRed),
    ]
    return Section(headerDescription: "Actions", items: actionsRows)
  }

  var sections: [Section] {
    [infoSection, metaDataSection, otherSection, actionSection]
  }
}

// MARK: - UIKit Extensions

public extension UIViewController {
  func displayInfo(title: String, message: String, style: UIAlertController.Style) {
    let alert = UIAlertController(title: title, message: message, preferredStyle: style)
    alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))

    DispatchQueue.main.async { // Ensure UI updates on the main thread
      self.present(alert, animated: true, completion: nil)
    }
  }

  @MainActor func displayError(_ error: (any Error)?, from function: StaticString = #function) {
    guard let error = error else { return }
    print("ⓧ Error in \(function): \(error.localizedDescription)")
    let message = "\(error.localizedDescription)\n\n Occurred in \(function)"
    let errorAlertController = UIAlertController(
      title: "Error",
      message: message,
      preferredStyle: .alert
    )
    errorAlertController.addAction(UIAlertAction(title: "OK", style: .default))
    present(errorAlertController, animated: true, completion: nil)
  }
}

extension UINavigationController {
  func configureTabBar(title: String, systemImageName: String) {
    let tabBarItemImage = UIImage(systemName: systemImageName)
    tabBarItem = UITabBarItem(title: title,
                              image: tabBarItemImage?.withRenderingMode(.alwaysTemplate),
                              selectedImage: tabBarItemImage)
  }

  enum titleType: CaseIterable {
    case regular, large
  }

  func setTitleColor(_ color: UIColor, _ types: [titleType] = titleType.allCases) {
    if types.contains(.regular) {
      navigationBar.titleTextAttributes = [.foregroundColor: color]
    }
    if types.contains(.large) {
      navigationBar.largeTitleTextAttributes = [.foregroundColor: color]
    }
  }
}

extension UITextField {
  func setImage(_ image: UIImage?) {
    guard let image = image else { return }
    let imageView = UIImageView(image: image)
    imageView.frame = CGRect(x: 10, y: 10, width: 20, height: 20)
    imageView.contentMode = .scaleAspectFit

    let containerView = UIView()
    containerView.frame = CGRect(x: 20, y: 0, width: 40, height: 40)
    containerView.addSubview(imageView)
    leftView = containerView
    leftViewMode = .always
  }
}

extension UIImageView {
  convenience init(systemImageName: String, tintColor: UIColor? = nil) {
    var systemImage = UIImage(systemName: systemImageName)
    if let tintColor = tintColor {
      systemImage = systemImage?.withTintColor(tintColor, renderingMode: .alwaysOriginal)
    }
    self.init(image: systemImage)
  }

  func setImage(from url: URL?) {
    guard let url = url else { return }
    DispatchQueue.global(qos: .background).async {
      guard let data = try? Data(contentsOf: url) else { return }

      let image = UIImage(data: data)
      DispatchQueue.main.async {
        self.image = image
        self.contentMode = .scaleAspectFit
      }
    }
  }
}

extension UIImage {
  static func systemImage(_ systemName: String, tintColor: UIColor) -> UIImage? {
    let systemImage = UIImage(systemName: systemName)
    return systemImage?.withTintColor(tintColor, renderingMode: .alwaysOriginal)
  }
}

extension UIColor {
  static let highlightedLabel = UIColor.label.withAlphaComponent(0.8)

  var highlighted: UIColor { withAlphaComponent(0.8) }

  var image: UIImage {
    let pixel = CGSize(width: 1, height: 1)
    return UIGraphicsImageRenderer(size: pixel).image { context in
      self.setFill()
      context.fill(CGRect(origin: .zero, size: pixel))
    }
  }
}

// MARK: UINavigationBar + UserDisplayable Protocol

protocol UserDisplayable {
  func addProfilePic(_ imageView: UIImageView)
}

extension UINavigationBar: UserDisplayable {
  func addProfilePic(_ imageView: UIImageView) {
    let length = frame.height * 0.46
    imageView.clipsToBounds = true
    imageView.layer.cornerRadius = length / 2
    imageView.translatesAutoresizingMaskIntoConstraints = false
    addSubview(imageView)
    NSLayoutConstraint.activate([
      imageView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -15),
      imageView.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -5),
      imageView.heightAnchor.constraint(equalToConstant: length),
      imageView.widthAnchor.constraint(equalToConstant: length),
    ])
  }
}

// MARK: Extending UITabBarController to work with custom transition animator

extension UITabBarController: UITabBarControllerDelegate {
  public func tabBarController(_ tabBarController: UITabBarController,
                               animationControllerForTransitionFrom fromVC: UIViewController,
                               to toVC: UIViewController)
    -> (any UIViewControllerAnimatedTransitioning)? {
    let fromIndex = tabBarController.viewControllers!.firstIndex(of: fromVC)!
    let toIndex = tabBarController.viewControllers!.firstIndex(of: toVC)!

    let direction: Animator.TransitionDirection = fromIndex < toIndex ? .right : .left
    return Animator(direction)
  }

  func transitionToViewController(atIndex index: Int) {
    selectedIndex = index
  }
}

// MARK: - Foundation Extensions

extension Date {
  var description: String {
    let dateFormatter = DateFormatter()
    dateFormatter.dateStyle = .medium
    dateFormatter.timeStyle = .short
    return dateFormatter.string(from: self)
  }
}
