import { ChangeDetectorRef, Component, Input, OnDestroy, OnInit } from '@angular/core';
import { HttpErrorResponse } from '@angular/common/http';
import { Subscription } from 'rxjs';

import { AccountInfoService } from 'src/app/services/accountInfo/account-info.service';
import { CartService } from 'src/app/services/cart/cart.service';
import { OrdersService } from 'src/app/services/orders/orders.service';
import { environment } from 'src/environments/environment';
import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap';
import { ModalConfig } from 'src/app/interfaces/common-interfaces';
import { StripePaymentModelComponent } from 'src/app/components/stripe-payment-model/stripe-payment-model.component';
import { UpdatePaymentMethodMsgModalComponent } from '../update-payment-method-msg-modal/update-payment-method-msg-modal.component';

@Component({
  standalone: false,
  selector: 'app-change-payment-method-modal',
  templateUrl: './change-payment-method-modal.component.html',
  styleUrls: ['./change-payment-method-modal.component.scss']
})
export class ChangePaymentMethodModalComponent implements OnInit, OnDestroy {
  @Input() order!: any;
  @Input() changeMethodModalClose!: () => void;
  imgUrl: string = environment.imageUrl;
  subscriptions: Subscription[] = [];

  originalPaymentMethodId!: string;
  originalPaymentMethodName!: string;
  originalSurchargeAmt = 0;
  isCollapsable!:boolean;

  selectedPaymentMethodId!: string;
  selectedPaymentMethodName!: string;
  selectedSurchargeAmt = 0;

  orderSubtotal = 0;
  showRecalculation = false;
  isSubmitting = false;

  confirmationState: 'none' | 'success' | 'error' = 'none';
  confirmationMessage = '';
  confirmationHeader = '';
  confirmationAmount = '';
  confirmationMethod = '';
  confirmationDate = '';

  stripePaymentModel!: NgbModalRef;

  constructor(
    private orders: OrdersService,
    private modalService: NgbModal,
    private cartService: CartService,
    private accountInfoService: AccountInfoService,
    private cdr: ChangeDetectorRef
  ) {}

    /**
     * Initializes the component state from the current order details.
     *
     * @returns void
     */
  ngOnInit(): void {
    this.orderSubtotal = this.setOrderTotal(this.order.items);
    this.originalPaymentMethodId = String(this.order.stripe_payment_method_id || '');
    this.originalPaymentMethodName = this.order.payment_method;
    this.originalSurchargeAmt = Number(this.order.surcharge_amount || 0);

    this.selectedPaymentMethodId = this.originalPaymentMethodId;
    this.selectedPaymentMethodName = this.originalPaymentMethodName;
    this.selectedSurchargeAmt = this.originalSurchargeAmt;
    this.initializeSelectedPaymentMethod();

    this.enrichPaymentMethods();
  }

  /**
   * Calculates the subtotal for active order items.
   *
   * @param {any[]} items
   * @returns {number}
   */
  setOrderTotal(items: any[]): number {
    let total = 0;
    const excludeStatusArr = [3, 21];
    items.forEach((item: any) => {
      if (excludeStatusArr.indexOf(item.flag_status) === -1) {
        total += item.bid_amount * item.quantity_offered;
      }
    });
    return total;
  }

  /**
   * Returns the payment methods that are allowed for the order.
   *
   * @returns {any[]}
   */
  get acceptedPaymentMethods(): any[] {
    const methods = this.order.payment_methods || [];
    return methods.filter((method: any) => {
      if (this.order.payment_type_cc && this.order.payment_type_ach) {
        return true;
      }
      if (this.order.payment_type_cc && !this.order.payment_type_ach) {
        return method.type === 'card';
      }
      if (!this.order.payment_type_cc && this.order.payment_type_ach) {
        return method.type === 'bank';
      }
      return false;
    });
  }

