import { Injectable, BadRequestException } from '@nestjs/common';
import { DataSource, EntityManager } from 'typeorm';
import { UsageRecord } from '../feature_usage/entities/usage-record.entity';

@Injectable()
export class WalletService {
  constructor(private dataSource: DataSource) {}

  /**
   * Ensures a wallet exists for the user.
   * Can be called during user registration or lazily.
   */
  async createWalletIfNotExists(userId: number, manager?: EntityManager) {
    const execute = async (em: EntityManager) => {
      const exists = await em.query('SELECT 1 FROM wallets WHERE user_id = ?', [
        userId,
      ]);
      if (exists.length === 0) {
        await em.query('INSERT INTO wallets (user_id) VALUES (?)', [userId]);
      }
    };

    // Use provided manager or default
    if (manager) await execute(manager);
    else await this.dataSource.transaction(execute);
  }

  /**
   * Safe Debit Function with Locking.
   * MUST be called within a transaction if chaining with other logic.
   */
  async debitWallet(
    userId: number,
    creditType: 'job' | 'database' | 'ai',
    amount: number,
    reason: string,
    refType: string | null = null,
    refId: number | null = null,
    externalManager?: EntityManager, // Allow participating in larger transactions
  ) {
    const runInTransaction = async (manager: EntityManager) => {
      // 1. LOCK the wallet row to prevent concurrent modifications
      const [wallet] = await manager.query(
        'SELECT * FROM wallets WHERE user_id = ? FOR UPDATE',
        [userId],
      );

      if (!wallet) {
        // If wallet strictly doesn't exist, we can't debit.
        throw new BadRequestException('Wallet not found for user');
      }

      // 2. Validate Balance
      const currentBalance = Number(wallet[`${creditType}_credits`] || 0);
      if (currentBalance < amount) {
        throw new BadRequestException(
          `Insufficient ${creditType} credits. Required: ${amount}, Available: ${currentBalance}`,
        );
      }

      const newBalance = currentBalance - amount;

      // 3. Deduct from Wallet
      await manager.query(
        `UPDATE wallets SET ${creditType}_credits = ? WHERE user_id = ?`,
        [newBalance, userId],
      );

      // 4. Update Ledger (Immutable Record)
      await manager.query(
        `INSERT INTO wallet_transactions 
        (wallet_id, type, credit_type, amount, reason, reference_type, reference_id, balance_after)
        VALUES (?, 'debit', ?, ?, ?, ?, ?, ?)`,
        [wallet.id, creditType, amount, reason, refType, refId, newBalance],
      );
    };

    if (externalManager) {
      await runInTransaction(externalManager);
    } else {
      await this.dataSource.transaction(runInTransaction);
    }
  }

