// 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 UIKit

/// Firebase Auth supported identity providers and other methods of authentication
enum AuthMenu: String {
  case settings = "Settings"
  case google = "google.com"
  case apple = "apple.com"
  case twitter = "twitter.com"
  case microsoft = "microsoft.com"
  case gitHub = "github.com"
  case yahoo = "yahoo.com"
  case linkedIn = "linkedin.com"
  case facebook = "facebook.com"
  case gameCenter = "gc.apple.com"
  case emailPassword = "password"
  case passwordless = "emailLink"
  case phoneNumber = "phone"
  case anonymous
  case custom
  case initRecaptcha
  case customAuthDomain
  case getToken
  case getTokenForceRefresh
  case addAuthStateChangeListener
  case removeLastAuthStateChangeListener
  case addIdTokenChangeListener
  case removeLastIdTokenChangeListener
  case verifyClient
  case deleteApp
  case actionType
  case continueURL
  case linkDomain
  case requestVerifyEmail
  case requestPasswordReset
  case resetPassword
  case checkActionCode
  case applyActionCode
  case verifyPasswordResetCode
  case phoneEnroll
  case totpEnroll
  case multifactorUnenroll

  // More intuitively named getter for `rawValue`.
  var id: String { rawValue }

  // The UI friendly name of the `AuthMenu`. Used for display.
  var name: String {
    switch self {
    case .settings:
      return "Settings"
    case .google:
      return "Google"
    case .apple:
      return "Apple"
    case .twitter:
      return "Twitter"
    case .microsoft:
      return "Microsoft"
    case .gitHub:
      return "GitHub"
    case .yahoo:
      return "Yahoo"
    case .linkedIn:
      return "LinkedIn"
    case .facebook:
      return "Facebook"
    case .gameCenter:
      return "Game Center"
    case .emailPassword:
      return "Email & Password Login"
    case .passwordless:
      return "Email Link/Passwordless"
    case .phoneNumber:
      return "Phone Number"
    case .anonymous:
      return "Anonymous Authentication"
    case .custom:
      return "Custom Auth System"
    // Recpatcha
    case .initRecaptcha:
      return "Initialize reCAPTCHA Enterprise"
    // Custom Auth Domain
    case .customAuthDomain:
      return "Set Custom Auth Domain"
    // App Section
    case .getToken:
      return "Get Token"
    case .getTokenForceRefresh:
      return "Get Token Force Refresh"
    case .addAuthStateChangeListener:
      return "Add Auth State Change Listener"
    case .removeLastAuthStateChangeListener:
      return "Remove Last Auth State Change Listener"
    case .addIdTokenChangeListener:
      return "Add ID Token Change Listener"
    case .removeLastIdTokenChangeListener:
      return "Remove Last ID Token Change Listener"
    case .verifyClient:
      return "Verify Client"
    case .deleteApp:
      return "Delete App"
    // OOB
    case .actionType:
      return "Action Type"
    case .continueURL:
      return "Continue URL"
    case .linkDomain:
      return "Link Domain"
    case .requestVerifyEmail:
      return "Request Verify Email"
    case .requestPasswordReset:
      return "Request Password Reset"
    case .resetPassword:
      return "Reset Password"
    case .checkActionCode:
      return "Check Action Code"
    case .applyActionCode:
      return "Apply Action Code"
    case .verifyPasswordResetCode:
      return "Verify Password Reset Code"
    // Multi factor
    case .phoneEnroll:
      return "Phone Enroll"
    case .totpEnroll:
      return "TOTP Enroll"
    case .multifactorUnenroll:
      return "Multifactor unenroll"
    }
  }

  // Failable initializer to create an `AuthMenu` from its corresponding `name` value.
  // - Parameter rawValue: String value representing `AuthMenu`'s name or type.
  init?(rawValue: String) {
    switch rawValue {
    case "Settings":
      self = .settings
    case "Google":
      self = .google
    case "Apple":
      self = .apple
    case "Twitter":
      self = .twitter
    case "Microsoft":
      self = .microsoft
    case "GitHub":
      self = .gitHub
    case "Yahoo":
      self = .yahoo
    case "LinkedIn":
      self = .linkedIn
    case "Facebook":
      self = .facebook
    case "Game Center":
      self = .gameCenter
    case "Email & Password Login":
      self = .emailPassword
    case "Email Link/Passwordless":
      self = .passwordless
    case "Phone Number":
      self = .phoneNumber
    case "Anonymous Authentication":
      self = .anonymous
    case "Custom Auth System":
      self = .custom
    case "Initialize reCAPTCHA Enterprise":
      self = .initRecaptcha
    case "Set Custom Auth Domain":
      self = .customAuthDomain
    case "Get Token":
      self = .getToken
    case "Get Token Force Refresh":
      self = .getTokenForceRefresh
    case "Add Auth State Change Listener":
      self = .addAuthStateChangeListener
    case "Remove Last Auth State Change Listener":
      self = .removeLastAuthStateChangeListener
    case "Add ID Token Change Listener":
      self = .addIdTokenChangeListener
    case "Remove Last ID Token Change Listener":
      self = .removeLastIdTokenChangeListener
    case "Verify Client":
      self = .verifyClient
    case "Delete App":
      self = .deleteApp
    case "Action Type":
      self = .actionType
    case "Continue URL":
      self = .continueURL
    case "Link Domain":
      self = .linkDomain
    case "Request Verify Email":
      self = .requestVerifyEmail
    case "Request Password Reset":
      self = .requestPasswordReset
    case "Reset Password":
      self = .resetPassword
    case "Check Action Code":
      self = .checkActionCode
    case "Apply Action Code":
      self = .applyActionCode
    case "Verify Password Reset Code":
      self = .verifyPasswordResetCode
    case "Phone Enroll":
      self = .phoneEnroll
    case "TOTP Enroll":
      self = .totpEnroll
    case "Multifactor unenroll":
      self = .multifactorUnenroll
    default:
      return nil
    }
  }
}

