import {
  AddCDIdentification,
  AddCDReference,
  AddCDVehicle,
  CreateProposal,
  GetCDReferences,
  GetCDVehicles,
  UpdateCDAddress,
  UpdateCDHome,
  UpdateCDPersonalData,
  UpdateCDReference,
  UpdateCDSourceIncome,
  UpdateCDSpouseData,
  UpdateCDVehicle
} from '@agiliza/api/domain'
import { ClienteApi, Configuration } from '@microcredito/client'

import { ErrorMappers, ProposalDataApiMappers, ProposalDataMappers } from '../../mappers'
import { API_URL, mapUserAgentToString, SESSION_KEY, UserAgent } from '../shared'

export interface PDCustomerDataDataRepository {
  createProposal(input: CreateProposal['Input']): Promise<CreateProposal['Output']>
  addIdentification(input: AddCDIdentification['Input']): Promise<AddCDIdentification['Output']>
  updatePersonalData(input: UpdateCDPersonalData['Input']): Promise<UpdateCDPersonalData['Output']>
  updateAddress(input: UpdateCDAddress['Input']): Promise<UpdateCDAddress['Output']>
  updateSourceIncome(input: UpdateCDSourceIncome['Input']): Promise<UpdateCDSourceIncome['Output']>
  updateSpouseData(input: UpdateCDSpouseData['Input']): Promise<UpdateCDSpouseData['Output']>
  getReferences(input: GetCDReferences['Input']): Promise<GetCDReferences['Output']>
  addReference(input: AddCDReference['Input']): Promise<AddCDReference['Output']>
  updateReference(input: UpdateCDReference['Input']): Promise<UpdateCDReference['Output']>
  updateHome(input: UpdateCDHome['Input']): Promise<UpdateCDHome['Output']>
  getVehicles(input: GetCDVehicles['Input']): Promise<GetCDVehicles['Output']>
  addVehicle(input: AddCDVehicle['Input']): Promise<AddCDVehicle['Output']>
  updateVehicle(input: UpdateCDVehicle['Input']): Promise<UpdateCDVehicle['Output']>
}

export class PDCustomerDataRepositoryImpl implements PDCustomerDataDataRepository {
  private api: ClienteApi
  private errorAdapter: ErrorMappers.ResponseErrorAdapter
  private createProposalApiMapper: ProposalDataApiMappers.CreateProposalApiMapper
  private cdUpdateIdentificationApiMapper: ProposalDataApiMappers.CDAddIdentificationMapper
  private cdUpdatePersonalDataApiMapper: ProposalDataApiMappers.CDUpdatePersonalDataMapper
  private cdIdentificationMapper: ProposalDataMappers.CDIdentificationAdapter
  private cdPersonalDataMapper: ProposalDataMappers.CDPersonalDataMapper
  private cdUpdateAddressApiMapper: ProposalDataApiMappers.CDUpdateAddressApiMapper
  private cdUpdateAddressMapper: ProposalDataMappers.CDUpdateAddressMapper
  private cdUpdateSourceIncomeApiMapper: ProposalDataApiMappers.CDUpdateSourceIncomeApiMapper
  private cdUpdateSourceIncomeMapper: ProposalDataMappers.CDSourceIncomeAdapter
  private cdUpdateSpouseDataApiMapper: ProposalDataApiMappers.CDUpdateSpouseDataApiMapper
  private cdUpdateSpouseDataMapper: ProposalDataMappers.CDUpdateSpouseDataMapper
  private cdReferenceMapper: ProposalDataMappers.ReferenceMapper
  private cdAddReferenceApiMapper: ProposalDataApiMappers.CDAddReferenceApiMapper
  private cdUpdateReferenceApiMapper: ProposalDataApiMappers.CDUpdateReferenceApiMapper
  private cdUpdateHomeApiMapper: ProposalDataApiMappers.CDUpdateHomeApiMapper
  private cdUpdateHomeMapper: ProposalDataMappers.CDUpdateHomeMapper
  private cdVehicleMapper: ProposalDataMappers.VehicleMapper
  private cdAddVehicleApiMapper: ProposalDataApiMappers.CDAddVehicleApiMapper
  private cdUpdateVehicleApiMapper: ProposalDataApiMappers.CDUpdateVehicleApiMapper