  /**
   * Safe Credit Function (e.g. after payment).
   */
  async creditWallet(
    userId: number,
    creditType: 'job' | 'database' | 'ai',
    amount: number,
    reason: string,
    refType: string,
    refId: number,
    externalManager?: EntityManager,
    expiresAt?: Date, // Optional Expiry
  ) {
    console.log('💳 [WALLET] creditWallet called with:', {
      userId,
      creditType,
      amount,
      reason,
      refType,
      refId,
      expiresAt: expiresAt ? expiresAt.toISOString() : 'no expiry',
      hasExternalManager: !!externalManager,
    });

    const runInTransaction = async (manager: EntityManager) => {
      try {
        console.log('💳 [WALLET] Transaction started for user:', userId);

        // 1. Lock
        let [wallet] = await manager.query(
          'SELECT * FROM wallets WHERE user_id = ? FOR UPDATE',
          [userId],
        );
        console.log('💳 [WALLET] Wallet fetched (after lock):', wallet);

        if (!wallet) {
          console.log('💳 [WALLET] Wallet not found, creating new one...');
          // Atomic Create if not exists (Lazy)
          await manager.query('INSERT INTO wallets (user_id) VALUES (?)', [
            userId,
          ]);
          [wallet] = await manager.query(
            'SELECT * FROM wallets WHERE user_id = ? FOR UPDATE',
            [userId],
          );
          console.log('💳 [WALLET] New wallet created:', wallet);
        }

        const currentBalance = Number(wallet[`${creditType}_credits`] || 0);
        const newBalance = currentBalance + amount;
        console.log(
          '💳 [WALLET] Balance update:',
          currentBalance,
          '→',
          newBalance,
        );

        // 2. Update Balance
        await manager.query(
          `UPDATE wallets SET ${creditType}_credits = ? WHERE user_id = ?`,
          [newBalance, userId],
        );
        console.log('💳 [WALLET] Balance updated in database');

        // 3. Ledger
        const transactionData = [
          wallet.id,
          creditType,
          amount,
          reason,
          refType,
          refId,
          newBalance,
          expiresAt || null,
        ];
        console.log('💳 [WALLET] Creating transaction record:', {
          wallet_id: transactionData[0],
          credit_type: transactionData[1],
          amount: transactionData[2],
          reason: transactionData[3],
          reference_type: transactionData[4],
          reference_id: transactionData[5],
          balance_after: transactionData[6],
          expires_at: transactionData[7],
        });

        await manager.query(
          `INSERT INTO wallet_transactions 
          (wallet_id, type, credit_type, amount, reason, reference_type, reference_id, balance_after, expires_at)
          VALUES (?, 'credit', ?, ?, ?, ?, ?, ?, ?)`,
          transactionData,
        );
        console.log('✅ [WALLET] Transaction record created successfully');
      } catch (error) {
        console.error('❌ [WALLET] Error in transaction:', error);
        console.error('❌ [WALLET] Error message:', error.message);
        throw error;
      }
    };

    if (externalManager) {
      console.log('💳 [WALLET] Using external manager for transaction');
      await runInTransaction(externalManager);
    } else {
      console.log('💳 [WALLET] Creating new transaction via dataSource');
      await this.dataSource.transaction(runInTransaction);
    }
    console.log('✅ [WALLET] creditWallet completed successfully');
  }

  /**
   * 🔴 PRODUCTION-READY: Deduct Job Credits with Full Transaction & Rollback
   */
  async deductJobCreditsForPackage(userId: number, packageCode: string) {
    // 🔴 LAZY EXPIRY CHECK (Before transaction)
    await this.checkAndProcessExpiry(userId);

    // 🔴 CRITICAL: Start database transaction using QueryRunner for granular control
    const queryRunner = this.dataSource.createQueryRunner();
    await queryRunner.connect();
    await queryRunner.startTransaction();

    try {
      // Step 1: Get package details
      const [jobPackage] = await queryRunner.query(
        'SELECT job_credits, ai_credits, has_ai_interview, name FROM job_packages WHERE code = ? AND is_active = 1',
        [packageCode],
      );

      if (!jobPackage) {
        throw new Error('Package not found or inactive');
      }

      // Step 2: Determine credit type
      const isAiPackage = jobPackage.has_ai_interview || false;
      const creditType = isAiPackage ? 'ai' : 'job';
      const requiredCredits = isAiPackage
        ? jobPackage.ai_credits || 1
        : jobPackage.job_credits || 1;

      // 🔴 CRITICAL: Lock wallet row (prevents concurrent deductions)
      // This FOR UPDATE lock ensures no OTHER transaction can read/write this row until we commit/rollback
      const [wallet] = await queryRunner.query(
        'SELECT id, job_credits, ai_credits FROM wallets WHERE user_id = ? FOR UPDATE',
        [userId],
      );

      if (!wallet) {
        throw new Error('Wallet not found');
      }

      const availableCredits = isAiPackage
        ? wallet?.ai_credits || 0
        : wallet?.job_credits || 0;

      // 🔴 EDGE CASE: Insufficient credits check INSIDE transaction
      if (availableCredits < requiredCredits) {
        throw new Error(
          `Insufficient ${creditType} credits. Need ${requiredCredits}, have ${availableCredits}`,
        );
      }

      // Step 3: Deduct credits
      const creditField = isAiPackage ? 'ai_credits' : 'job_credits';
      const newBalance = availableCredits - requiredCredits;

      await queryRunner.query(
        `UPDATE wallets SET ${creditField} = ? WHERE user_id = ?`,
        [newBalance, userId],
      );

      // Step 4: Create transaction record (linked to wallet_id)
      await queryRunner.query(
        `INSERT INTO wallet_transactions 
         (wallet_id, type, credit_type, amount, reason, reference_type, reference_id, balance_after) 
         VALUES (?, 'debit', ?, ?, ?, 'job_post', NULL, ?)`,
        [
          wallet.id,
          creditType,
          requiredCredits,
          `Job post - ${jobPackage.name}`,
          newBalance,
        ],
      );

      // 5. Record Usage (Analytics)
      await queryRunner.manager.save(UsageRecord, {
        user_id: userId,
        feature_key: 'job_post',
        units_used: 1,
        // Note: We don't have job_id here yet as it's typically created after.
        // Ideally we pass job_id or update this record later.
        // For now, logging the EVENT is sufficient for "How many jobs posted?".
        reference_id: null,
      });

      // 🔴 CRITICAL: Commit transaction (all or nothing!)
      await queryRunner.commitTransaction();

      return {
        success: true,
        credit_type: creditType,
        deducted: requiredCredits,
        remaining: newBalance,
      };
    } catch (error) {
      // 🔴 CRITICAL: Rollback on ANY error to ensure data consistency
      await queryRunner.rollbackTransaction();
      throw error;
    } finally {
      // Always release connection back to pool
      await queryRunner.release();
    }
  }

