import { Injectable } from '@nestjs/common';
import { InjectDataSource } from '@nestjs/typeorm';
import { DataSource } from 'typeorm';
import { UserReferralCode } from '../../domain/entities/user-referral-code.entity';

/** Generates a random user referral code: USR-XXXX-XXXX */
function generateUserCode(): string {
  const chars = 'ABCDEFGHJKLMNPQRSTUVWXYZ23456789';
  const seg = (n: number) =>
    Array.from({ length: n }, () => chars[Math.floor(Math.random() * chars.length)]).join('');
  return `USR-${seg(4)}-${seg(4)}`;
}

@Injectable()
export class UserReferralCodeRepository {
  constructor(@InjectDataSource() private readonly ds: DataSource) {}

  /** Create a referral code for a user (idempotent — skips if already exists). */
  async createForUser(userId: string): Promise<UserReferralCode> {
    // Try up to 5 times to find a unique code
    for (let attempt = 0; attempt < 5; attempt++) {
      const code = generateUserCode();
      try {
        const rows = await this.ds.query(
          `INSERT INTO public.user_referral_codes (user_id, code)
           VALUES ($1, $2)
           ON CONFLICT (user_id) DO UPDATE SET user_id = EXCLUDED.user_id
           RETURNING *`,
          [userId, code],
        );
        return this.map(rows[0]);
      } catch {
        // code collision — retry
      }
    }
    // Fallback: just return whatever exists
    return this.findByUserId(userId) as Promise<UserReferralCode>;
  }

  async findByCode(code: string): Promise<UserReferralCode | null> {
    const rows = await this.ds.query(
      `SELECT * FROM public.user_referral_codes WHERE code = $1`,
      [code],
    );
    return rows[0] ? this.map(rows[0]) : null;
  }

  async findByUserId(userId: string): Promise<UserReferralCode | null> {
    const rows = await this.ds.query(
      `SELECT * FROM public.user_referral_codes WHERE user_id = $1`,
      [userId],
    );
    return rows[0] ? this.map(rows[0]) : null;
  }

  /** Increment click count asynchronously (fire-and-forget safe). */
  async incrementClickCount(code: string): Promise<void> {
    await this.ds.query(
      `UPDATE public.user_referral_codes SET click_count = click_count + 1 WHERE code = $1`,
      [code],
    );
  }

  private map(r: any): UserReferralCode {
    return {
      userId: r.user_id,
      code: r.code,
      clickCount: r.click_count,
      createdAt: r.created_at,
    };
  }
}