  /**
   * Builds the dropdown payment methods list, including the current method when needed.
   *
   * @returns {any[]}
   */
  get dropdownPaymentMethods(): any[] {
    const acceptedMethods = this.acceptedPaymentMethods;
    const currentMethod = this.getCurrentOrderPaymentMethod();
    if (!currentMethod) {
      return acceptedMethods;
    }

    const currentMethodInAccepted = acceptedMethods.some((method: any) => this.paymentMethodIdMatches(method.id, currentMethod.id));
    if (currentMethodInAccepted) {
      return acceptedMethods;
    }

    return [currentMethod, ...acceptedMethods];
  }

  /**
   * Returns the currently selected payment method object.
   *
   * @returns {any}
   */
  get selectedMethodObj(): any {
    return this.dropdownPaymentMethods.find((method: any) => this.paymentMethodIdMatches(method.id, this.selectedPaymentMethodId));
  }

  /**
   * Returns the original order total including fees and surcharge.
   *
   * @returns {number}
   */
  get originalOrderTotal(): number {
    return this.orderSubtotal + Number(this.order.ship_fee || 0) + Number(this.order.additional_charge_amt || 0) + this.originalSurchargeAmt;
  }

  /**
   * Returns the updated order total including fees and the selected surcharge.
   *
   * @returns {number}
   */
  get updatedOrderTotal(): number {
    return this.orderSubtotal + Number(this.order.ship_fee || 0) + Number(this.order.additional_charge_amt || 0) + this.selectedSurchargeAmt;
  }

  /**
   * Returns the surcharge delta between the original and selected methods.
   *
   * @returns {number}
   */
  get surchargeDelta(): number {
    return Number((this.selectedSurchargeAmt - this.originalSurchargeAmt).toFixed(2));
  }

  /**
   * Indicates whether the surcharge amount changed.
   *
   * @returns {boolean}
   */
  get hasSurchargeChange(): boolean {
    return Math.abs(this.surchargeDelta) > 0;
  }

  /**
   * Indicates whether the selected payment method differs from the original one.
   *
   * @returns {boolean}
   */
  get isMethodChanged(): boolean {
    return !this.paymentMethodIdMatches(this.selectedPaymentMethodId, this.originalPaymentMethodId);
  }

  /**
   * Controls whether the surcharge notice should be shown for the selected method.
   *
   * @returns {boolean}
   */
  get showCcsNoticeLine(): boolean {
    const isCard = this.selectedMethodObj?.type === 'card';
    const supplierSurcharges = this.originalSurchargeAmt > 0 || this.selectedSurchargeAmt > 0;
    return isCard && supplierSurcharges;
  }
  
  /**
   * Returns the dropdown label for a payment method.
   *
   * @param {any} method
   * @returns {string}
   */
  getDefaultLabel(method: any): string {
    const isCurrent = this.paymentMethodIdMatches(method.id, this.originalPaymentMethodId);
    return `${method.name}${isCurrent ? ' (current)' : ''}`;
  }

  /**
   * Compares payment method ids after coercing them to strings.
   *
   * @param {any} methodIdA
   * @param {any} methodIdB
   * @returns {boolean}
   */
  private paymentMethodIdMatches(methodIdA: any, methodIdB: any): boolean {
    return String(methodIdA ?? '') === String(methodIdB ?? '');
  }

  /**
   * Returns the payment method currently attached to the order.
   *
   * @returns {any}
   */
  private getCurrentOrderPaymentMethod(): any {
    const methods = this.order?.payment_methods || [];
    return methods.find((method: any) => this.paymentMethodIdMatches(method.id, this.originalPaymentMethodId));
  }

  /**
   * Initializes the selected payment method with the current order method or the first available option.
   *
   * @returns void
   */
  private initializeSelectedPaymentMethod(): void {
    const methods = this.dropdownPaymentMethods;
    if (!methods.length) {
      this.selectedPaymentMethodId = '';
      return;
    }

    const currentMethod = methods.find((method: any) => this.paymentMethodIdMatches(method.id, this.originalPaymentMethodId));
    const selectedMethod = currentMethod || methods[0];

    this.selectedPaymentMethodId = String(selectedMethod.id || '');
    this.selectedPaymentMethodName = selectedMethod.name || this.originalPaymentMethodName;
  }