  constructor(userAgent: string, accessToken: string) {
    this.errorAdapter = new ErrorMappers.ResponseErrorAdapter()
    this.createProposalApiMapper = new ProposalDataApiMappers.CreateProposalApiMapper()
    this.cdIdentificationMapper = new ProposalDataMappers.CDIdentificationAdapter()
    this.cdUpdateIdentificationApiMapper = new ProposalDataApiMappers.CDAddIdentificationMapper()
    this.cdUpdatePersonalDataApiMapper = new ProposalDataApiMappers.CDUpdatePersonalDataMapper()
    this.cdPersonalDataMapper = new ProposalDataMappers.CDPersonalDataMapper()
    this.cdUpdateAddressApiMapper = new ProposalDataApiMappers.CDUpdateAddressApiMapper()
    this.cdUpdateAddressMapper = new ProposalDataMappers.CDUpdateAddressMapper()
    this.cdUpdateSourceIncomeApiMapper = new ProposalDataApiMappers.CDUpdateSourceIncomeApiMapper()
    this.cdUpdateSourceIncomeMapper = new ProposalDataMappers.CDSourceIncomeAdapter()
    this.cdUpdateSpouseDataApiMapper = new ProposalDataApiMappers.CDUpdateSpouseDataApiMapper()
    this.cdUpdateSpouseDataMapper = new ProposalDataMappers.CDUpdateSpouseDataMapper()
    this.cdReferenceMapper = new ProposalDataMappers.ReferenceMapper()
    this.cdAddReferenceApiMapper = new ProposalDataApiMappers.CDAddReferenceApiMapper()
    this.cdUpdateReferenceApiMapper = new ProposalDataApiMappers.CDUpdateReferenceApiMapper()
    this.cdUpdateHomeApiMapper = new ProposalDataApiMappers.CDUpdateHomeApiMapper()
    this.cdUpdateHomeMapper = new ProposalDataMappers.CDUpdateHomeMapper()
    this.cdVehicleMapper = new ProposalDataMappers.VehicleMapper()
    this.cdAddVehicleApiMapper = new ProposalDataApiMappers.CDAddVehicleApiMapper()
    this.cdUpdateVehicleApiMapper = new ProposalDataApiMappers.CDUpdateVehicleApiMapper()

    this.api = new ClienteApi(
      new Configuration({
        basePath: API_URL,
        accessToken,
        headers: {
          'User-Agent': userAgent,
        },
      })
    )
  }

  public addIdentification = async (input: AddCDIdentification['Input']): Promise<AddCDIdentification['Output']> => {
    try {
      const response = await this.api.incluirPessoaEnvolvida(this.cdUpdateIdentificationApiMapper.mapDomainToApiModel(input))
      return this.cdIdentificationMapper.mapApiModelToDomain(response)
    } catch (e) {
      const result = await this.errorAdapter.mapApiModelToDomain(e)
      throw result
    }
  }

  public createProposal = async (input: CreateProposal['Input']): Promise<CreateProposal['Output']> => {
    try {
      const response = await this.api.criarProposta(this.createProposalApiMapper.mapDomainToApiModel(input))
      return response.propostaId || ''
    } catch (e) {
      const result = await this.errorAdapter.mapApiModelToDomain(e)
      throw result
    }
  }

  public updatePersonalData = async (input: UpdateCDPersonalData['Input']): Promise<UpdateCDPersonalData['Output']> => {
    try {
      const response = await this.api.atualizarDadosPessoaisPessoaEnvolvida(this.cdUpdatePersonalDataApiMapper.mapDomainToApiModel(input))
      return this.cdPersonalDataMapper.mapApiModelToDomain(response)
    } catch (e) {
      const result = await this.errorAdapter.mapApiModelToDomain(e)
      throw result
    }
  }

  public updateAddress = async (input: UpdateCDAddress['Input']): Promise<UpdateCDAddress['Output']> => {
    try {
      const response = await this.api.atualizarEnderecoPessoaEnvolvida(this.cdUpdateAddressApiMapper.mapDomainToApiModel(input))
      return this.cdUpdateAddressMapper.mapApiModelToDomain(response)
    } catch (e) {
      const result = await this.errorAdapter.mapApiModelToDomain(e)
      throw result
    }
  }

