import { Platform, Dimensions, PixelRatio } from 'react-native';
import axios from 'axios';
import * as RNIap from 'react-native-iap';
import * as Application from 'expo-application';
import * as Device from 'expo-device';
import * as Localization from 'expo-localization';

export class GoMarketMeAffiliateMarketingData {
  campaign: Campaign;
  affiliate: Affiliate;
  saleDistribution: SaleDistribution;
  affiliateCampaignCode: string;
  deviceId: string;
  offerCode?: string;

  constructor(
    campaign: Campaign,
    affiliate: Affiliate,
    saleDistribution: SaleDistribution,
    affiliateCampaignCode: string,
    deviceId: string,
    offerCode?: string
  ) {
    this.campaign = campaign;
    this.affiliate = affiliate;
    this.saleDistribution = saleDistribution;
    this.affiliateCampaignCode = affiliateCampaignCode;
    this.deviceId = deviceId;
    this.offerCode = offerCode;
  }

  static fromJson(json: Record<string, any>): GoMarketMeAffiliateMarketingData | null {
    if (Object.keys(json).length === 0) {
      return null;
    }

    return new GoMarketMeAffiliateMarketingData(
      Campaign.fromJson(json.campaign),
      Affiliate.fromJson(json.affiliate),
      SaleDistribution.fromJson(json.sale_distribution),
      json.affiliate_campaign_code || '',
      json.device_id || '',
      json.offer_code
    );
  }
}

export class Campaign {
  id: string;
  name: string;
  status: string;
  type: string;
  publicLinkUrl?: string;

  constructor(id: string, name: string, status: string, type: string, publicLinkUrl?: string) {
    this.id = id;
    this.name = name;
    this.status = status;
    this.type = type;
    this.publicLinkUrl = publicLinkUrl;
  }

  static fromJson(json: Record<string, any>): Campaign {
    return new Campaign(
      json.id || '',
      json.name || '',
      json.status || '',
      json.type || '',
      json.public_link_url
    );
  }
}

export class Affiliate {
  id: string;
  firstName: string;
  lastName: string;
  countryCode: string;
  instagramAccount: string;
  tiktokAccount: string;
  xAccount: string;

  constructor(
    id: string,
    firstName: string,
    lastName: string,
    countryCode: string,
    instagramAccount: string,
    tiktokAccount: string,
    xAccount: string
  ) {
    this.id = id;
    this.firstName = firstName;
    this.lastName = lastName;
    this.countryCode = countryCode;
    this.instagramAccount = instagramAccount;
    this.tiktokAccount = tiktokAccount;
    this.xAccount = xAccount;
  }

  static fromJson(json: Record<string, any>): Affiliate {
    return new Affiliate(
      json.id || '',
      json.first_name || '',
      json.last_name || '',
      json.country_code || '',
      json.instagram_account || '',
      json.tiktok_account || '',
      json.x_account || ''
    );
  }
}

export class SaleDistribution {
  platformPercentage: string;
  affiliatePercentage: string;

  constructor(platformPercentage: string, affiliatePercentage: string) {
    this.platformPercentage = platformPercentage;
    this.affiliatePercentage = affiliatePercentage;
  }

  static fromJson(json: Record<string, any>): SaleDistribution {
    return new SaleDistribution(
      json.platform_percentage || '',
      json.affiliate_percentage || ''
    );
  }
}

class GoMarketMe {
  private static instance: GoMarketMe;
  private sdkType = 'ReactNativeExpo';
  private sdkVersion = '2.0.1';
  private sdkInitializedKey = 'GOMARKETME_SDK_INITIALIZED';
  private sdkInitializationUrl = 'https://4v9008q1a5.execute-api.us-west-2.amazonaws.com/prod/v1/sdk-initialization';
  private systemInfoUrl = 'https://4v9008q1a5.execute-api.us-west-2.amazonaws.com/prod/v1/mobile/system-info';
  private eventUrl = 'https://4v9008q1a5.execute-api.us-west-2.amazonaws.com/prod/v1/event';
  private _affiliateCampaignCode = '';
  private _deviceId = '';
  private _packageName = ''
  public affiliateMarketingData?: GoMarketMeAffiliateMarketingData | null;

  private constructor() { }

  public static getInstance(): GoMarketMe {
    if (!GoMarketMe.instance) {
      GoMarketMe.instance = new GoMarketMe();
    }
    return GoMarketMe.instance;
  }

