import { Injectable, Inject, NotFoundException, BadRequestException } from '@nestjs/common';
import { DataSource } from 'typeorm';
import { COUPON_REPOSITORY, ICouponRepository } from '../../domain/repositories/coupon.repository.interface';
import { Coupon, CouponStatus } from '../../domain/entities/coupon.entity';

export interface BatchOperation {
  id: string;
  type: 'issue' | 'recall' | 'price_adjust';
  issuerId: string;
  affectedCount: number;
  details: Record<string, any>;
  createdAt: Date;
}

// In-memory operation log (would be a DB table in production)
const operationHistory: BatchOperation[] = [];

@Injectable()
export class CouponBatchService {
  constructor(
    @Inject(COUPON_REPOSITORY)
    private readonly couponRepo: ICouponRepository,
    private readonly dataSource: DataSource,
  ) {}

  /**
   * Batch issue coupons from a template coupon.
   */
  async batchIssue(
    issuerId: string,
    templateId: string,
    quantity: number,
    expiryDate?: string,
  ) {
    const template = await this.couponRepo.findById(templateId);
    if (!template) throw new NotFoundException('Template coupon not found');
    if (template.issuerId !== issuerId) {
      throw new BadRequestException('Template does not belong to this issuer');
    }

    const coupons: Coupon[] = [];

    await this.dataSource.transaction(async (manager) => {
      for (let i = 0; i < quantity; i++) {
        const coupon = manager.create(Coupon, {
          issuerId,
          name: template.name,
          description: template.description,
          imageUrl: template.imageUrl,
          faceValue: template.faceValue,
          currentPrice: template.currentPrice,
          issuePrice: template.issuePrice,
          totalSupply: 1,
          remainingSupply: 1,
          expiryDate: expiryDate ? new Date(expiryDate) : template.expiryDate,
          couponType: template.couponType,
          category: template.category,
          status: CouponStatus.MINTED,
          isTransferable: template.isTransferable,
          maxResaleCount: template.maxResaleCount,
        });
        const saved = await manager.save(coupon);
        coupons.push(saved);
      }
    });

    const operation: BatchOperation = {
      id: require('crypto').randomUUID(),
      type: 'issue',
      issuerId,
      affectedCount: coupons.length,
      details: { templateId, quantity, expiryDate },
      createdAt: new Date(),
    };
    operationHistory.unshift(operation);

    return {
      operationId: operation.id,
      issued: coupons.length,
      couponIds: coupons.map((c) => c.id),
    };
  }

  /**
   * Batch recall coupons.
   */
  async batchRecall(issuerId: string, couponIds: string[], reason: string) {
    const results: Array<{ couponId: string; success: boolean; error?: string }> = [];

    await this.dataSource.transaction(async (manager) => {
      for (const couponId of couponIds) {
        try {
          const coupon = await manager.findOne(Coupon, {
            where: { id: couponId },
            lock: { mode: 'pessimistic_write' },
          });

          if (!coupon) {
            results.push({ couponId, success: false, error: 'Coupon not found' });
            continue;
          }
          if (coupon.issuerId !== issuerId) {
            results.push({ couponId, success: false, error: 'Not owned by this issuer' });
            continue;
          }
          if (coupon.status === CouponStatus.REDEEMED) {
            results.push({ couponId, success: false, error: 'Already redeemed' });
            continue;
          }

          coupon.status = CouponStatus.RECALLED;
          await manager.save(coupon);
          results.push({ couponId, success: true });
        } catch (err: any) {
          results.push({ couponId, success: false, error: err.message });
        }
      }
    });

    const successCount = results.filter((r) => r.success).length;
    const operation: BatchOperation = {
      id: require('crypto').randomUUID(),
      type: 'recall',
      issuerId,
      affectedCount: successCount,
      details: { couponIds, reason, results },
      createdAt: new Date(),
    };
    operationHistory.unshift(operation);

    return {
      operationId: operation.id,
      results,
      successCount,
      failCount: results.length - successCount,
    };
  }

  /**
   * Batch price adjustment.
   */
  async batchPriceAdjust(issuerId: string, couponIds: string[], adjustPercent: number) {
    const results: Array<{ couponId: string; success: boolean; oldPrice?: string; newPrice?: string; error?: string }> = [];

    await this.dataSource.transaction(async (manager) => {
      for (const couponId of couponIds) {
        try {
          const coupon = await manager.findOne(Coupon, {
            where: { id: couponId },
            lock: { mode: 'pessimistic_write' },
          });

          if (!coupon) {
            results.push({ couponId, success: false, error: 'Coupon not found' });
            continue;
          }
          if (coupon.issuerId !== issuerId) {
            results.push({ couponId, success: false, error: 'Not owned by this issuer' });
            continue;
          }

          const oldPrice = coupon.currentPrice || coupon.faceValue;
          const multiplier = 1 + adjustPercent / 100;
          const newPrice = Math.round(Number(oldPrice) * multiplier * 100) / 100;

          if (newPrice < 0) {
            results.push({ couponId, success: false, error: 'Adjusted price would be negative' });
            continue;
          }

          coupon.currentPrice = String(newPrice);
          await manager.save(coupon);
          results.push({ couponId, success: true, oldPrice: String(oldPrice), newPrice: String(newPrice) });
        } catch (err: any) {
          results.push({ couponId, success: false, error: err.message });
        }
      }
    });

    const successCount = results.filter((r) => r.success).length;
    const operation: BatchOperation = {
      id: require('crypto').randomUUID(),
      type: 'price_adjust',
      issuerId,
      affectedCount: successCount,
      details: { couponIds, adjustPercent, results },
      createdAt: new Date(),
    };
    operationHistory.unshift(operation);

    return {
      operationId: operation.id,
      results,
      successCount,
      failCount: results.length - successCount,
    };
  }

  /**
   * List past batch operations.
   */
  async listOperations(issuerId: string, page: number, limit: number) {
    const filtered = operationHistory.filter((op) => op.issuerId === issuerId);
    const total = filtered.length;
    const start = (page - 1) * limit;
    const items = filtered.slice(start, start + limit);
    return { items, total, page, limit };
  }
}