  /**
   * Returns the label for a payment method option including status flags.
   *
   * @param {any} method
   * @returns {string}
   */
  getPaymentMethodOptionLabel(method: any): string {
    const baseLabel = this.getDefaultLabel(method);
    if (this.isMethodNotVerified(method)) {
      return `${baseLabel} - not verified`;
    }
    if (this.isMethodExpired(method)) {
      return `${baseLabel} - expired`;
    }
    return baseLabel;
  }

  /**
   * Indicates whether a payment method should be disabled in the selector.
   *
   * @param {any} method
   * @returns {boolean}
   */
  isMethodDisabled(method: any): boolean {
    return this.isMethodNotVerified(method) || this.isMethodExpired(method) || method?.disabled === true;
  }

  /**
   * Indicates whether a payment method is not verified.
   *
   * @param {any} method
   * @returns {boolean}
   */
  private isMethodNotVerified(method: any): boolean {
    if (method?.is_verified === 0 || method?.verified === false || method?.not_verified === true) {
      return true;
    }
    const status = String(method?.verification_status || method?.status || '').toLowerCase();
    return ['not_verified', 'unverified', 'pending', 'requires_action', 'verification_pending'].includes(status);
  }

  /**
   * Indicates whether a payment method is expired.
   *
   * @param {any} method
   * @returns {boolean}
   */
  private isMethodExpired(method: any): boolean {
    if (method?.is_expired === 1 || method?.expired === true) {
      return true;
    }
    const status = String(method?.status || '').toLowerCase();
    if (status === 'expired') {
      return true;
    }

    const expMonth = Number(method?.exp_month || method?.expMonth || 0);
    const expYear = Number(method?.exp_year || method?.expYear || 0);
    if (!expMonth || !expYear) {
      return false;
    }

    const now = new Date();
    const nowMonth = now.getMonth() + 1;
    const nowYear = now.getFullYear();
    return expYear < nowYear || (expYear === nowYear && expMonth < nowMonth);
  }

  /**
   * Enriches order payment methods with card and bank verification metadata.
   *
   * @returns void
   */
  private enrichPaymentMethods(): void {
    const enrichSub = this.accountInfoService.getAllPaymentMethods()
      .subscribe({
        next: (res: any) => {
          const cards = res?.data?.cards || [];
          const pendingBanks = res?.data?.pendingBankPayMethods || [];

          if (!Array.isArray(this.order?.payment_methods)) {
            return;
          }

          const cardsById: {[key: string]: any} = {};
          cards.forEach((card: any) => {
            cardsById[card.stripe_payment_id] = card;
          });

          const pendingById: {[key: string]: any} = {};
          pendingBanks.forEach((bank: any) => {
            pendingById[bank.stripe_payment_id] = bank;
          });

          this.order.payment_methods = this.order.payment_methods.map((method: any) => {
            const cardMeta = cardsById[method.id] || {};
            const pendingMeta = pendingById[method.id] || null;
            return {
              ...method,
              exp_month: cardMeta.exp_month,
              exp_year: cardMeta.exp_year,
              not_verified: !!pendingMeta
            };
          });

          this.initializeSelectedPaymentMethod();
        },
        error: () => {
          // Ignore enrichment failures; dropdown still works with base payment method data.
        }
      });

    this.subscriptions.push(enrichSub);
  }

  /**
   * Handles a payment method selection change.
   *
   * @param {any} paymentMethodId
   * @returns void
   */
  onMethodChange(paymentMethodId: any): void {
    if (!paymentMethodId) {
      this.showStripePaymentModel();
      return;
    }

    this.selectedPaymentMethodId = String(paymentMethodId);
    const selectedOption = this.dropdownPaymentMethods.find((method: any) => this.paymentMethodIdMatches(method.id, paymentMethodId));
    this.selectedPaymentMethodName = selectedOption ? selectedOption.name : this.originalPaymentMethodName;
    this.showRecalculation = false;

    this.getOrderSurchargeAmt(String(paymentMethodId));
  }