  /**
   * Get Balance
   */
  async getBalance(userId: number) {
    try {
      // console.log('💳 [WALLET SERVICE] getBalance called for userId:', userId);

      // 🔴 LAZY EXPIRY CHECK
      await this.checkAndProcessExpiry(userId);

      // Get wallet balance
      const [wallet] = await this.dataSource.query(
        'SELECT job_credits, database_credits, ai_credits FROM wallets WHERE user_id = ?',
        [userId],
      );
      // console.log('💳 [WALLET SERVICE] Wallet found:', wallet);

      // Get active subscription(s) - Use UTC_TIMESTAMP for consistency
      // const now = new Date().toISOString().slice(0, 19).replace('T', ' ');
      // console.log('💳 [WALLET SERVICE] Current time (UTC from Node):', now);

      /* OLD: Single Subscription Fetch
    const [subscription] = await this.dataSource.query(
      `SELECT ... LIMIT 1`, ...
    ); 
    */

      const activeSubscriptions: any[] = await this.dataSource.query(
        `SELECT us.id, us.plan_id, us.starts_at, us.ends_at, us.status, p.name as plan_name, p.code as plan_code, p.type as plan_type
         FROM user_subscriptions us
         LEFT JOIN plans p ON us.plan_id = p.id
         WHERE us.user_id = ? AND us.status = 'active' AND us.starts_at <= NOW() AND us.ends_at >= NOW()
         ORDER BY us.ends_at DESC`,
        [userId],
      );
      // console.log(
      // '💳 [WALLET SERVICE] Active subscriptions found:',
      // activeSubscriptions,
      // );

      // Debug: Also check what the database sees for this user
      if (activeSubscriptions.length === 0) {
        const debugSubs = await this.dataSource.query(
          `SELECT id, plan_id, starts_at, ends_at, status, NOW() as db_now, UTC_TIMESTAMP() as db_utc
           FROM user_subscriptions WHERE user_id = ?`,
          [userId],
        );
        // console.log(
        //   '💳 [WALLET SERVICE] DEBUG - All subscriptions for user (timezone check):',
        //   debugSubs,
        // );
      }

      // Check if user has made any purchases
      const [purchaseCount] = await this.dataSource.query(
        'SELECT COUNT(*) as count FROM payments WHERE user_id = ? AND status = "success"',
        [userId],
      );

      // Get free tier limits from config_settings
      const [freeJobPostsConfig] = await this.dataSource.query(
        'SELECT setting_value FROM config_setting WHERE setting_key = "free_job_posts"',
      );
      const [freeCVViewsConfig] = await this.dataSource.query(
        'SELECT setting_value FROM config_setting WHERE setting_key = "cv_limit_free"',
      );

      const freeJobPosts = parseInt(freeJobPostsConfig?.setting_value || '1');
      const freeCVViews = parseInt(freeCVViewsConfig?.setting_value || '5');

      const subscription =
        activeSubscriptions.length > 0 ? activeSubscriptions[0] : null;
      // console.log('💳 [WALLET SERVICE] subscription determined:', subscription);

      const baseResponse = {
        job_credits: wallet?.job_credits || 0,
        database_credits: wallet?.database_credits || 0,
        ai_credits: wallet?.ai_credits || 0,
        subscription: subscription
          ? {
              plan_name: subscription.plan_name,
              starts_at: subscription.starts_at,
              ends_at: subscription.ends_at,
              status: subscription.status,
              is_active: true,
            }
          : null,
        has_purchases: parseInt(String(purchaseCount?.count || 0)) > 0,
      };

      // Determine User Type
      let userType = 'free';
      if (subscription) {
        userType = 'subscription';
      } else if (parseInt(String(purchaseCount?.count || 0)) > 0) {
        userType = 'payg';
      }
      // console.log('💳 [WALLET SERVICE] User type determined:', {
      // userType,
      // hasSubscription: !!subscription,
      // hasPurchases: parseInt(String(purchaseCount?.count || 0)) > 0,
      // });

      // Construct Active Plans Array
      const activePlans = activeSubscriptions.map((sub) => ({
        id: String(sub.plan_code || sub.plan_id),
        dbId: sub.plan_id,
        name: sub.plan_name,
        type: sub.plan_type,
        expiry_date: sub.ends_at,
        days_leaving: Math.ceil(
          (new Date(sub.ends_at).getTime() - Date.now()) /
            (1000 * 60 * 60 * 24),
        ),
        status: 'active',
      }));

      // Maintain backward compatibility for 'current_plan' (Primary Plan)
      let currentPlan = null;
      if (userType === 'subscription' && subscription) {
        currentPlan = {
          id: String(subscription.plan_code || subscription.plan_id),
          name: subscription.plan_name,
          is_active: true,
          expiry_date: subscription.ends_at,
          days_leaving: Math.ceil(
            (new Date(subscription.ends_at).getTime() - Date.now()) /
              (1000 * 60 * 60 * 24),
          ),
          features: ['Premium Features', 'Priority Support'],
          limits: { job_posts: 1, cv_views: 5 },
        };
      } else if (userType === 'payg') {
        currentPlan = {
          id: 'payg',
          name: 'Pay As You Go',
          is_active: true,
          features: ['Pay per Job', 'No Expiry'],
          limits: null,
        };
      } else {
        currentPlan = {
          id: 'free',
          name: 'Free',
          is_active: true,
          features: ['1 Free Job Post', 'Limited Search'],
          limits: {
            job_posts: freeJobPosts,
            cv_views: freeCVViews,
          },
        };
      }

      // 🔴 FETCH EXPIRING MANUAL CREDITS
      const expiringCredits = await this.dataSource.query(
        `SELECT type, credit_type, amount, created_at, expires_at 
         FROM wallet_transactions 
         WHERE wallet_id = (SELECT id FROM wallets WHERE user_id = ?)
         AND type = 'credit' 
         AND expires_at IS NOT NULL 
         AND is_expired = 0 
         AND expires_at > NOW()
         ORDER BY expires_at ASC`,
        [userId],
      );

      // console.log('💳 [WALLET SERVICE] Final response about to return:', {
      // user_type: userType,
      // active_plans_count: activePlans.length,
      // has_current_plan: !!currentPlan,
      // activePlansTEST: activePlans,
      // });

      return {
        ...baseResponse,
        user_type: userType,
        current_plan: currentPlan,
        active_plans: activePlans,
        expiring_credits: expiringCredits, // ✅ RETURN EXPIRY DATA
        free_limits: {
          job_posts: freeJobPosts,
          cv_views: freeCVViews,
        },
      };
    } catch (error) {
      console.error('❌ [WALLET SERVICE] Error in getBalance:', error);
      // Return default response so frontend doesn't crash
      return {
        job_credits: 0,
        database_credits: 0,
        ai_credits: 0,
        subscription: null,
        has_purchases: false,
        user_type: 'free',
        current_plan: null,
        active_plans: [],
        expiring_credits: [],
        free_limits: {
          job_posts: 1,
          cv_views: 5,
        },
        error: error instanceof Error ? error.message : 'Unknown error',
      };
    }
  }

