from pathlib import Path
import re
root = Path.cwd()

# 1) entity
entity_dir = root/'src/modules/customers/entities'
entity_dir.mkdir(parents=True, exist_ok=True)
(entity_dir/'loyalty-setting.entity.ts').write_text("""import { Column, CreateDateColumn, Entity, PrimaryGeneratedColumn, UpdateDateColumn } from 'typeorm';

@Entity('loyalty_settings')
export class LoyaltySetting {
  @PrimaryGeneratedColumn()
  id: number;

  @Column({ nullable: true })
  agencyId?: number;

  @Column({ type: 'jsonb', default: {} })
  settings: any;

  @CreateDateColumn()
  createdAt: Date;

  @UpdateDateColumn()
  updatedAt: Date;
}
""")

# 2) customer entity columns
p = root/'src/modules/customers/entities/customer.entity.ts'
s = p.read_text()
if 'loyaltyPoints' not in s:
    marker = "  @Column({ default: false })\n  isVip: boolean;"
    insert = marker + """

  @Column({ default: 0 })
  loyaltyPoints: number;

  @Column({ default: 'Standard' })
  loyaltyTier: string;

  @Column({ type: 'decimal', precision: 5, scale: 2, default: 0 })
  vipDiscountPercent: number;

  @Column({ type: 'date', nullable: true })
  vipSince?: string;

  @Column({ type: 'text', nullable: true })
  vipBenefits?: string;

  @Column({ type: 'text', nullable: true })
  loyaltyHistory?: string;"""
    s = s.replace(marker, insert)
    p.write_text(s)

# 3) DTOs add fields
for rel in ['src/modules/customers/dto/create-customer.dto.ts','src/modules/customers/dto/update-customer.dto.ts']:
    p = root/rel
    if not p.exists(): continue
    s = p.read_text()
    if 'loyaltyPoints' not in s:
        # add before final class }
        add = """

  loyaltyPoints?: number;
  loyaltyTier?: string;
  vipDiscountPercent?: number;
  vipSince?: string;
  vipBenefits?: string;
  loyaltyHistory?: string;
"""
        idx = s.rfind('\n}')
        if idx != -1:
            s = s[:idx] + add + s[idx:]
            p.write_text(s)

# 4) customers.module.ts include entity in forFeature
p = root/'src/modules/customers/customers.module.ts'
if p.exists():
    s = p.read_text()
    if "loyalty-setting.entity" not in s:
        s = s.replace("import { Customer", "import { LoyaltySetting } from './entities/loyalty-setting.entity';\nimport { Customer")
        # if forFeature list exists, add LoyaltySetting before closing first ])
        s = re.sub(r"TypeOrmModule\.forFeature\(\[([^\]]*)\]\)", lambda m: "TypeOrmModule.forFeature([" + m.group(1).rstrip() + ("," if m.group(1).strip() and not m.group(1).strip().endswith(',') else "") + " LoyaltySetting])", s, count=1)
        p.write_text(s)

# 5) service patch
p = root/'src/modules/customers/customers.service.ts'
s = p.read_text()
if "loyalty-setting.entity" not in s:
    s = s.replace("import { Customer, CustomerType }", "import { LoyaltySetting } from './entities/loyalty-setting.entity';\nimport { Customer, CustomerType }")
if "loyaltySettingRepository" not in s:
    s = s.replace("@InjectRepository(Note)\n    private readonly noteRepository: Repository<Note>,", "@InjectRepository(Note)\n    private readonly noteRepository: Repository<Note>,\n\n    @InjectRepository(LoyaltySetting)\n    private readonly loyaltySettingRepository: Repository<LoyaltySetting>,")
