import { isNullOrUndefined } from 'util';
import { Injectable } from '@angular/core';
import { User, Address, Features, Organisation } from '../models/user.model';
import {
  Gender,
  AddressDTO,
  EntityDTOContainerOfShippingAddressDTO,
  OrganisationDTO,
  CommunicationDetailsDTO,
  AssignedPayerDTO,
  CustomerInfoDTO,
  PayerDTO,
  CustomerDTO,
  ShippingAddressDTO,
} from '@swagger/crm';
import { NotificationChannels } from '../models/notification-channels.enum';
import { EntityDTOContainerOfBranchDTO, ShippingAddressDTO as CheckoutShippingAddressDTO } from '@swagger/checkout';
import { DatePipe } from '@angular/common';
import { BACKEND_API_TIMESTAMP_FORMAT } from '../utils/app.formats';
import { KeyValueDTOOfStringAndString } from '@swagger/cat';

@Injectable({ providedIn: 'root' })
export class CustomerMapping {
  constructor(private datePipe: DatePipe) {}

  fromUser(customer: User): CustomerDTO {
    if (isNullOrUndefined(customer)) {
      throw new Error('argument customer:User is null or undefined.');
    }

    let gender: Gender = 0;
    let address: AddressDTO = {};
    const addresses: EntityDTOContainerOfShippingAddressDTO[] = [];
    let organisation: OrganisationDTO = {};
    let communicationDetails: CommunicationDetailsDTO = {};
    const notificationChannels = this.getNotificationChannels(customer);
    const invoiceAdresses: AssignedPayerDTO[] = [];
    const shippingAddresses: EntityDTOContainerOfShippingAddressDTO[] = [];
    const features: Array<KeyValueDTOOfStringAndString> = [];
    let isGuestAccount: boolean;
    let hasOnlineAccount: boolean;

    if (customer.mobile_number || customer.phone_number || customer.email) {
      communicationDetails = {
        email: customer.email ? customer.email : null,
        mobile: customer.mobile_number ? customer.mobile_number : null,
        phone: customer.phone_number ? customer.phone_number : null,
        fax: null,
      };
    }

    if (customer.gender) {
      gender = this.getGender(customer.gender);
    }

    if (customer.delivery_addres) {
      address = {
        city: customer.delivery_addres.city,
        country: customer.delivery_addres.country,
        zipCode: customer.delivery_addres.zip + '',
        street: customer.delivery_addres.street,
        streetNumber: customer.delivery_addres.streetNo + '',
        info: customer.delivery_addres.note ? customer.delivery_addres.note : null,
      };
    }

    if (customer.invoice_address) {
      address = {
        city: customer.invoice_address.city,
        country: customer.invoice_address.country,
        zipCode: customer.invoice_address.zip + '',
        street: customer.invoice_address.street,
        streetNumber: customer.invoice_address.streetNo + '',
        info: customer.invoice_address.note ? customer.invoice_address.note : null,
      };
    }

    if (customer.organisation) {
      organisation = {
        name: customer.organisation.name, // Firma
        nameSuffix: customer.organisation.extraAddress, // Addresszutsaiz
        department: customer.organisation.department, // abteilung
        vatId: customer.organisation.vatId, // USt-ID
      };
    }

    if (Array.isArray(customer.poossible_delivery_addresses) && customer.poossible_delivery_addresses.length > 0) {
      customer.poossible_delivery_addresses.forEach((adrs: Address) => {
        addresses.push(this.addressesItem(adrs));
        shippingAddresses.push(this.addressesItem(adrs));
      });
    }

    if (Array.isArray(customer.poossible_invoice_addresses) && customer.poossible_invoice_addresses.length > 0) {
      customer.poossible_invoice_addresses.forEach((adrs: Address) => {
        if (adrs) {
          addresses.push(this.addressesItem(adrs));
          invoiceAdresses.push({
            payer: {
              data: {
                firstName: adrs.first_name,
                lastName: adrs.last_name,
                gender: adrs.gender ? this.getGender(adrs.gender) : 0,
                title: adrs.title ? adrs.title : null,
                address: this.invoiceAdressItem(adrs),
                payerType: !!adrs.payer_type ? adrs.payer_type : 8,
              },
            },
          });
        }
      });
    }

    if (Array.isArray(customer.features) && customer.features.length > 0) {
      customer.features.forEach((feature) => {
        if (feature.key === 'guestaccount') {
          isGuestAccount = true;
        }
        if (feature.key === 'onlineshop') {
          hasOnlineAccount = true;
        }
        features.push({
          key: feature.key,
          value: feature.value,
          description: feature.description,
          enabled: true,
        });
      });
    }

    const createdInBranch = <EntityDTOContainerOfBranchDTO>{
      id: customer.createdInBranch,
      // TODO: what is the meaning of enabled attribute
      enabled: true,
    };

    return <CustomerDTO>{
      id: customer.id,
      firstName: customer.first_name,
      lastName: customer.last_name,
      title: customer.title ? customer.title : null,
      dateOfBirth: customer.date_of_birth ? customer.date_of_birth : null,
      customerNumber: customer.customerId ? customer.customerId : null,
      customerType: 8, // 8 => B2C customer, and the store app will have only B2C customers
      customerGroup: null,
      agentComment: null,
      statusChangeComment: null,
      deactivationComment: null,
      statusComment: null,
      label: null,
      user: null,
      campaignCode: null,
      bonusCard: null,
      isGuestAccount: isGuestAccount,
      hasOnlineAccount: hasOnlineAccount,
      attributes: null,
      createdInBranch: createdInBranch,
      customerStatus: 0, // Will this be ever different?
      gender: gender,
      address: address,
      fetchOnDeliveryNote: null,
      payers: invoiceAdresses,
      organisation: organisation,
      shippingAddresses: shippingAddresses,
      communicationDetails: communicationDetails,
      notificationChannels: notificationChannels,
      features: features.length > 0 ? features : null,
    };
  }

