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

type SubscriptionSource = 'apple' | 'google' | 'manual' | 'signup_bonus' | 'stripe';

interface UpsertSubscriptionInput {
  db: Kysely<DB>;
  user_id: string;
  platform: BillingPlatform;
  sourceOverride?: SubscriptionSource;
  productId: string;
  status: SubscriptionStatus;
  transactionId: string;
  originalTransactionId?: string;
  expiresAt?: Date;
}

export function mapPlatformToSource(platform: BillingPlatform): 'apple' | 'google' {
  return platform === 'ios' ? 'apple' : 'google';
}

export function isStoreSubscriptionSource(source?: string | null): boolean {
  return source === 'apple' || source === 'google';
}

export async function getActiveExternalPremium(
  db: Kysely<DB>,
  user_id: string,
  lockUser = false,
): Promise<{ premiumUntil: Date; premiumSource: string } | null> {
  let query = db
    .selectFrom('users')
    .select(['premium_until', 'premium_source'])
    .where('id', '=', user_id)
    .where('deleted_at', 'is', null);

  if (lockUser) {
    query = query.forUpdate();
  }

  const row = await query.executeTakeFirst();
  if (!row?.premium_until) {
    return null;
  }

  const premiumUntil = new Date(row.premium_until);
  const premiumSource = String(row.premium_source || '').trim();
  if (premiumUntil.getTime() > Date.now() && premiumSource && !isStoreSubscriptionSource(premiumSource)) {
    return { premiumUntil, premiumSource };
  }

  return null;
}

export async function upsertSubscription(input: UpsertSubscriptionInput): Promise<void> {
  const {
    db,
    user_id,
    platform,
    sourceOverride,
    productId,
    status,
    transactionId,
    originalTransactionId,
    expiresAt,
  } = input;

  const source = sourceOverride || mapPlatformToSource(platform);
  const plan = planFromProductId(productId);
  const activeExternalPremium = isStoreSubscriptionSource(source)
    ? await getActiveExternalPremium(db, user_id, true)
    : null;

  const existing = originalTransactionId
    ? await db
        .selectFrom('subscriptions')
        .select('id')
        .where('user_id', '=', user_id)
        .where('original_transaction_id', '=', originalTransactionId)
        .where('deleted_at', 'is', null)
        .orderBy('updated_at', 'desc')
        .limit(1)
        .executeTakeFirst()
    : await db
        .selectFrom('subscriptions')
        .select('id')
        .where('user_id', '=', user_id)
        .where('source', '=', source)
        .where('deleted_at', 'is', null)
        .orderBy('updated_at', 'desc')
        .limit(1)
        .executeTakeFirst();

  if (existing) {
    await db
      .updateTable('subscriptions')
      .set({
        plan,
        status,
        source,
        product_id: productId,
        latest_transaction_id: transactionId,
        original_transaction_id: originalTransactionId || null,
        current_period_start: (eb) => eb.fn.coalesce('current_period_start', eb.val(new Date())),
        current_period_end: expiresAt || null,
        expires_at: expiresAt || null,
        updated_at: new Date(),
      })
      .where('id', '=', existing.id)
      .execute();
  } else {
    await db
      .insertInto('subscriptions')
      .values({
        user_id,
        plan,
        status,
        source,
        product_id: productId,
        original_transaction_id: originalTransactionId || null,
        latest_transaction_id: transactionId,
        current_period_start: new Date(),
        current_period_end: expiresAt || null,
        started_at: new Date(),
        expires_at: expiresAt || null,
      })
      .execute();
  }

  if (activeExternalPremium) {
    return;
  }

  const premiumUntil = expiresAt || new Date();
  await db
    .updateTable('users')
    .set({
      premium_until: premiumUntil,
      premium_source: source,
    })
    .where('id', '=', user_id)
    .where('deleted_at', 'is', null)
    .execute();
}