# add assignment fields in create and update if not present
if "loyaltyPoints: data.loyaltyPoints" not in s:
    s = s.replace("isVip: data.isVip ?? false,", "isVip: data.isVip ?? false,\n      loyaltyPoints: Number(data.loyaltyPoints ?? 0),\n      loyaltyTier: data.loyaltyTier || 'Standard',\n      vipDiscountPercent: Number(data.vipDiscountPercent ?? 0),\n      vipSince: data.vipSince,\n      vipBenefits: data.vipBenefits,\n      loyaltyHistory: data.loyaltyHistory,")
if "loyaltyPoints: data.loyaltyPoints ?? customer.loyaltyPoints" not in s:
    s = s.replace("isVip: data.isVip ?? customer.isVip,", "isVip: data.isVip ?? customer.isVip,\n      loyaltyPoints: data.loyaltyPoints ?? customer.loyaltyPoints,\n      loyaltyTier: data.loyaltyTier ?? customer.loyaltyTier,\n      vipDiscountPercent: data.vipDiscountPercent ?? customer.vipDiscountPercent,\n      vipSince: data.vipSince ?? customer.vipSince,\n      vipBenefits: data.vipBenefits ?? customer.vipBenefits,\n      loyaltyHistory: data.loyaltyHistory ?? customer.loyaltyHistory,")