  getGender(gender: string): Gender {
    if (gender === 'Frau') {
      return 4;
    } else if (gender === 'Herr') {
      return 2;
    } else {
      return 0;
    }
  }

  private addressesItem(address: Address): EntityDTOContainerOfShippingAddressDTO {
    if (!address) {
      return;
    }
    return {
      data: {
        firstName: address.first_name,
        lastName: address.last_name,
        gender: address.gender ? this.getGender(address.gender) : 0,
        title: address.title ? address.title : null,
        type: 1,
        address: {
          city: address.city,
          country: address.country,
          zipCode: address.zip + '',
          street: address.street,
          streetNumber: address.streetNo + '',
          info: address.note,
        },
      },
    };
  }

  private invoiceAdressItem(address: Address): AddressDTO {
    return {
      city: address.city,
      country: address.country,
      zipCode: address.zip + '',
      street: address.street,
      streetNumber: address.streetNo + '',
      info: address.note,
    };
  }

  customerInfoDTOtoUser(customerInfo: CustomerInfoDTO): User {
    if (isNullOrUndefined(customerInfo)) {
      throw new Error('argument customerInfo:CustomerInfoDTO is null or undefined.');
    }

    const id = customerInfo.id;
    const firstName = customerInfo.firstName;
    const lastName = customerInfo.lastName;
    const gender = this.getGenderFromCode(customerInfo.gender);

    let dateOfBirth: string;
    if (!!customerInfo.dateOfBirth) {
      dateOfBirth = customerInfo.dateOfBirth;
    }

    let email: string;
    let phoneNumber: string;
    let mobileNumber: string;
    if (!!customerInfo.communicationDetails) {
      email = customerInfo.communicationDetails.email;
      phoneNumber = customerInfo.communicationDetails.phone;
      mobileNumber = customerInfo.communicationDetails.mobile;
    }

    let newUser = false;
    if (customerInfo.version === 2) {
      newUser = true;
    }

    let notificationSms = false;
    let notificationEmail = false;
    if (customerInfo.notificationChannels) {
      notificationSms =
        customerInfo.notificationChannels === NotificationChannels.Sms ||
        customerInfo.notificationChannels === NotificationChannels.EmailSms;
      notificationEmail =
        customerInfo.notificationChannels === NotificationChannels.Email ||
        customerInfo.notificationChannels === NotificationChannels.EmailSms;
    }

    let baseAddres: Address = {} as Address;
    if (!!customerInfo.address) {
      baseAddres = {
        id: Date.now() + Math.random(),
        title: customerInfo.title,
        first_name: customerInfo.firstName,
        last_name: customerInfo.lastName,
        city: customerInfo.address.city ? customerInfo.address.city : undefined,
        zip: customerInfo.address.zipCode ? customerInfo.address.zipCode : undefined,
        country: customerInfo.address.country,
        street: customerInfo.address.street,
        streetNo: customerInfo.address.streetNumber ? +customerInfo.address.streetNumber : undefined,
      };
    }

    let features: Features[] = [];
    if (customerInfo.features) {
      features = customerInfo.features
        .filter((t) => t.enabled === true && !isNullOrUndefined(t.description))
        .map((t) => {
          return <Features>{
            key: t.key,
            value: t.value,
            description: t.description,
          };
        });
    }

    let organisation: Organisation = {};
    if (customerInfo.organisation) {
      organisation = {
        name: customerInfo.organisation.name,
        extraAddress: customerInfo.organisation.nameSuffix,
        department: customerInfo.organisation.department,
        vatId: customerInfo.organisation.vatId,
      };
    }

    // TODO: review missing info from the API
    return <User>{
      id: id,
      customerId: customerInfo.customerNumber,
      first_name: firstName,
      last_name: lastName,
      gender: gender,
      date_of_birth: dateOfBirth,
      email: email,
      phone_number: phoneNumber,
      mobile_number: mobileNumber,
      newUser: newUser,
      customer_card: false,
      newsletter: false, // missing info from API
      shop: false,
      notificationSms: notificationSms,
      notificationEmail: notificationEmail,
      payement_method: null, // missing info from API
      tolino: false, // missing info from API
      title: null, // missing info from API
      base_addres: baseAddres,
      hasOnlineAccount: customerInfo.hasOnlineAccount,
      isGuestAccount: customerInfo.isGuestAccount,
      features: features,
      changed: customerInfo.changed,
      created: customerInfo.created,
      customerNumber: customerInfo.customerNumber,
      organisation: organisation,
    };
  }