  // ✅ New Method: Get Subscription History
  async getSubscriptionHistory(
    userId: number,
    status: 'active' | 'expired',
    limit: number,
    offset: number,
  ) {
    try {
      const now = new Date().toISOString().slice(0, 19).replace('T', ' ');
      const statusCondition =
        status === 'active'
          ? `us.status = 'active' AND us.ends_at >= '${now}'`
          : `(us.status != 'active' OR us.ends_at < '${now}')`;

      const subscriptions = await this.dataSource.query(
        `SELECT 
            us.id, us.plan_id, us.starts_at, us.ends_at, us.status,
            p.name as plan_name, p.code as plan_code, p.type as plan_type, p.price
        FROM user_subscriptions us
        LEFT JOIN plans p ON us.plan_id = p.id
        WHERE us.user_id = ? AND ${statusCondition}
        ORDER BY us.ends_at DESC
        LIMIT ? OFFSET ?`,
        [userId, limit, offset],
      );

      const [total] = await this.dataSource.query(
        `SELECT COUNT(*) as count 
        FROM user_subscriptions us 
        WHERE us.user_id = ? AND ${statusCondition}`,
        [userId],
      );

      return {
        data: subscriptions
          ? subscriptions.map((s) => ({
              id: s.id,
              name: s.plan_name,
              type: s.plan_type,
              expiryDate: s.ends_at,
              startDate: s.starts_at,
              status: s.status,
              price: s.price,
            }))
          : [],
        total: total ? Number(total.count) : 0,
      };
    } catch (error) {
      console.error('Error in getSubscriptionHistory:', error);
      throw error;
    }
  }