enum ActionCodeRequestType: String {
  case email
  case `continue`
  case inApp

  var name: String {
    switch self {
    case .email:
      return "Email Only"
    case .inApp:
      return "In-App + Continue URL"
    case .continue:
      return "Continue URL"
    }
  }

  init?(rawValue: String) {
    switch rawValue {
    case "Email Only":
      self = .email
    case "In-App + Continue URL":
      self = .inApp
    case "Continue URL":
      self = .continue
    default:
      return nil
    }
  }
}

// MARK: DataSourceProvidable

class AuthMenuData: DataSourceProvidable {
  private static var providers: [AuthMenu] {
    [.google, .apple, .twitter, .microsoft, .gitHub, .yahoo, .linkedIn, .facebook, .gameCenter]
  }

  static var settingsSection: Section {
    let header = "Auth Settings"
    let item = Item(title: AuthMenu.settings.name, hasNestedContent: true)
    return Section(headerDescription: header, items: [item])
  }

  static var providerSection: Section {
    let providers = self.providers.map { Item(title: $0.name) }
    let header = "Identity Providers"
    let footer = "Choose a login flow from one of the identity providers above."
    return Section(headerDescription: header, footerDescription: footer, items: providers)
  }

  static var emailPasswordSection: Section {
    let image = UIImage(named: "firebaseIcon")
    let header = "Email and Password Login"
    let item = Item(title: AuthMenu.emailPassword.name, hasNestedContent: true, image: image)
    return Section(headerDescription: header, items: [item])
  }

  static var otherSection: Section {
    let lockSymbol = UIImage.systemImage("lock.slash.fill", tintColor: .systemOrange)
    let phoneSymbol = UIImage.systemImage("phone.fill", tintColor: .systemOrange)
    let anonSymbol = UIImage.systemImage("questionmark.circle.fill", tintColor: .systemOrange)
    let shieldSymbol = UIImage.systemImage("lock.shield.fill", tintColor: .systemOrange)

    let otherOptions = [
      Item(title: AuthMenu.passwordless.name, image: lockSymbol),
      Item(title: AuthMenu.phoneNumber.name, image: phoneSymbol),
      Item(title: AuthMenu.anonymous.name, image: anonSymbol),
      Item(title: AuthMenu.custom.name, image: shieldSymbol),
    ]
    let header = "Other Authentication Methods"
    return Section(headerDescription: header, items: otherOptions)
  }

  static var recaptchaSection: Section {
    let image = UIImage(named: "firebaseIcon")
    let header = "Initialize reCAPTCHA Enterprise"
    let item = Item(title: AuthMenu.initRecaptcha.name, hasNestedContent: false, image: image)
    return Section(headerDescription: header, items: [item])
  }

  static var customAuthDomainSection: Section {
    let image = UIImage(named: "firebaseIcon")
    let header = "Custom Auth Domain"
    let item = Item(title: AuthMenu.customAuthDomain.name, hasNestedContent: false, image: image)
    return Section(headerDescription: header, items: [item])
  }

  static var appSection: Section {
    let header = "APP"
    let items: [Item] = [
      Item(title: AuthMenu.getToken.name),
      Item(title: AuthMenu.getTokenForceRefresh.name),
      Item(title: AuthMenu.addAuthStateChangeListener.name),
      Item(title: AuthMenu.removeLastAuthStateChangeListener.name),
      Item(title: AuthMenu.addIdTokenChangeListener.name),
      Item(title: AuthMenu.removeLastIdTokenChangeListener.name),
      Item(title: AuthMenu.verifyClient.name),
      Item(title: AuthMenu.deleteApp.name),
    ]
    return Section(headerDescription: header, items: items)
  }

  static var oobSection: Section {
    let header = "OOB"
    let items: [Item] = [
      Item(title: AuthMenu.actionType.name, detailTitle: ActionCodeRequestType.inApp.name),
      Item(title: AuthMenu.continueURL.name, detailTitle: "--", isEditable: true),
      Item(title: AuthMenu.linkDomain.name, detailTitle: "--", isEditable: true),
      Item(title: AuthMenu.requestVerifyEmail.name),
      Item(title: AuthMenu.requestPasswordReset.name),
      Item(title: AuthMenu.resetPassword.name),
      Item(title: AuthMenu.checkActionCode.name),
      Item(title: AuthMenu.applyActionCode.name),
      Item(title: AuthMenu.verifyPasswordResetCode.name),
    ]
    return Section(headerDescription: header, items: items)
  }

  static var multifactorSection: Section {
    let header = "Multi Factor"
    let items: [Item] = [
      Item(title: AuthMenu.phoneEnroll.name),
      Item(title: AuthMenu.totpEnroll.name),
      Item(title: AuthMenu.multifactorUnenroll.name),
    ]
    return Section(headerDescription: header, items: items)
  }

  static let sections: [Section] =
    [settingsSection, providerSection, emailPasswordSection, otherSection, recaptchaSection,
     customAuthDomainSection, appSection, oobSection, multifactorSection]

  static var authLinkSections: [Section] {
    let allItems = [providerSection, emailPasswordSection, otherSection].flatMap { $0.items }
    let header = "Manage linking between providers"
    let footer =
      "Select an unchecked row to link the currently signed in user to that auth provider. To unlink the user from a linked provider, select its corresponding row marked with a checkmark."
    return [Section(headerDescription: header, footerDescription: footer, items: allItems)]
  }

  var sections: [Section] = AuthMenuData.sections
}