  private getGenderFromCode(gender: number): string {
    if (gender === 4) {
      return 'Frau';
    } else if (gender === 2) {
      return 'Herr';
    } else if (gender === 1) {
      return 'Divers';
    } else {
      return null;
    }
  }

  customerDTOtoUser(customerDto: CustomerDTO): User {
    if (isNullOrUndefined(customerDto)) {
      throw new Error('argument customerDto:CustomerInfoDTO is null or undefined.');
    }

    const id = customerDto.id;
    const firstName = customerDto.firstName;
    const lastName = customerDto.lastName;
    const gender = this.getGenderFromCode(customerDto.gender);

    let dateOfBirth: string;
    if (!!customerDto.dateOfBirth) {
      dateOfBirth = customerDto.dateOfBirth;
    }

    let email: string;
    let phoneNumber: string;
    let mobileNumber: string;
    if (!!customerDto.communicationDetails) {
      email = customerDto.communicationDetails.email;
      phoneNumber = customerDto.communicationDetails.phone;
      mobileNumber = customerDto.communicationDetails.mobile;
    }

    let newUser = false;
    if (customerDto.version === 2) {
      newUser = true;
    }

    let notificationSms = false;
    let notificationEmail = false;
    if (customerDto.notificationChannels) {
      notificationSms =
        customerDto.notificationChannels === NotificationChannels.Sms || customerDto.notificationChannels === NotificationChannels.EmailSms;
      notificationEmail =
        customerDto.notificationChannels === NotificationChannels.Email ||
        customerDto.notificationChannels === NotificationChannels.EmailSms;
    }

    let baseAddres: Address;
    if (!!customerDto.address) {
      baseAddres = {
        id: Date.now() + Math.random(),
        title: customerDto.title,
        first_name: customerDto.firstName,
        last_name: customerDto.lastName,
        city: customerDto.address.city,
        zip: customerDto.address.zipCode,
        country: customerDto.address.country,
        street: customerDto.address.street,
        streetNo: +customerDto.address.streetNumber,
      };
    }

    let features: Features[] = [];
    if (customerDto.features) {
      features = customerDto.features
        .filter((t) => t.enabled === true && !isNullOrUndefined(t.description))
        .map((t) => {
          return <Features>{
            key: t.key,
            value: t.value,
            description: t.description,
          };
        });
    }

    let organisation: Organisation = {};
    if (customerDto.organisation) {
      organisation = {
        name: customerDto.organisation.name,
        extraAddress: customerDto.organisation.nameSuffix,
        department: customerDto.organisation.department,
        vatId: customerDto.organisation.vatId,
      };
    }

    // TODO: review missing info from the API
    return <User>{
      id: id,
      customerId: customerDto.customerNumber,
      first_name: firstName,
      last_name: lastName,
      gender: gender,
      date_of_birth: dateOfBirth,
      email: email,
      phone_number: phoneNumber,
      mobile_number: mobileNumber,
      newUser: newUser,
      customer_card: false,
      createdInBranch: customerDto.createdInBranch ? customerDto.createdInBranch.id : null,
      newsletter: false, // missing info from API
      shop: false,
      notificationSms: notificationSms,
      notificationEmail: notificationEmail,
      payement_method: null, // missing info from API
      tolino: false, // missing info from API
      title: null, // missing info from API
      features: features,
      changed: customerDto.changed,
      created: customerDto.created,
      customerNumber: customerDto.customerNumber,
      organisation: organisation,
      hasOnlineAccount: customerDto.hasOnlineAccount,
    };
  }

