import { Api } from "./api";
import notification from "utils/notification";
import {
  CanDeletePaymentMethodSuccess,
  CheckoutInvoice,
  CheckoutInvoiceSuccess,
  CustomerWithDetails,
  CustomerWithDetailsSuccess,
  Interval,
  Product,
  ProductKey,
  ProductSuccess,
  SalesSetupConfirmationFailure,
  SalesSoftFail,
  SalesSuccess,
  StripeSetupIntentSuccess,
  Subscription,
  SubscriptionSuccess,
} from "types/sales";
import { Maybe } from "types/utilities";
import { CancelFeedback } from "@komi-app/components";

export interface SalesServiceInterface {
  getCustomerInfo(): Promise<Maybe<CustomerWithDetails>>;
  createSetup(): Promise<Maybe<string>>;
  confirmSetup(
    setup_id: string
  ): Promise<
    Maybe<SalesSuccess | SalesSetupConfirmationFailure | SalesSoftFail>
  >;
  getProduct(product_key: ProductKey): Promise<Maybe<Product>>;
  cancelSubscription(product_key: ProductKey): Promise<Maybe<boolean>>;
  checkoutSubscription(
    product_key: ProductKey,
    interval: Interval
  ): Promise<Maybe<CheckoutInvoice>>;
  cancelSubscriptionFeedback(
    product_key: ProductKey,
    feedback: CancelFeedback
  ): Promise<Maybe<boolean>>;
  createSubscription(
    product_key: ProductKey,
    interval: Interval
  ): Promise<Maybe<Subscription>>;
  updateSubscription(
    product_key: ProductKey,
    interval: Interval
  ): Promise<Maybe<Subscription>>;
  deletePaymentMethod(): Promise<any>;
  canDeletePaymentMethod(): Promise<Maybe<boolean>>;
  chargePayment(
    product_key: ProductKey
  ): Promise<Maybe<SalesSuccess | SalesSetupConfirmationFailure>>;
}

export default class SalesService implements SalesServiceInterface {
  constructor(private readonly api: Api) {
    this.api = api;
  }

  getCustomerInfo = async (): Promise<Maybe<CustomerWithDetails>> => {
    const res: any = await this.api.getCustomerInfo();

    if (!res.ok) return void notification.error({ message: res.message });

    return (res.response as CustomerWithDetailsSuccess).customer;
  }

  async createSetup(): Promise<Maybe<string>> {
    const res: any = await this.api.createSetup();

    if (!res.ok) return void notification.error({ message: res.message });

    return (res.response as StripeSetupIntentSuccess).client_secret;
  }

  async confirmSetup(
    setup_id: string
  ): Promise<
    Maybe<SalesSuccess | SalesSetupConfirmationFailure | SalesSoftFail>
  > {
    const res: any = await this.api.confirmSetup(setup_id);

    if (!res.ok) {
      const message = res?.response?.message || res.message;
      const value = {
        ...res.response,
        status: res.status,
      };

      // Don't show notification if it's 409 error
      if (res.status === 409) {
        return {
          success: null,
          status: res.status,
        } as SalesSoftFail;
      }
      notification.error({ message });

      return value as SalesSetupConfirmationFailure;
    }

    return res.response as SalesSuccess;
  }

  async chargePayment(product_key: ProductKey): Promise<Maybe<SalesSuccess>> {
    const res: any = await this.api.chargePayment(product_key);

    if (!res.ok) return void 0;

    return res.response as SalesSuccess;
  }

  async getProduct(product_key: ProductKey): Promise<Maybe<Product>> {
    const res: any = await this.api.getSalesProducts(product_key);

    if (!res.ok) return void notification.error({ message: res.message });

    return (res.response as ProductSuccess).product;
  }

  async cancelSubscription(product_key: ProductKey): Promise<Maybe<boolean>> {
    const res: any = await this.api.cancelSubscription(product_key);

    if (!res.ok) return void notification.error({ message: res.message });

    return (res.response as SalesSuccess).success;
  }

  async checkoutSubscription(
    product_key: ProductKey,
    interval: Interval
  ): Promise<Maybe<CheckoutInvoice>> {
    const res: any = await this.api.checkoutSubscription(product_key, interval);

    if (!res.ok) {
      //SB-274: Hiding the "no payment method found" error since it is meaningless to users
      if (res.status === 402) return void 0;

      return void notification.error({ message: res.message });
    }

    return (res.response as CheckoutInvoiceSuccess).checkout;
  }

  async cancelSubscriptionFeedback(
    product_key: ProductKey,
    feedback: CancelFeedback
  ): Promise<Maybe<boolean>> {
    const res: any = await this.api.cancelSubscriptionFeedback(
      product_key,
      feedback
    );

    if (!res.ok) return void notification.error({ message: res.message });

    return (res.response as SalesSuccess).success;
  }

  async createSubscription(
    product_key: ProductKey,
    interval: Interval
  ): Promise<Maybe<Subscription>> {
    const res: any = await this.api.createSubscription(product_key, interval);

    if (!res.ok) {
      //SB-274: Hiding the "no payment method found" error since it is meaningless to users
      if (res.status === 402) return void 0;

      return void notification.error({ message: res.message });
    }

    return (res.response as SubscriptionSuccess).subscription;
  }

  async updateSubscription(
    product_key: ProductKey,
    interval: Interval
  ): Promise<Maybe<Subscription>> {
    const res: any = await this.api.updateSubscription(product_key, interval);

    if (!res.ok) {
      //SB-274: Hiding the "no payment method found" error since it is meaningless to users
      if (res.status === 402) return void 0;

      return void notification.error({ message: res.message });
    }

    return (res.response as SubscriptionSuccess).subscription;
  }

  async deletePaymentMethod(): Promise<any> {
    const res: any = await this.api.deletePaymentMethod();

    if (!res.ok)
      return void notification.error({
        message: "There was an error deleting your payment method",
      });

    return (res.response as SalesSuccess).success;
  }

  async canDeletePaymentMethod(): Promise<Maybe<boolean>> {
    const res: any = await this.api.canDeletePaymentMethod();

    if (!res.ok) return void notification.error({ message: res.message });

    return (res.response as CanDeletePaymentMethodSuccess).canDelete;
  }
}