  // ✅ New Method: Get Credit Pack History
  async getCreditPackHistory(
    userId: number,
    status: 'active' | 'expired',
    limit: number,
    offset: number,
  ) {
    try {
      const now = new Date().toISOString().slice(0, 19).replace('T', ' ');
      const statusCondition =
        status === 'active'
          ? `expires_at > '${now}' AND is_expired = 0`
          : `(expires_at <= '${now}' OR is_expired = 1)`;

      const transactions = await this.dataSource.query(
        `SELECT type, credit_type, amount, created_at, expires_at 
        FROM wallet_transactions 
        WHERE wallet_id = (SELECT id FROM wallets WHERE user_id = ?)
        AND type = 'credit' 
        AND expires_at IS NOT NULL 
        AND ${statusCondition}
        ORDER BY expires_at ${status === 'active' ? 'ASC' : 'DESC'}
        LIMIT ? OFFSET ?`,
        [userId, limit, offset],
      );

      const [total] = await this.dataSource.query(
        `SELECT COUNT(*) as count 
        FROM wallet_transactions 
        WHERE wallet_id = (SELECT id FROM wallets WHERE user_id = ?)
        AND type = 'credit' 
        AND expires_at IS NOT NULL 
        AND ${statusCondition}`,
        [userId],
      );

      return {
        data: transactions
          ? transactions.map((t) => ({
              creditType: t.credit_type,
              amount: t.amount,
              createdAt: t.created_at,
              expiresAt: t.expires_at,
              status: status,
            }))
          : [],
        total: total ? Number(total.count) : 0,
      };
    } catch (error) {
      console.error('Error in getCreditPackHistory:', error);
      throw error;
    }
  }

  /**
   * Get Transactions
   */
  async getTransactions(
    userId: number,
    limit = 20,
    offset = 0,
    type?: string,
    startDate?: string,
    endDate?: string,
    creditType?: string,
  ) {
    // Get wallet_id
    const [w] = await this.dataSource.query(
      'SELECT id FROM wallets WHERE user_id = ?',
      [userId],
    );
    if (!w) return { transactions: [], total: 0 };

    let query = 'SELECT * FROM wallet_transactions WHERE wallet_id = ?';
    const params: any[] = [w.id];

    if (type && type !== 'all') {
      query += ' AND type = ?';
      params.push(type.toLowerCase());
    }

    if (creditType && creditType !== 'all') {
      query += ' AND credit_type = ?';
      // Mapped frontend values to DB values if necessary, but assuming direct match for 'job', 'database', 'ai'
      // DB stores: 'job', 'database', 'ai' (based on other code)
      // Frontend sends: 'job_post' -> 'job'? Let's check consistency.
      // Other code uses 'job', 'database', 'ai'. Frontend currently sends 'job_post' in my plan?
      // I should standardize. Let's assume frontend sends 'job', 'database', 'ai'.
      params.push(creditType.toLowerCase());
    }

    if (startDate) {
      query += ' AND created_at >= ?';
      params.push(startDate);
    }

    if (endDate) {
      query += ' AND created_at <= ?';
      // Add 23:59:59 if only date provided, or trust input
      // Assuming strict string match or client handles full ISO
      params.push(endDate);
    }

    // Get Total Count
    const countQuery = query.replace('SELECT *', 'SELECT COUNT(*) as count');
    const [countRes] = await this.dataSource.query(countQuery, params);
    const total = countRes ? Number(countRes.count) : 0;

    // Get Data
    query += ' ORDER BY created_at DESC LIMIT ? OFFSET ?';
    params.push(limit, offset);

    const transactions = await this.dataSource.query(query, params);

    return { transactions, total };
  }