  fromShippingAddressDtoToAddress(shippingAddress: ShippingAddressDTO): Address {
    if (isNullOrUndefined(shippingAddress)) {
      throw new Error('argument shippingAddress:ShippingAddressDTO is null or undefined.');
    }

    let gender = '';
    if (!!shippingAddress.gender) {
      gender = this.getGenderFromCode(shippingAddress.gender);
    }

    let country = '';
    let city = '';
    let street = '';
    let streetNo = null;
    let zip: string = null;
    if (!!shippingAddress.address) {
      country = shippingAddress.address.country;
      city = shippingAddress.address.city;
      street = shippingAddress.address.street;
      streetNo = shippingAddress.address.streetNumber;
      zip = shippingAddress.address.zipCode;
    }

    let company_name = null;
    let company_department = null;
    let company_tax_number = null;
    if (!!shippingAddress.organisation) {
      company_name = shippingAddress.organisation.name;
      company_department = shippingAddress.organisation.department;
      company_tax_number = shippingAddress.organisation.vatId;
    }

    let defaultSince: Date;
    if (!!shippingAddress.isDefault) {
      defaultSince = new Date(shippingAddress.isDefault);
    }

    return <Address>{
      id: shippingAddress.id,
      title: shippingAddress.title,
      first_name: shippingAddress.firstName,
      last_name: shippingAddress.lastName,
      gender: gender,
      country: country,
      city: city,
      street: street,
      streetNo: streetNo,
      zip: zip,
      company_name: company_name,
      company_department: company_department,
      company_tax_number: company_tax_number,
      defaultSince: defaultSince,
    };
  }

  fromAddressToShippingAddressDTO(address: Address, forUpdate: boolean): ShippingAddressDTO {
    if (isNullOrUndefined(address)) {
      throw new Error('argument address:Address is null or undefined.');
    }

    let _address: AddressDTO;
    if (forUpdate) {
      _address = <AddressDTO>{
        id: address.id,
        country: address.country,
        city: address.city,
        street: address.street,
        streetNumber: address.streetNo + '',
        zipCode: address.zip + '',
      };
    } else {
      _address = <AddressDTO>{
        country: address.country,
        city: address.city,
        street: address.street,
        streetNumber: address.streetNo + '',
        zipCode: address.zip + '',
      };
    }

    return <ShippingAddressDTO>{
      type: 1,
      title: address.title,
      firstName: address.first_name,
      lastName: address.last_name,
      gender: this.getGender(address.gender),
      status: 1,
      isDefault: this.datePipe.transform(new Date(), BACKEND_API_TIMESTAMP_FORMAT),
      organisation: <OrganisationDTO>{
        name: address.company_name,
        department: address.company_department,
        vatId: address.company_tax_number,
      },
      address: _address,
    };
  }

