import { DIALOG_DATA, DialogConfig, DialogRef } from "@angular/cdk/dialog";
import { Component, ElementRef, Inject, OnInit, ViewChild } from "@angular/core";
import { FormControl, FormGroup, Validators } from "@angular/forms";
import { firstValueFrom, map } from "rxjs";

import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { PaymentMethodType } from "@bitwarden/common/billing/enums";
import { BitPayInvoiceRequest } from "@bitwarden/common/billing/models/request/bit-pay-invoice.request";
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { DialogService } from "@bitwarden/components";

export interface AddCreditDialogData {
  organizationId: string;
}

export enum AddCreditDialogResult {
  Added = "added",
  Cancelled = "cancelled",
}

export type PayPalConfig = {
  businessId?: string;
  buttonAction?: string;
};

@Component({
  templateUrl: "add-credit-dialog.component.html",
})
export class AddCreditDialogComponent implements OnInit {
  @ViewChild("ppButtonForm", { read: ElementRef, static: true }) ppButtonFormRef: ElementRef;

  paymentMethodType = PaymentMethodType;
  ppButtonFormAction: string;
  ppButtonBusinessId: string;
  ppButtonCustomField: string;
  ppLoading = false;
  subject: string;
  returnUrl: string;
  organizationId: string;

  private userId: string;
  private name: string;
  private email: string;
  private region: string;

  protected DialogResult = AddCreditDialogResult;
  protected formGroup = new FormGroup({
    method: new FormControl(PaymentMethodType.PayPal),
    creditAmount: new FormControl(null, [Validators.required]),
  });

  constructor(
    private dialogRef: DialogRef,
    @Inject(DIALOG_DATA) protected data: AddCreditDialogData,
    private accountService: AccountService,
    private apiService: ApiService,
    private platformUtilsService: PlatformUtilsService,
    private organizationService: OrganizationService,
    private logService: LogService,
    private configService: ConfigService,
  ) {
    this.organizationId = data.organizationId;
    const payPalConfig = process.env.PAYPAL_CONFIG as PayPalConfig;
    this.ppButtonFormAction = payPalConfig.buttonAction;
    this.ppButtonBusinessId = payPalConfig.businessId;
  }

  async ngOnInit() {
    if (this.organizationId != null) {
      if (this.creditAmount == null) {
        this.creditAmount = "20.00";
      }
      this.ppButtonCustomField = "organization_id:" + this.organizationId;
      const org = await this.organizationService.get(this.organizationId);
      if (org != null) {
        this.subject = org.name;
        this.name = org.name;
      }
    } else {
      if (this.creditAmount == null) {
        this.creditAmount = "10.00";
      }
      const [userId, email] = await firstValueFrom(
        this.accountService.activeAccount$.pipe(map((a) => [a?.id, a?.email])),
      );
      this.userId = userId;
      this.subject = email;
      this.email = this.subject;
      this.ppButtonCustomField = "user_id:" + this.userId;
    }
    this.region = await firstValueFrom(this.configService.cloudRegion$);
    this.ppButtonCustomField += ",account_credit:1";
    this.ppButtonCustomField += `,region:${this.region}`;
    this.returnUrl = window.location.href;
  }

  get creditAmount() {
    return this.formGroup.value.creditAmount;
  }
  set creditAmount(value: string) {
    this.formGroup.get("creditAmount").setValue(value);
  }

  get method() {
    return this.formGroup.value.method;
  }

  submit = async () => {
    if (this.creditAmount == null || this.creditAmount === "") {
      return;
    }

    if (this.method === PaymentMethodType.PayPal) {
      this.ppButtonFormRef.nativeElement.submit();
      this.ppLoading = true;
      return;
    }
    if (this.method === PaymentMethodType.BitPay) {
      const req = new BitPayInvoiceRequest();
      req.email = this.email;
      req.name = this.name;
      req.credit = true;
      req.amount = this.creditAmountNumber;
      req.organizationId = this.organizationId;
      req.userId = this.userId;
      req.returnUrl = this.returnUrl;
      const bitPayUrl: string = await this.apiService.postBitPayInvoice(req);
      this.platformUtilsService.launchUri(bitPayUrl);
      return;
    }
    this.dialogRef.close(AddCreditDialogResult.Added);
  };

  formatAmount() {
    try {
      if (this.creditAmount != null && this.creditAmount !== "") {
        const floatAmount = Math.abs(parseFloat(this.creditAmount));
        if (floatAmount > 0) {
          this.creditAmount = parseFloat((Math.round(floatAmount * 100) / 100).toString())
            .toFixed(2)
            .toString();
          return;
        }
      }
    } catch (e) {
      this.logService.error(e);
    }
    this.creditAmount = "";
  }

  get creditAmountNumber(): number {
    if (this.creditAmount != null && this.creditAmount !== "") {
      try {
        return parseFloat(this.creditAmount);
      } catch (e) {
        this.logService.error(e);
      }
    }
    return null;
  }
}

/**
 * Strongly typed helper to open a AddCreditDialog
 * @param dialogService Instance of the dialog service that will be used to open the dialog
 * @param config Configuration for the dialog
 */
export function openAddCreditDialog(
  dialogService: DialogService,
  config: DialogConfig<AddCreditDialogData>,
) {
  return dialogService.open<AddCreditDialogResult>(AddCreditDialogComponent, config);
}