  public updateSourceIncome = async (input: UpdateCDSourceIncome['Input']): Promise<UpdateCDSourceIncome['Output']> => {
    try {
      const response = await this.api.atualizarFonteRendaPessoaEnvolvida(this.cdUpdateSourceIncomeApiMapper.mapDomainToApiModel(input))
      return this.cdUpdateSourceIncomeMapper.mapApiModelToDomain(response)
    } catch (e) {
      const result = await this.errorAdapter.mapApiModelToDomain(e)
      throw result
    }
  }

  public updateSpouseData = async (input: UpdateCDSpouseData['Input']): Promise<UpdateCDSpouseData['Output']> => {
    try {
      const response = await this.api.atualizarConjugePessoaEnvolvida(this.cdUpdateSpouseDataApiMapper.mapDomainToApiModel(input))
      return this.cdUpdateSpouseDataMapper.mapApiModelToDomain(response)
    } catch (e) {
      const result = await this.errorAdapter.mapApiModelToDomain(e)
      throw result
    }
  }

  public getReferences = async (input: GetCDReferences['Input']): Promise<GetCDReferences['Output']> => {
    try {
      const response = await this.api.obterReferenciasPessoaEnvolvida({ pessoaId: input.personId, projetoId: input.projectId })
      return response.map(this.cdReferenceMapper.mapApiModelToDomain)
    } catch (e) {
      const result = await this.errorAdapter.mapApiModelToDomain(e)
      throw result
    }
  }

  public addReference = async (input: AddCDReference['Input']): Promise<AddCDReference['Output']> => {
    try {
      const response = await this.api.criarReferenciaPessoaEnvolvida(this.cdAddReferenceApiMapper.mapDomainToApiModel(input))
      return response.map(this.cdReferenceMapper.mapApiModelToDomain)
    } catch (e) {
      const result = await this.errorAdapter.mapApiModelToDomain(e)
      throw result
    }
  }

  public updateReference = async (input: UpdateCDReference['Input']): Promise<UpdateCDReference['Output']> => {
    try {
      const response = await this.api.atualiarReferenciaPessoaEnvolvida(this.cdUpdateReferenceApiMapper.mapDomainToApiModel(input))
      return response.map(this.cdReferenceMapper.mapApiModelToDomain)
    } catch (e) {
      const result = await this.errorAdapter.mapApiModelToDomain(e)
      throw result
    }
  }

  public updateHome = async (input: UpdateCDHome['Input']): Promise<UpdateCDHome['Output']> => {
    try {
      const response = await this.api.atualizarDadosMoradiaPessoaEnvolvida(this.cdUpdateHomeApiMapper.mapDomainToApiModel(input))
      return this.cdUpdateHomeMapper.mapApiModelToDomain(response)
    } catch (e) {
      const result = await this.errorAdapter.mapApiModelToDomain(e)
      throw result
    }
  }

  public getVehicles = async (input: GetCDVehicles['Input']): Promise<GetCDVehicles['Output']> => {
    try {
      const response = await this.api.obterVeiculosPessoaEnvolvida({ projetoId: input.projectId, pessoaId: input.personId })
      return response.map(this.cdVehicleMapper.mapApiModelToDomain)
    } catch (e) {
      const result = await this.errorAdapter.mapApiModelToDomain(e)
      throw result
    }
  }

  public addVehicle = async (input: AddCDVehicle['Input']): Promise<AddCDVehicle['Output']> => {
    try {
      const response = await this.api.criarVeiculoPessoaEnvolvida(this.cdAddVehicleApiMapper.mapDomainToApiModel(input))
      return response.map(this.cdVehicleMapper.mapApiModelToDomain)
    } catch (e) {
      const result = await this.errorAdapter.mapApiModelToDomain(e)
      throw result
    }
  }

  public updateVehicle = async (input: UpdateCDVehicle['Input']): Promise<UpdateCDVehicle['Output']> => {
    try {
      const response = await this.api.atualizarVeiculoPessoaEnvolvida(this.cdUpdateVehicleApiMapper.mapDomainToApiModel(input))
      return response.map(this.cdVehicleMapper.mapApiModelToDomain)
    } catch (e) {
      const result = await this.errorAdapter.mapApiModelToDomain(e)
      throw result
    }
  }
}

export class PDCustomerDataRepositoryImplFactory {
  static create(userAgent: UserAgent) {
    const accessToken = localStorage.getItem(SESSION_KEY) || ''
    const repository = new PDCustomerDataRepositoryImpl(mapUserAgentToString(userAgent), accessToken)
    return repository
  }
}