  /**
   * Retrieves the surcharge amount for the selected payment method.
   *
   * @param {string} paymentMethodId
   * @returns void
   */
  getOrderSurchargeAmt(paymentMethodId: string): void {
    const surchargeSub = this.orders.getPaymentMethodSurcharge(this.order.order_id, paymentMethodId)
      .subscribe({
        next: (res: any) => {
          if (!res.data.error_msg) {
            this.selectedSurchargeAmt = Number(res.data.surcharge_amount || 0);
          }
        },
        error: () => {
          this.selectedSurchargeAmt = this.originalSurchargeAmt;
        }
      });

    this.subscriptions.push(surchargeSub);
  }

  /**
   * Starts the update flow for the selected payment method.
   *
   * @returns void
   */
  onUpdateMethodClick(): void {
    if (!this.isMethodChanged) {
      return;
    }

    const surchargeSub = this.orders.getPaymentMethodSurcharge(this.order.order_id, this.selectedPaymentMethodId)
      .subscribe({
        next: (res: any) => {
          if (!res.data.error_msg) {
            this.selectedSurchargeAmt = Number(res.data.surcharge_amount || 0);
          }

          if (!this.hasSurchargeChange) {
            this.submitUpdatedMethod();
            return;
          }
          this.showRecalculation = true;
        },
        error: () => {
          this.selectedSurchargeAmt = this.originalSurchargeAmt;
          this.submitUpdatedMethod();
        }
      });

    this.subscriptions.push(surchargeSub);
  }

  /**
   * Closes the modal without changing the payment method.
   *
   * @returns void
   */
  onKeepCurrentMethod(): void {
    this.changeMethodModalClose();
  }

  /**
   * Confirms the newly selected payment method.
   *
   * @returns void
   */
  onConfirmNewMethod(): void {
    if (!this.isMethodChanged) {
      return;
    }
    this.submitUpdatedMethod();
  }

  /**
   * Submits the updated payment method for the scheduled order.
   *
   * @returns void
   */
  submitUpdatedMethod(): void {
    this.isSubmitting = true;
    const updateSub = this.orders.updateScheduledPaymentMethod(this.order.order_id, this.selectedPaymentMethodId)
      .subscribe({
        next: (res: any) => {
          this.isSubmitting = false;
          if (res?.data?.success) {
            this.order.payment_method = this.selectedPaymentMethodName;
            this.order.stripe_payment_method_id = this.selectedPaymentMethodId;
            this.order.surcharge_amount = Number(res.data.order_info?.surcharge_amount ?? this.selectedSurchargeAmt);
            this.order.stripe_payment_type = res.data.order_info?.payment_type || this.order.stripe_payment_type;

            this.confirmationState = 'success';
            this.confirmationHeader = 'Payment method updated';
            this.confirmationMessage = `Your payment method has been updated successfully. Your order will be charged ${this.updatedOrderTotal.toLocaleString('en-US', { style: 'currency', currency: 'USD' })} to your ${this.selectedPaymentMethodName} on ${this.order.payment_due_date}`;
            this.confirmationAmount = this.updatedOrderTotal.toLocaleString('en-US', { style: 'currency', currency: 'USD' });
            this.confirmationMethod = this.selectedPaymentMethodName;
            this.confirmationDate = this.order.payment_due_date;
          } else {
            this.confirmationState = 'error';
            this.confirmationHeader = 'Payment Method update not successful';
            this.confirmationMessage = 'We were unable to update your payment method. Please try again or contact support if the issue persists.';
          }
          this.cdr.detectChanges();
          this.changeMethodModalClose();
          this.displayPaymentMsgModal();
        },
        error: () => {
          this.isSubmitting = false;
          this.confirmationState = 'error';
          this.confirmationHeader = 'Payment Method update not successful';
          this.confirmationMessage = 'We were unable to update your payment method. Please try again or contact support if the issue persists.';
          this.changeMethodModalClose();
          this.displayPaymentMsgModal();
        }
      });

    this.subscriptions.push(updateSub);
  }

  /**
   * Closes the confirmation modal state.
   *
   * @returns void
   */
  closeConfirmation(): void {
    this.changeMethodModalClose();
  }

