import { property } from 'lit/decorators.js'
import { EventTypes as CoreEventTypes } from '@api-client/core/events/EventTypes.js'
import type { IOAuth2Authorization, ITokenInfo } from '@api-client/core/models/Authorization.js'
import type { ContextEvent } from '@api-client/core/events/BaseEvents.js'
import type { OauthProcessingOptions } from '@api-client/core/authorization/types.js'
import { OAuth2Authorization } from '@api-client/core/authorization/OAuth2Authorization.js'
import ApiElement from '../ApiElement.js'

export const authorizeHandler = Symbol('authorizeHandler')

/**
 * An element that utilizes the `OAuth2Authorization` class in a web component.
 * It handles DOM events to perform the authorization.
 */
export default class OAuth2AuthorizationElement extends ApiElement {
  /**
   * When set it uses this value to prefix the call to the
   * OAuth 2 token endpoint. This is to support use cases when
   * the requests should be proxied through a server to avoid CORS problems.
   * @attribute
   */
  @property({ type: String }) accessor tokenProxy: string | undefined

  /**
   * When set it encodes the token URI value before adding it to the
   * `tokenProxy`. This is to be used when the proxy takes the target
   * URL as a query parameter.
   * @attribute
   */
  @property({ type: String }) accessor tokenProxyEncode: boolean | undefined

  constructor() {
    super()
    this[authorizeHandler] = this[authorizeHandler].bind(this)
  }

  override connectedCallback(): void {
    super.connectedCallback()
    this.eventsTarget.addEventListener(CoreEventTypes.Authorization.OAuth2.authorize, this[authorizeHandler])
    this.setAttribute('aria-hidden', 'true')
  }

  override disconnectedCallback(): void {
    super.disconnectedCallback()
    this.eventsTarget.removeEventListener(CoreEventTypes.Authorization.OAuth2.authorize, this[authorizeHandler])
  }

  [authorizeHandler](event: Event): void {
    const e = event as ContextEvent<IOAuth2Authorization, ITokenInfo>
    const config = { ...e.detail } as IOAuth2Authorization
    e.detail.result = this.authorize(config)
  }

  /**
   * Authorize the user using provided settings.
   * This is left for compatibility. Use the `OAuth2Authorization` instead.
   *
   * @param settings The authorization configuration.
   */
  async authorize(settings: IOAuth2Authorization): Promise<ITokenInfo> {
    const { tokenProxy, tokenProxyEncode } = this
    const options: OauthProcessingOptions = {}
    if (tokenProxy && typeof tokenProxy === 'string') {
      options.tokenProxy = tokenProxy
    }
    if (tokenProxy && tokenProxyEncode && typeof tokenProxyEncode === 'boolean') {
      options.tokenProxyEncode = tokenProxyEncode
    }
    const auth = new OAuth2Authorization(settings, options)
    auth.checkConfig()
    return auth.authorize() as Promise<ITokenInfo>
  }
}