  fromAddressToAddressDTO(address: Address): AddressDTO {
    if (isNullOrUndefined(address)) {
      throw new Error('argument address:Address is null or undefined.');
    }

    return <AddressDTO>{
      country: address.country,
      city: address.city,
      street: address.street,
      streetNumber: address.streetNo + '',
      zipCode: address.zip + '',
    };
  }

  fromAddressToAssignedPAyerDto(address: Address): PayerDTO {
    if (!address) {
      return null;
    }

    let _address: AddressDTO;
    if (!isNullOrUndefined(address.id)) {
      _address = <AddressDTO>{
        id: address.id,
        country: address.country,
        city: address.city,
        street: address.street,
        streetNumber: address.streetNo + '',
        zipCode: address.zip + '',
      };
    } else {
      _address = <AddressDTO>{
        country: address.country,
        city: address.city,
        street: address.street,
        streetNumber: address.streetNo + '',
        zipCode: address.zip + '',
      };
    }
    return <PayerDTO>{
      type: 1,
      title: address.title,
      firstName: address.first_name,
      lastName: address.last_name,
      gender: this.getGender(address.gender),
      status: 1,
      isDefault: this.datePipe.transform(new Date(), BACKEND_API_TIMESTAMP_FORMAT),
      address: _address,
      payerNumber: address.payer_number,
      payerStatus: address.payer_status,
      payerType: !!address.payer_type ? address.payer_type : 8,
      organisation: <OrganisationDTO>{
        name: address.company_name,
        department: address.company_department,
        vatId: address.company_tax_number,
      },
    };
  }

  fromAssignedPayerDtoToAddress(assignedPayers: AssignedPayerDTO): Address {
    if (isNullOrUndefined(assignedPayers)) {
      throw new Error('argument shippingAddress:ShippingAddressDTO is null or undefined.');
    }

    let id = null;
    let firstName = null;
    let lastName = null;
    if (!!assignedPayers.payer && !!assignedPayers.payer.data) {
      id = assignedPayers.payer.data.id;
      firstName = assignedPayers.payer.data.firstName;
      lastName = assignedPayers.payer.data.lastName;
    }

    let gender = '';
    if (!!assignedPayers.payer && !!assignedPayers.payer.data && !!assignedPayers.payer.data.gender) {
      gender = this.getGenderFromCode(assignedPayers.payer.data.gender);
    }

    let country = '';
    let city = '';
    let street = '';
    let streetNo = null;
    let zip: string = null;
    if (!!assignedPayers.payer && !!assignedPayers.payer.data && !!assignedPayers.payer.data.address) {
      country = assignedPayers.payer.data.address.country;
      city = assignedPayers.payer.data.address.city;
      street = assignedPayers.payer.data.address.street;
      streetNo = assignedPayers.payer.data.address.streetNumber;
      zip = assignedPayers.payer.data.address.zipCode;
    }

    let payerNumber = null;
    let payerStatus = null;
    let payerType = null;
    let title = null;
    if (!!assignedPayers.payer && !!assignedPayers.payer.data) {
      payerNumber = assignedPayers.payer.data.payerNumber;
      payerStatus = assignedPayers.payer.data.payerStatus;
      payerType = assignedPayers.payer.data.payerType;
      title = assignedPayers.payer.data.title;
    }

    let company_name = null;
    let company_department = null;
    let company_tax_number = null;
    if (!!assignedPayers.payer && !!assignedPayers.payer.data && !!assignedPayers.payer.data.organisation) {
      company_name = assignedPayers.payer.data.organisation.name;
      company_department = assignedPayers.payer.data.organisation.department;
      company_tax_number = assignedPayers.payer.data.organisation.vatId;
    }

    let defaultSince: Date;
    if (!!assignedPayers.isDefault) {
      defaultSince = new Date(assignedPayers.isDefault);
    }

    return <Address>{
      id: id,
      title: title,
      first_name: firstName,
      last_name: lastName,
      gender: gender,
      country: country,
      city: city,
      street: street,
      streetNo: streetNo,
      zip: zip,
      company_name: company_name,
      company_department: company_department,
      company_tax_number: company_tax_number,
      payer_number: payerNumber,
      payer_status: payerStatus,
      payer_type: payerType,
      defaultSince: defaultSince,
    };
  }

