import {
  CreateCustomer,
  CreatePassword,
  GetContext,
  GetLoggedCustomer,
  SendCode,
  VerifyCode,
  VerifyCPF
} from '@agiliza/api/domain'
import { extractNumbers } from '@agiliza/utils/extractors'
import { ClienteApi, Configuration, DominioApi } from '@microcredito/client'

import {
  AuthenticationApiMappers,
  AuthenticationMappers,
  CustomerMappers,
  ErrorMappers
} from '../mappers'
import { API_URL, mapUserAgentToString, UserAgent } from './shared'

export interface AuthenticationRepository {
  getContext(): Promise<GetContext['Output']>
  verifyCPF(input: VerifyCPF['Input']): Promise<VerifyCPF['Output']>
  createCustomer(input: CreateCustomer['Input']): Promise<CreateCustomer['Output']>
  sendCode(input: SendCode['Input']): Promise<SendCode['Output']>
  verifyCode(input: VerifyCode['Input']): Promise<VerifyCode['Output']>
  createPassword(input: CreatePassword['Input']): Promise<CreatePassword['Output']>
  getLoggedCustomer(): Promise<GetLoggedCustomer['Output']>
}

export class AuthenticationRepositoryImpl implements AuthenticationRepository {
  private api: DominioApi
  private clienteApi: ClienteApi
  private errorAdapter: ErrorMappers.ResponseErrorAdapter
  private stateMapper: AuthenticationMappers.StateMapper
  private cityMapper: AuthenticationMappers.CityMapper
  private customerMapper: CustomerMappers.CustomerMapper
  private authenticationApiMapper: AuthenticationApiMappers.CreateCustomerApiMapper

  constructor(userAgent: string) {
    this.errorAdapter = new ErrorMappers.ResponseErrorAdapter()
    this.stateMapper = new AuthenticationMappers.StateMapper()
    this.cityMapper = new AuthenticationMappers.CityMapper()
    this.customerMapper = new CustomerMappers.CustomerMapper()
    this.authenticationApiMapper = new AuthenticationApiMappers.CreateCustomerApiMapper()
    this.api = new DominioApi(
      new Configuration({
        basePath: API_URL,
        headers: {
          'User-Agent': userAgent,
        },
      })
    )
    this.clienteApi = new ClienteApi(
      new Configuration({
        basePath: API_URL,
        headers: {
          'User-Agent': userAgent,
        },
      })
    )
  }

  public getContext = async (): Promise<GetContext['Output']> => {
    try {
      const estados = await this.api.obterEstados()
      const cidades = await this.api.obterMunicipios()
      return { states: estados.map(this.stateMapper.mapApiModelToDomain), cities: cidades.map(this.cityMapper.mapApiModelToDomain) }
    } catch (e) {
      const result = await this.errorAdapter.mapApiModelToDomain(e)
      throw result
    }
  }

  public verifyCPF = async (input: VerifyCPF['Input']): Promise<VerifyCPF['Output']> => {
    try {
      await this.clienteApi.iniciarPrimeiroAcessoCliente({ iniciarPrimeiroAcessoRequestApiModel: { cpfcnpj: extractNumbers(input.cpf) } })
    } catch (e) {
      const result = await this.errorAdapter.mapApiModelToDomain(e)
      throw result
    }
  }

  public createCustomer = async (input: CreateCustomer['Input']): Promise<CreateCustomer['Output']> => {
    try {
      const cliente = await this.clienteApi.criarUsuarioPrimeiroAcesso(this.authenticationApiMapper.mapDomainToApiModel(input))
      return { customer: this.customerMapper.mapApiModelToDomain(cliente) }
    } catch (e) {
      const result = await this.errorAdapter.mapApiModelToDomain(e)
      throw result
    }
  }

  public sendCode = async (input: SendCode['Input']): Promise<SendCode['Output']> => {
    try {
      await this.clienteApi.enviarCodigoPrimeiroAcessoCliente({
        enviarCodigoPrimeiroAcessoRequestApiModel: { cpfcnpj: extractNumbers(input.cpf), email: input.email },
      })
    } catch (e) {
      const result = await this.errorAdapter.mapApiModelToDomain(e)
      throw result
    }
  }

  public verifyCode = async (input: VerifyCode['Input']): Promise<VerifyCode['Output']> => {
    try {
      await this.clienteApi.validarCodigoPrimeiroAcessoCliente({
        validarCodigoPrimeiroAcessoRequestApiModel: { cpfcnpj: extractNumbers(input.cpf), codigo: extractNumbers(input.code) },
      })
    } catch (e) {
      const result = await this.errorAdapter.mapApiModelToDomain(e)
      throw result
    }
  }

  public createPassword = async (input: CreatePassword['Input']): Promise<CreatePassword['Output']> => {
    try {
      await this.clienteApi.definirSenhaPrimeiroAcessoCliente({
        definirSenhaPrimeiroAcessoRequestApiModel: { cpfcnpj: extractNumbers(input.cpf), codigo: extractNumbers(input.code), senha: input.password },
      })
    } catch (e) {
      const result = await this.errorAdapter.mapApiModelToDomain(e)
      throw result
    }
  }

  public getLoggedCustomer = async (): Promise<GetLoggedCustomer['Output']> => {
    try {
      const usuario = await this.clienteApi.obterUsuarioLogado()
      return this.customerMapper.mapApiModelToDomain(usuario)
    } catch (e) {
      const result = await this.errorAdapter.mapApiModelToDomain(e)
      throw result
    }
  }
}

export class AuthenticationRepositoryImplFactory {
  static create(userAgent: UserAgent) {
    const repository = new AuthenticationRepositoryImpl(mapUserAgentToString(userAgent))
    return repository
  }
}
