import { BadRequest } from '@feathersjs/errors'
import { Id, NullableId, Params, ServiceMethods } from '@feathersjs/feathers'
import appRootPath from 'app-root-path'
import * as path from 'path'
import * as pug from 'pug'

import { Application } from '../../../declarations'
import config from '../../appconfig'
import Paginated from '../../types/PageObject'
import { getLink, sendEmail, sendSms } from '../auth-management/auth-management.utils'
import { IdentityProvider } from '../identity-provider/identity-provider.class'

interface Data {}

interface ServiceOptions {}

const emailAccountTemplatesPath = path.join(appRootPath.path, 'packages', 'server-core', 'email-templates', 'account')
export class Magiclink implements ServiceMethods<Data> {
  app: Application
  options: ServiceOptions
  docs: any

  constructor(options: ServiceOptions = {}, app: Application) {
    this.options = options
    this.app = app
  }

  async setup() {}

  /**
   * A function which find magic link  and display it
   *
   * @param params
   * @returns {@Array} all magic link
   */
  async find(params?: Params): Promise<Data[] | Paginated<Data>> {
    return []
  }

  /**
   * A function which find specific magic link by id
   *
   * @param id of specific magic link
   * @param params
   * @returns {@Object} contains id of magic link and message
   */
  async get(id: Id, params?: Params): Promise<Data> {
    return {
      id,
      text: `A new message with ID: ${id}!`
    }
  }
  /**
   * A function which is used to update magic link
   *
   * @param id
   * @param data which will be used for updating magic link
   * @param params
   * @returns updated data
   */
  async update(id: NullableId, data: Data, params?: Params): Promise<Data> {
    return data
  }

  /**
   * A function which is used to update magic link
   *
   * @param id
   * @param data used to update
   * @param params
   * @returns data
   */
  async patch(id: NullableId, data: Data, params?: Params): Promise<Data> {
    return data
  }
  /**
   * A function which is used to remove magic link
   *
   * @param id of magic link used to remove data
   * @param params
   * @returns id
   */
  async remove(id: NullableId, params?: Params): Promise<Data> {
    return { id }
  }

  /**
   * A function used to sent an email
   *
   * @param toEmail email of reciever
   * @param token generated token
   * @param type of login
   * @param identityProvider of user
   * @param subscriptionId optional subscription ID
   * @returns {@function} sent email
   */
  async sendEmail(
    toEmail: string,
    token: string,
    type: 'connection' | 'login',
    identityProvider: IdentityProvider,
    subscriptionId?: string
  ): Promise<void> {
    const hashLink = getLink(type, token, subscriptionId ?? '')
    let subscription
    let username
    if (subscriptionId != null) {
      subscription = await this.app.service('subscription' as any).find({
        id: subscriptionId
      })

      if (subscription.total === 0) {
        throw new BadRequest('Invalid subscription')
      }

      const subscriptionUser = await this.app.service('user').get(subscription.data[0].userId)

      username = subscriptionUser.name
    }
    const templatePath =
      subscriptionId == null
        ? path.join(emailAccountTemplatesPath, 'magiclink-email.pug')
        : path.join(emailAccountTemplatesPath, 'magiclink-email-subscription.pug')

    const compiledHTML = pug.compileFile(templatePath)({
      logo: config.client.logo,
      title: config.client.title,
      hashLink,
      username: username
    })
    const mailSender = config.email.from
    const email = {
      from: mailSender,
      to: toEmail,
      subject: config.email.subject.login,
      html: compiledHTML
    }

    return await sendEmail(this.app, email)
  }

  /**
   * A function which used to send sms
   *
   * @param mobile of receiver user
   * @param token generated token
   * @param type of login
   * @returns {@function}  send sms
   */

  async sendSms(mobile: string, token: string, type: 'connection' | 'login'): Promise<void> {
    const hashLink = getLink(type, token, '')
    const templatePath = path.join(emailAccountTemplatesPath, 'magiclink-sms.pug')
    const compiledHTML = pug
      .compileFile(templatePath)({
        title: config.client.title,
        hashLink
      })
      .replace(/&amp;/g, '&') // Text message links can't have HTML escaped ampersands.

    const sms = {
      mobile,
      text: compiledHTML
    }
    return await sendSms(this.app, sms)
  }
  /**
   * A function which is used to create magic link
   *
   * @param data used create magic link
   * @param params contain user info
   * @returns creted data
   */

  async create(data: any, params?: Params): Promise<Data> {
    const identityProviderService = this.app.service('identity-provider')

    // check magiclink type
    let token = ''
    if (data.type === 'email') token = data.email
    else if (data.type === 'sms') token = data.mobile

    let identityProvider
    const identityProviders = (
      (await identityProviderService.find({
        query: {
          token: token,
          type: data.type
        }
      })) as any
    ).data

    if (identityProviders.length === 0) {
      identityProvider = await identityProviderService.create(
        {
          token: token,
          type: data.type,
          accountIdentifier: token
        },
        params
      )
    } else {
      identityProvider = identityProviders[0]
    }

    if (identityProvider) {
      const loginToken = await this.app.service('login-token').create({
        identityProviderId: identityProvider.id
      })

      if (data.type === 'email') {
        await this.sendEmail(
          data.email,
          loginToken.token,
          data.userId ? 'connection' : 'login',
          identityProvider,
          data.subscriptionId
        )
      } else if (data.type === 'sms') {
        await this.sendSms(data.mobile, loginToken.token, data.userId ? 'connection' : 'login')
      }
    }
    return data
  }
}