  public async initialize(apiKey: string): Promise<void> {
    try {
      const isSDKInitialized = await this._isSDKInitialized();
      if (!isSDKInitialized) {
        await this._postSDKInitialization(apiKey);
      }
      this._packageName = Application.applicationId ?? '';
      const systemInfo = await this._getSystemInfo();
      this.affiliateMarketingData = await this._postSystemInfo(systemInfo, apiKey);
      const currPurchases = await RNIap.getPurchaseHistory();
      await this._fetchConsolidatedPurchases(currPurchases, apiKey)
      await this._addListener(apiKey);
    } catch (e) {
      console.log('Error initializing GoMarketMe:', e);
    }
  }

  private async _addListener(apiKey: string): Promise<void> {
    try {
      RNIap.initConnection().then(result => {
        RNIap.purchaseUpdatedListener(async (purchase: RNIap.Purchase) => {
          await this._fetchConsolidatedPurchases([purchase], apiKey);
        });
        RNIap.purchaseErrorListener((error) => {
          console.log('Purchase error:', error);
        });
      });
    } catch (e) {
      console.log('Error setting up IAP listeners:', e);
    }
  }

  private async _getSystemInfo(): Promise<any> {
    const deviceData = Platform.select({
      ios: await this._readIosDeviceInfo(),
      android: await this._readAndroidDeviceInfo(),
    });

    this._deviceId = deviceData['deviceId'];

    const devicePixelRatio = PixelRatio.get();
    const dimension = Dimensions.get('window');

    const windowData = {
      devicePixelRatio: devicePixelRatio,
      width: dimension.width * devicePixelRatio,
      height: dimension.height * devicePixelRatio,
    };

    return {
      device_info: deviceData,
      window_info: windowData,
      time_zone: this._getTimeZone(),
      language_code: this._getLanguageCode(),
    };
  }

  private async _postSDKInitialization(apiKey: string): Promise<void> {
    try {
      const response = await axios.post(this.sdkInitializationUrl, {}, {
        headers: {
          'Content-Type': 'application/json',
          'x-api-key': apiKey,
        },
      });
      if (response.status === 200) {
        await this._markSDKAsInitialized();
      } else {
        console.log('Failed to mark SDK as Initialized. Status code:', response.status);
      }
    } catch (e) {
      console.log('Error sending SDK information to server:', e);
    }
  }

  private async _postSystemInfo(data: any, apiKey: string): Promise<GoMarketMeAffiliateMarketingData | null> {
    let output: GoMarketMeAffiliateMarketingData | null = null;
    try {
      data['sdk_type'] = this.sdkType;
      data['sdk_version'] = this.sdkVersion;
      data['package_name'] = this._packageName;
      const response = await axios.post(this.systemInfoUrl, data, {
        headers: {
          'Content-Type': 'application/json',
          'x-api-key': apiKey,
        },
      });
      if (response.status === 200) {
        output = GoMarketMeAffiliateMarketingData.fromJson(response.data);
        if (output != null) {
          this._affiliateCampaignCode = output.affiliateCampaignCode;
        }
      } else {
        console.log('Failed to send system info. Status code:', response.status);
      }
    } catch (e) {
      console.log('Error sending system info to server:', e);
    }
    return output;
  }

  private _generateAndroidId = () => {
    const characters =
      "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";

    const getRandomString = (length: number) => {
      return Array.from({ length }, () =>
        characters[Math.floor(Math.random() * characters.length)]
      ).join('');
    };

    const part1 = getRandomString(4);
    const part2 = getRandomString(6);
    const part3 = getRandomString(3);

    return `${part1}.${part2}.${part3}`;
  };

  private async _readAndroidDeviceInfo(): Promise<any> {
    let androidId = Platform.OS === 'android' ? Application.getAndroidId() : '';
    let deviceId = this._generateAndroidId();
    let systemName = Device.osName;
    let systemVersion = Device.osVersion;
    let brand = Device.brand;
    let model = Device.modelName;
    let manufacturer = Device.manufacturer;
    let isEmulator = !Device.isDevice;

    return {
      deviceId: androidId,
      _deviceId: deviceId,
      _uniqueId: deviceId,
      systemName: systemName,
      systemVersion: systemVersion,
      brand: brand,
      model: model,
      manufacturer: manufacturer,
      isEmulator: isEmulator
    };
  }

  private async _readIosDeviceInfo(): Promise<any> {

    let deviceId = Platform.OS === 'ios' ? await Application.getIosIdForVendorAsync() : '';
    let systemName = Device.osName;
    let systemVersion = Device.osVersion;
    let brand = Device.brand;
    let model = Device.modelName;
    let manufacturer = Device.manufacturer;
    let isEmulator = !Device.isDevice;

    return {
      deviceId: deviceId,
      _deviceId: deviceId,
      systemName: systemName,
      systemVersion: systemVersion,
      brand: brand,
      model: model,
      manufacturer: manufacturer,
      isEmulator: isEmulator
    };
  }

