import { Injectable, NotFoundException, BadRequestException, Logger } from '@nestjs/common';
import { PrismaService } from '../../prisma/prisma.service';
import { TossPaymentsService } from './toss-payments.service';
import { RegisterCardDto } from './dto/register-card.dto';
import { TossConfirmDto } from './dto/toss-confirm.dto';
import { ChargeBillingDto } from './dto/charge-billing.dto';

@Injectable()
export class PaymentsService {
  private readonly logger = new Logger(PaymentsService.name);

  constructor(
    private prisma: PrismaService,
    private tossPayments: TossPaymentsService,
  ) {}

  async getMethods(userId: number) {
    return this.prisma.paymentMethod.findMany({
      where: { userId },
      orderBy: { createdAt: 'desc' },
      select: {
        id: true,
        brand: true,
        last4: true,
        holderName: true,
        expiry: true,
        isDefault: true,
        createdAt: true,
      },
    });
  }

  async registerCard(userId: number, dto: RegisterCardDto) {
    const result = await this.tossPayments.issueBillingKey(dto.authKey, dto.customerKey);

    if (!result.billingKey) {
      throw new BadRequestException('Failed to issue billing key');
    }

    const card = result.card || {};
    const existingCount = await this.prisma.paymentMethod.count({ where: { userId } });

    const paymentMethod = await this.prisma.paymentMethod.create({
      data: {
        userId,
        brand: card.issuerCode || card.acquirerCode || 'CARD',
        last4: card.number ? card.number.slice(-4) : '****',
        holderName: card.ownerType === 'PERSONAL' ? '개인' : '법인',
        expiry: '',
        isDefault: existingCount === 0,
        tossBillingKey: result.billingKey,
      },
    });

    return {
      id: paymentMethod.id,
      brand: paymentMethod.brand,
      last4: paymentMethod.last4,
      holderName: paymentMethod.holderName,
      expiry: paymentMethod.expiry,
      isDefault: paymentMethod.isDefault,
      createdAt: paymentMethod.createdAt,
    };
  }

  async removeCard(userId: number, id: number) {
    const method = await this.prisma.paymentMethod.findFirst({
      where: { id, userId },
    });
    if (!method) {
      throw new NotFoundException('Payment method not found');
    }
    await this.prisma.paymentMethod.delete({ where: { id } });

    if (method.isDefault) {
      const next = await this.prisma.paymentMethod.findFirst({
        where: { userId },
        orderBy: { createdAt: 'desc' },
      });
      if (next) {
        await this.prisma.paymentMethod.update({
          where: { id: next.id },
          data: { isDefault: true },
        });
      }
    }
    return { success: true };
  }

  async setDefaultCard(userId: number, id: number) {
    const method = await this.prisma.paymentMethod.findFirst({
      where: { id, userId },
    });
    if (!method) {
      throw new NotFoundException('Payment method not found');
    }

    await this.prisma.$transaction([
      this.prisma.paymentMethod.updateMany({
        where: { userId },
        data: { isDefault: false },
      }),
      this.prisma.paymentMethod.update({
        where: { id },
        data: { isDefault: true },
      }),
    ]);
    return { success: true };
  }

  async confirmPayment(dto: TossConfirmDto) {
    const result = await this.tossPayments.confirmPayment(
      dto.paymentKey,
      dto.orderId,
      dto.amount,
    );
    return result;
  }

  async processBilling(dto: RegisterCardDto) {
    const result = await this.tossPayments.issueBillingKey(dto.authKey, dto.customerKey);
    return result;
  }

  async chargeBilling(userId: number, dto: ChargeBillingDto) {
    // Find user's default payment method
    const defaultMethod = await this.prisma.paymentMethod.findFirst({
      where: { userId, isDefault: true },
    });
    if (!defaultMethod || !defaultMethod.tossBillingKey) {
      throw new BadRequestException('No default payment method with billing key');
    }

    const orderId = dto.orderId || `order_${userId}_${Date.now()}`;
    const customerKey = `user_${userId}`;

    const result = await this.tossPayments.chargeBillingKey(
      defaultMethod.tossBillingKey,
      customerKey,
      dto.amount,
      dto.orderName,
      orderId,
    );

    return result;
  }
}