  /**
   * Refreshes the available payment methods after a payment method change.
   *
   * @returns void
   */
  refreshPaymentMethods(): void {
    const refreshSub = this.accountInfoService.getStripePayMethods()
      .subscribe({
        next: (res: any) => {
          if (res?.data && res.data.length > 0) {
            this.order.payment_methods = res.data;
            this.enrichPaymentMethods();

            this.initializeSelectedPaymentMethod();
            if (this.selectedPaymentMethodId) {
              this.getOrderSurchargeAmt(String(this.selectedPaymentMethodId));
            }
          }
        },
        error: (err: any) => {
          this.cartService.errorCallBack(err);
        }
      });

    this.subscriptions.push(refreshSub);
  }

  /**
   * Opens the Stripe payment modal to add or manage payment methods.
   *
   * @returns void
   */
  showStripePaymentModel(): void {
    const methodsSub = this.accountInfoService
      .getAllPaymentMethods()
      .subscribe({
        next: (res: any) => {
          if (res.data !== false) {
            this.stripePaymentModel = this.modalService.open(StripePaymentModelComponent, {size: 'sm'});
            this.stripePaymentModel.componentInstance.stripePaymentModel = this.stripePaymentModel;
            this.stripePaymentModel.componentInstance.stripePaymentModelNo = this.stripePaymentModelNo.bind(this);
            this.stripePaymentModel.componentInstance.paymentMethodCards = res.data?.cards || [];
            this.stripePaymentModel.componentInstance.paymentMethodBank = res.data?.bankAccounts || [];
            this.stripePaymentModel.componentInstance.pendingBankPymentMethods = res.data?.pendingBankPayMethods || [];
            this.stripePaymentModel.componentInstance.isFromCart = false;
            this.stripePaymentModel.componentInstance.addPayMethodStatus.subscribe((data: any) => {
              if (data.status) {
                this.refreshPaymentMethods();
              }
            });
            this.stripePaymentModel.componentInstance.deletePaymentMethod.subscribe(() => {
              this.refreshPaymentMethods();
            });
          }
        },
        error: (err: HttpErrorResponse) => {
          this.accountInfoService.errorCallBack(err);
        }
      });

    this.subscriptions.push(methodsSub);
  }

  /**
   * Dismisses the Stripe payment modal.
   *
   * @returns void
   */
  stripePaymentModelNo(): void {
    this.stripePaymentModel.dismiss();
  }
  
    /**
    * Base modal configuration for the payment status message modal.
    */
    paymentMsgModalConfig:ModalConfig = {
     showHeader: false,
     title: ""
    };

    /**
    * Display payment status message Modal popup
    * @returns void
    */
    paymentMsgModalSub!:NgbModalRef;
    displayPaymentMsgModal() {
      this.paymentMsgModalConfig.title = "";
      this.paymentMsgModalSub = this.modalService.open(UpdatePaymentMethodMsgModalComponent, {size: 'sm'});
      this.paymentMsgModalSub.componentInstance.confirmationState = this.confirmationState;
      this.paymentMsgModalSub.componentInstance.confirmationHeader = this.confirmationHeader;
      this.paymentMsgModalSub.componentInstance.confirmationMessage = this.confirmationMessage;
      this.paymentMsgModalSub.componentInstance.confirmationAmount = this.confirmationAmount;
      this.paymentMsgModalSub.componentInstance.confirmationMethod = this.confirmationMethod;
      this.paymentMsgModalSub.componentInstance.confirmationDate = this.confirmationDate; 
      this.paymentMsgModalSub.componentInstance.paymentMsgModalClose = () => {
        this.paymentMsgModalClose();
      };
    };

    /**
     * Closes the payment status message modal.
     *
     * @returns void
    */
    paymentMsgModalClose() {
       this.paymentMsgModalSub.close('ok');
    };

  /**
   * Unsubscribes from active subscriptions when the component is destroyed.
   *
   * @returns void
   */
  ngOnDestroy(): void {
    this.subscriptions.forEach((sub: Subscription) => sub?.unsubscribe());
  }
}