  private _getTimeZone = (): string => {
    return Intl.DateTimeFormat().resolvedOptions().timeZone;
  };

  private _getLanguageCode(): string {
    return Localization.getLocales()[0].languageTag;
  }
  private async _fetchConsolidatedPurchases(purchaseDetailsList: RNIap.Purchase[], apiKey: string): Promise<void> {
    for (const purchase of purchaseDetailsList) {
      if (purchase.transactionReceipt) {
        var data = this._serializePurchaseDetails(purchase);
        data['products'] = []
        if (data.productID != '') {
          const products = await RNIap.getProducts({ skus: [data.productID] })
          if (products.length > 0) {
            for (const product0 of products) {
              data['products'].push(this._serializeProductDetails(product0))
            }
          }
          else {
            const products = await RNIap.getSubscriptions({ skus: [data.productID] });
            if (products.length > 0) {
              for (const product0 of products) {
                data['products'].push(this._serializeSubscriptionDetails(product0))
              }
            }
          }
        }
        await this._sendEventToServer(JSON.stringify(data), 'purchase', apiKey);
      }
    }
  }

  private async _sendEventToServer(body: string, eventType: string, apiKey: string): Promise<void> {
    try {
      const response = await axios.post(this.eventUrl, body, {
        headers: {
          'Content-Type': 'application/json',
          'x-affiliate-campaign-code': this._affiliateCampaignCode,
          'x-device-id': this._deviceId,
          'x-event-type': eventType,
          'x-product-type': Platform.OS,
          'x-source-name': Platform.OS === 'android' ? 'google_play' : 'app_store',
          'x-api-key': apiKey,
        },
      });
      if (response.status === 200) {
        console.log(`${eventType} sent successfully`);
      } else {
        console.log(`Failed to send ${eventType}. Status code:`, response.status);
      }
    } catch (e) {
      console.log(`Error sending ${eventType} to server:`, e);
    }
  }

  private _serializePurchaseDetails(purchase: RNIap.Purchase): any {
    return {
      packageName: this._packageName,
      productID: purchase.productId,
      purchaseID: purchase.transactionId || '',
      transactionDate: purchase.transactionDate || '',
      status: Platform.select({
        ios: (purchase as any).transactionStateIOS, // Removed non-existent properties
        android: (purchase as any).purchaseStateAndroid,
      }),
      verificationData: {
        localVerificationData: purchase.transactionReceipt,
        serverVerificationData: purchase.transactionReceipt,
        source: Platform.OS === 'android' ? 'google_play' : 'app_store',
      },
      pendingCompletePurchase: '',
      error: '',
      hashCode: '',
      _purchase: purchase
    };
  }

  private _serializeProductDetails(product: RNIap.Product): any {
    return {
      packageName: this._packageName,
      productID: product.productId,
      productTitle: product.title,
      productDescription: product.description,
      productPrice: product.localizedPrice,
      productRawPrice: product.price,
      productCurrencySymbol: product.localizedPrice.replace(product.price, ''),
      productCurrencyCode: product.currency,
      hashCode: '',
      _product: product
    };
  }

  private _serializeSubscriptionDetails(subscription: RNIap.Subscription): any {
    let output: any = {
      productID: subscription.productId,
      productTitle: subscription.title,
      productDescription: subscription.description,
      hashCode: '',
    };

    if (Platform.OS === 'ios') {
      const subscriptionIOS = subscription as RNIap.SubscriptionIOS;
      output.productPrice = subscriptionIOS.localizedPrice;
      output.productRawPrice = subscriptionIOS.price;
      output.productCurrencyCode = subscriptionIOS.currency;
      output._subscription = subscriptionIOS;
    } else if (Platform.OS === 'android') {
      const subscriptionAndroid = subscription as RNIap.SubscriptionAndroid;
      if (subscriptionAndroid.subscriptionOfferDetails?.length) {
        const offerDetails = subscriptionAndroid.subscriptionOfferDetails[0];
        const priceAmountMicros = parseInt(offerDetails.pricingPhases.pricingPhaseList[0].priceAmountMicros, 10) || 0;
        output.productPrice = offerDetails.pricingPhases.pricingPhaseList[0].formattedPrice;
        output.productRawPrice = String(priceAmountMicros / 1_000_000);
        output.productCurrencyCode = offerDetails.pricingPhases.pricingPhaseList[0].priceCurrencyCode;
      }
      output._subscription = subscriptionAndroid;
    }

    return output;
  }

  private async _markSDKAsInitialized(): Promise<boolean> {
    return true;
  }

  private async _isSDKInitialized(): Promise<boolean> {
    return false;
  }
}

export default GoMarketMe.getInstance();