  fromUserSimple(customer: User): CustomerDTO {
    if (isNullOrUndefined(customer)) {
      throw new Error('argument customer:User is null or undefined.');
    }

    let gender: Gender = 0;
    let address: AddressDTO = {};
    let organisation: OrganisationDTO = {};
    let communicationDetails: CommunicationDetailsDTO = {};
    const notificationChannels = this.getNotificationChannels(customer);

    if (customer.mobile_number || customer.phone_number || customer.email) {
      communicationDetails = {
        email: customer.email ? customer.email : null,
        mobile: customer.mobile_number ? customer.mobile_number : null,
        phone: customer.phone_number ? customer.phone_number : null,
        fax: null,
      };
    }

    if (customer.gender) {
      gender = this.getGender(customer.gender);
    }

    if (customer.delivery_addres) {
      address = {
        city: customer.delivery_addres.city,
        country: customer.delivery_addres.country,
        zipCode: customer.delivery_addres.zip + '',
        street: customer.delivery_addres.street,
        streetNumber: customer.delivery_addres.streetNo + '',
        info: customer.delivery_addres.note ? customer.delivery_addres.note : null,
      };
    }

    if (customer.organisation) {
      organisation = {
        name: customer.organisation.name, // Firma
        nameSuffix: customer.organisation.extraAddress, // Addresszutsaiz
        department: customer.organisation.department, // abteilung
        vatId: customer.organisation.vatId, // USt-ID
      };
    }

    const createdInBranch = <EntityDTOContainerOfBranchDTO>{
      id: customer.createdInBranch,
      // TODO: what is the meaning of enabled attribute
      enabled: true,
    };

    return <CustomerDTO>{
      id: customer.id,
      firstName: customer.first_name,
      lastName: customer.last_name,
      title: customer.title ? customer.title : null,
      dateOfBirth: customer.date_of_birth ? customer.date_of_birth : null,
      customerNumber: customer.customerId ? customer.customerId : null,
      customerType: 8, // 8 => B2C customer, and the store app will have only B2C customers
      customerGroup: null,
      agentComment: null,
      statusChangeComment: null,
      deactivationComment: null,
      statusComment: null,
      label: null,
      user: null,
      campaignCode: null,
      bonusCard: null,
      isGuestAccount: null,
      features: null,
      attributes: null,
      createdInBranch: createdInBranch,
      customerStatus: 0, // Will this be ever different?
      gender: gender,
      fetchOnDeliveryNote: null,
      organisation: organisation,
      communicationDetails: communicationDetails,
      notificationChannels: notificationChannels,
    };
  }

  getNotificationChannels(c: User): NotificationChannels {
    if (!c) {
      return NotificationChannels.NotSet;
    }
    return c.notificationEmail && c.notificationSms
      ? NotificationChannels.EmailSms
      : c.notificationEmail
      ? NotificationChannels.Email
      : c.notificationSms
      ? NotificationChannels.Sms
      : NotificationChannels.NotSet;
  }

  fromOrganisationToOrganisationDTO(organisation: Organisation): OrganisationDTO {
    if (!organisation) {
      return {};
    }
    return {
      name: organisation.name,
      department: organisation.department,
      vatId: organisation.vatId,
    };
  }

  fromUserToCommunicationDetails(user: User): CommunicationDetailsDTO {
    return {
      phone: user.phone_number,
      mobile: user.mobile_number,
      email: user.email,
    };
  }

  fromUserToShippingAddressDTO(user: User): CheckoutShippingAddressDTO {
    const deliveryDetails = user.delivery_addres;

    if (!deliveryDetails) {
      throw Error('No Delivery Address set.');
    }

    return {
      title: deliveryDetails.title,
      gender: this.getGender(user.gender),
      firstName: deliveryDetails.first_name,
      lastName: deliveryDetails.last_name,
      organisation: this.fromOrganisationToOrganisationDTO(user.organisation),
      communicationDetails: this.fromUserToCommunicationDetails(user),
      address: this.fromAddressToAddressDTO(deliveryDetails),
      source: deliveryDetails.id,
    };
  }
}