  /**
   * 🔴 CORE LOGIC: Check and Process Expiry (FIFO Strategy)
   * This should be called before checking balance or making deductions.
   */
  async checkAndProcessExpiry(userId: number) {
    // Run in transaction to ensure atomicity
    const queryRunner = this.dataSource.createQueryRunner();
    await queryRunner.connect();
    await queryRunner.startTransaction();

    try {
      // 1. Get ALL credit inputs (ordered by date) to build the "Stack"
      const credits = await queryRunner.query(
        `SELECT * FROM wallet_transactions 
         WHERE wallet_id = (SELECT id FROM wallets WHERE user_id = ?) 
         AND type = 'credit' 
         ORDER BY created_at ASC`,
        [userId],
      );

      // 2. Get TOTAL usage (debits)
      // We assume usage consumes the oldest credits first (FIFO)
      const [usage] = await queryRunner.query(
        `SELECT SUM(amount) as total_spent FROM wallet_transactions 
         WHERE wallet_id = (SELECT id FROM wallets WHERE user_id = ?) 
         AND type = 'debit'`,
        [userId],
      );

      let totalSpent = Number(usage?.total_spent || 0);
      const now = new Date();

      // 3. Replay History
      for (const credit of credits) {
        // If this credit batch is ALREADY marked as expired/processed, skip logic?
        // No, we need to account for its "usage" vs "expiry" status to maintain the running total.
        // Actually, "is_expired" just means we already deducted the *remaining* amount.
        // But for the calculation loop, we treat it as:
        // - If it was used by spending -> It's gone.
        // - If it was expired -> It's gone.
        // We only care about *Pending Expiry* here.

        // HOWEVER, to be simple: We just iterate and see "Has this specific batch expired NOW?"
        // AND "Has it not been marked yet?".

        const amount = Number(credit.amount);
        let amountUsed = 0;

        if (totalSpent >= amount) {
          amountUsed = amount;
          totalSpent -= amount;
        } else {
          amountUsed = totalSpent;
          totalSpent = 0;
        }

        const remainingInBatch = amount - amountUsed;

        // Check if this batch is Expired AND has remaining credits
        if (
          remainingInBatch > 0 &&
          credit.expires_at &&
          new Date(credit.expires_at) < now
        ) {
          if (!credit.is_expired) {
            // 🔴 EXPIRE THIS BATCH
            // Deduct 'remainingInBatch' from wallet
            const creditType = credit.credit_type; // 'job', 'database', etc.

            // Lock Wallet
            const [wallet] = await queryRunner.query(
              'SELECT * FROM wallets WHERE user_id = ? FOR UPDATE',
              [userId],
            );

            const currentBalance = Number(wallet[`${creditType}_credits`] || 0);
            const newBalance = Math.max(0, currentBalance - remainingInBatch);
            // Math.max 0 safety, though logically it should match.

            // Update Wallet
            await queryRunner.query(
              `UPDATE wallets SET ${creditType}_credits = ? WHERE user_id = ?`,
              [newBalance, userId],
            );

            // Record "System Debit" (Expiry)
            await queryRunner.query(
              `INSERT INTO wallet_transactions 
              (wallet_id, type, credit_type, amount, reason, reference_type, reference_id, balance_after, is_expired, created_at)
              VALUES (?, 'debit', ?, ?, ?, 'system_expiry', NULL, ?, 1, NOW())`,
              [
                wallet.id,
                creditType,
                remainingInBatch,
                `Expired credits from ${new Date(credit.created_at).toISOString().slice(0, 10)}`,
                newBalance,
              ],
            );

            // Mark original credit as expired so we don't process again
            await queryRunner.query(
              'UPDATE wallet_transactions SET is_expired = 1 WHERE id = ?',
              [credit.id],
            );
          }
        }
      }

      await queryRunner.commitTransaction();
    } catch (err) {
      await queryRunner.rollbackTransaction();
      console.error('Expiry Check Failed', err);
    } finally {
      await queryRunner.release();
    }
  }
}