if "async getLoyaltySettingsForUser" not in s:
    methods = r'''

  private defaultLoyaltySettings() {
    return {
      pointsAmountUnit: 100,
      pointsPerUnit: 10,
      vipMinSpent: 10000,
      vipMinBookings: 10,
      vipMinTier: 'Gold',
      autoVip: true,
      tiers: [
        { name: 'Standard', minPoints: 0, discountPercent: 0, benefits: '' },
        { name: 'Silver', minPoints: 500, discountPercent: 5, benefits: 'Priorité support' },
        { name: 'Gold', minPoints: 1000, discountPercent: 10, benefits: 'Remise VIP 10%' },
        { name: 'Platinum', minPoints: 2500, discountPercent: 15, benefits: 'Surclassement selon disponibilité' },
        { name: 'Diamond', minPoints: 5000, discountPercent: 20, benefits: 'Service premium prioritaire' },
      ],
    };
  }

  async getLoyaltySettingsForUser(currentUser: AuthUser) {
    const agencyId = currentUser.role === 'super_admin' ? null : currentUser.agencyId || null;
    let row = await this.loyaltySettingRepository.findOne({ where: { agencyId: agencyId as any } });
    if (!row) {
      row = this.loyaltySettingRepository.create({ agencyId: agencyId as any, settings: this.defaultLoyaltySettings() });
      row = await this.loyaltySettingRepository.save(row);
    }
    return { id: row.id, agencyId: row.agencyId, ...this.defaultLoyaltySettings(), ...(row.settings || {}) };
  }

  async updateLoyaltySettingsForUser(data: any, currentUser: AuthUser) {
    const agencyId = currentUser.role === 'super_admin' ? null : currentUser.agencyId || null;
    let row = await this.loyaltySettingRepository.findOne({ where: { agencyId: agencyId as any } });
    if (!row) row = this.loyaltySettingRepository.create({ agencyId: agencyId as any, settings: {} });
    row.settings = { ...this.defaultLoyaltySettings(), ...(row.settings || {}), ...(data || {}) };
    row = await this.loyaltySettingRepository.save(row);
    return { success: true, id: row.id, agencyId: row.agencyId, ...row.settings };
  }

  private tierRank(name?: string) {
    const order = ['standard', 'silver', 'gold', 'platinum', 'diamond'];
    const idx = order.indexOf(String(name || '').toLowerCase());
    return idx < 0 ? 0 : idx;
  }

  async recalculateLoyaltyForCustomer(customerId: number, currentUser: AuthUser) {
    const customer = await this.findOneForUser(customerId, currentUser);
    const settings: any = await this.getLoyaltySettingsForUser(currentUser);
    const payments = await this.findPayments(customer.id, currentUser);
    const bookings = await this.findBookings(customer.id, currentUser);
    const paidPayments = payments.filter((p: any) => String(p.status || '').toLowerCase() === 'paid');
    const totalSpent = paidPayments.reduce((sum: number, p: any) => sum + Number(p.amountPaid ?? p.amount ?? p.totalTtc ?? 0), 0);
    const amountUnit = Number(settings.pointsAmountUnit || 100) || 100;
    const pointsPerUnit = Number(settings.pointsPerUnit || 10) || 10;
    const points = Math.floor((totalSpent / amountUnit) * pointsPerUnit);
    const tiers = Array.isArray(settings.tiers) ? settings.tiers : this.defaultLoyaltySettings().tiers;
    const tier = [...tiers].sort((a:any,b:any)=>Number(b.minPoints||0)-Number(a.minPoints||0)).find((t:any)=>points >= Number(t.minPoints||0)) || tiers[0];
    const tierName = tier?.name || 'Standard';
    const tierDiscount = Number(tier?.discountPercent || 0);
    const vipBySpent = Number(settings.vipMinSpent || 0) > 0 && totalSpent >= Number(settings.vipMinSpent || 0);
    const vipByBookings = Number(settings.vipMinBookings || 0) > 0 && bookings.length >= Number(settings.vipMinBookings || 0);
    const vipByTier = this.tierRank(tierName) >= this.tierRank(settings.vipMinTier || 'Gold');
    const shouldVip = Boolean(settings.autoVip !== false && (vipBySpent || vipByBookings || vipByTier));

    customer.loyaltyPoints = points as any;
    customer.loyaltyTier = tierName as any;
    customer.vipDiscountPercent = tierDiscount as any;
    customer.vipBenefits = tier?.benefits || customer.vipBenefits;
    customer.isVip = shouldVip;
    if (shouldVip && !customer.vipSince) customer.vipSince = new Date().toISOString().slice(0,10) as any;
    customer.loyaltyHistory = JSON.stringify({ recalculatedAt: new Date().toISOString(), totalSpent, paidPayments: paidPayments.length, bookings: bookings.length, points, tier: tierName, isVip: shouldVip });
    await this.customerRepository.save(customer);
    return { success: true, customerId: customer.id, totalSpent, bookings: bookings.length, paidPayments: paidPayments.length, points, tier: tierName, discountPercent: tierDiscount, isVip: shouldVip };
  }

  async recalculateAllLoyaltyForUser(currentUser: AuthUser) {
    const customers = await this.findAllForUser(currentUser, {} as any);
    const results = [];
    for (const customer of customers) {
      results.push(await this.recalculateLoyaltyForCustomer(customer.id, currentUser));
    }
    return { success: true, count: results.length, results };
  }
'''
    s = s.replace("\n  async rebuildFromBookings()", methods + "\n  async rebuildFromBookings()")
p.write_text(s)

# 6) controller endpoints
p = root/'src/modules/customers/customers.controller.ts'
s = p.read_text()
if "loyalty/settings" not in s:
    block = """
  @Get('loyalty/settings')
  @Roles('super_admin', 'director', 'agency_manager')
  getLoyaltySettings(@CurrentUser() user: AuthUser) {
    return this.customersService.getLoyaltySettingsForUser(user);
  }

  @Patch('loyalty/settings')
  @Roles('super_admin', 'director', 'agency_manager')
  updateLoyaltySettings(@Body() data: any, @CurrentUser() user: AuthUser) {
    return this.customersService.updateLoyaltySettingsForUser(data, user);
  }

  @Post('loyalty/recalculate')
  @Roles('super_admin', 'director', 'agency_manager')
  recalculateLoyalty(@Body() body: any, @CurrentUser() user: AuthUser) {
    if (body?.customerId) return this.customersService.recalculateLoyaltyForCustomer(Number(body.customerId), user);
    return this.customersService.recalculateAllLoyaltyForUser(user);
  }

"""
    s = s.replace("  @Get(':id')", block + "  @Get(':id')")
p.write_text(s)
print('Loyalty settings backend patch applied')
