import { Kysely } from 'kysely';
import { DB } from '@/database/database.type';
import { BillingPlatform, PurchaseEventType } from './billing.types';

interface LogPurchaseEventInput {
  db: Kysely<DB>;
  user_id: string;
  platform: BillingPlatform;
  eventType: PurchaseEventType;
  productId?: string;
  transactionId?: string;
  originalTransactionId?: string;
  expiresAt?: Date;
  storePayload?: Record<string, unknown>;
}

function asRecord(value: unknown): Record<string, any> {
  if (value && typeof value === 'object' && !Array.isArray(value)) {
    return value as Record<string, any>;
  }
  return {};
}

function toNullableString(value: unknown): string | null {
  if (value === undefined || value === null) return null;
  const parsed = String(value).trim();
  return parsed ? parsed : null;
}

function toNullableNumber(value: unknown): number | null {
  if (value === undefined || value === null || value === '') return null;
  const parsed = Number(value);
  return Number.isFinite(parsed) ? parsed : null;
}

function readFirst(source: Record<string, any>, keys: string[]): unknown {
  for (const key of keys) {
    if (source[key] !== undefined && source[key] !== null) {
      return source[key];
    }
  }
  return undefined;
}

export async function logPurchaseEvent(input: LogPurchaseEventInput): Promise<void> {
  const {
    db,
    user_id,
    platform,
    eventType,
    productId,
    transactionId,
    originalTransactionId,
    expiresAt,
    storePayload = {},
  } = input;
  const root = asRecord(storePayload);
  const payload = asRecord(root.payload);
  const source = Object.keys(payload).length > 0 ? payload : root;

  const environment = toNullableString(readFirst(source, ['environmentIOS', 'environment'])) || 'unknown';
  const transactionReason = toNullableString(
    readFirst(source, ['transactionReasonIOS', 'reasonIOS', 'transactionReason', 'reason']),
  );
  const purchaseDateMs = toNullableNumber(
    readFirst(source, ['transactionDate', 'purchaseDate', 'purchaseTimeMillis']),
  );
  const expiresDateMs =
    toNullableNumber(
      readFirst(source, [
        'expirationDateIOS',
        'expiresDateIOS',
        'expiresDateMs',
        'expiryTimeMillis',
        'expirationDate',
      ]),
    ) || (expiresAt ? expiresAt.getTime() : null);
  const originalPurchaseDateMs = toNullableNumber(
    readFirst(source, ['originalTransactionDateIOS', 'originalPurchaseDate']),
  );
  const webOrderLineItemId = toNullableString(
    readFirst(source, ['webOrderLineItemIdIOS', 'webOrderLineItemId']),
  );
  const subscriptionGroupId = toNullableString(
    readFirst(source, ['subscriptionGroupIdIOS', 'subscriptionGroupId']),
  );
  const ownershipType = toNullableString(readFirst(source, ['ownershipTypeIOS', 'ownershipType']));
  const countryCode = toNullableString(readFirst(source, ['countryCodeIOS', 'countryCode']));
  const storefrontCountryCode = toNullableString(
    readFirst(source, ['storefrontCountryCodeIOS', 'storefrontCountryCode']),
  );
  const appBundleId = toNullableString(readFirst(source, ['appBundleIdIOS', 'appBundleId', 'packageName']));

  await db
    .insertInto('purchase_events')
    .values({
      user_id,
      platform,
      environment,
      event_type: eventType,
      transaction_id: transactionId || null,
      original_transaction_id: originalTransactionId || null,
      product_id: productId || null,
      expires_at: expiresAt || null,
      transaction_reason: transactionReason,
      purchase_date_ms: purchaseDateMs ? String(purchaseDateMs) : null,
      expires_date_ms: expiresDateMs ? String(expiresDateMs) : null,
      original_purchase_date_ms: originalPurchaseDateMs ? String(originalPurchaseDateMs) : null,
      web_order_line_item_id: webOrderLineItemId,
      subscription_group_id: subscriptionGroupId,
      ownership_type: ownershipType,
      country_code: countryCode,
      storefront_country_code: storefrontCountryCode,
      app_bundle_id: appBundleId,
      store_payload: JSON.stringify(storePayload),
    })
    .onConflict((oc) =>
      oc
        .columns(['platform', 'environment', 'transaction_id'])
        .where('transaction_id', 'is not', null)
        .where('deleted_at', 'is', null)
        .doNothing(),
    )
    .execute();
}
