const { Rental, User, Item } = require("../models");
const emailServices = require("./email");
const logger = require("../utils/logger");

class DisputeService {
  /**
   * Handle charge.dispute.created webhook
   * Called when a renter disputes a charge with their bank
   * @param {Object} dispute - The Stripe dispute object from the webhook
   * @returns {Object} Processing result
   */
  static async handleDisputeCreated(dispute) {
    const paymentIntentId = dispute.payment_intent;

    logger.info("Processing dispute.created webhook", {
      disputeId: dispute.id,
      paymentIntentId,
      reason: dispute.reason,
      amount: dispute.amount,
    });

    const rental = await Rental.findOne({
      where: { stripePaymentIntentId: paymentIntentId },
      include: [
        { model: User, as: "owner" },
        { model: User, as: "renter" },
        { model: Item, as: "item" },
      ],
    });

    if (!rental) {
      logger.warn("Dispute received for unknown rental", {
        paymentIntentId,
        disputeId: dispute.id,
      });
      return { processed: false, reason: "rental_not_found" };
    }

    // Update rental with dispute info
    await rental.update({
      stripeDisputeStatus: dispute.status,
      stripeDisputeId: dispute.id,
      stripeDisputeReason: dispute.reason,
      stripeDisputeAmount: dispute.amount,
      stripeDisputeCreatedAt: new Date(dispute.created * 1000),
      stripeDisputeEvidenceDueBy: new Date(
        dispute.evidence_details.due_by * 1000
      ),
    });

    // Pause payout if not yet deposited to owner's bank
    if (rental.bankDepositStatus !== "paid") {
      await rental.update({ payoutStatus: "on_hold" });
      logger.info("Payout placed on hold due to dispute", {
        rentalId: rental.id,
      });
    }

    // Send admin notification
    await emailServices.payment.sendDisputeAlertEmail({
      rentalId: rental.id,
      amount: dispute.amount / 100,
      reason: dispute.reason,
      evidenceDueBy: new Date(dispute.evidence_details.due_by * 1000),
      renterEmail: rental.renter?.email,
      renterName: rental.renter?.firstName,
      ownerEmail: rental.owner?.email,
      ownerName: rental.owner?.firstName,
      itemName: rental.item?.name,
    });

    logger.warn("Dispute created for rental", {
      rentalId: rental.id,
      disputeId: dispute.id,
      reason: dispute.reason,
      evidenceDueBy: dispute.evidence_details.due_by,
    });

    return { processed: true, rentalId: rental.id };
  }

  /**
   * Handle dispute closed events (won, lost, or warning_closed)
   * Called for: charge.dispute.closed, charge.dispute.funds_reinstated, charge.dispute.funds_withdrawn
   * @param {Object} dispute - The Stripe dispute object from the webhook
   * @returns {Object} Processing result
   */
  static async handleDisputeClosed(dispute) {
    logger.info("Processing dispute closed webhook", {
      disputeId: dispute.id,
      status: dispute.status,
    });

    const rental = await Rental.findOne({
      where: { stripeDisputeId: dispute.id },
      include: [{ model: User, as: "owner" }],
    });

    if (!rental) {
      logger.warn("Dispute closed for unknown rental", {
        disputeId: dispute.id,
      });
      return { processed: false, reason: "rental_not_found" };
    }

    const won = dispute.status === "won";

    await rental.update({
      stripeDisputeStatus: dispute.status,
      stripeDisputeClosedAt: new Date(),
    });

    // If we won the dispute, resume payout if it was on hold
    if (won && rental.payoutStatus === "on_hold") {
      await rental.update({ payoutStatus: "pending" });
      logger.info("Payout resumed after winning dispute", {
        rentalId: rental.id,
      });
    }

    // If we lost, record the loss amount
    if (!won && dispute.status === "lost") {
      await rental.update({
        stripeDisputeLost: true,
        stripeDisputeLostAmount: dispute.amount,
      });
      logger.warn("Dispute lost", {
        rentalId: rental.id,
        amount: dispute.amount,
      });

      // If owner was already paid, flag for manual review
      if (rental.bankDepositStatus === "paid") {
        await emailServices.payment.sendDisputeLostAlertEmail({
          rentalId: rental.id,
          amount: dispute.amount / 100,
          ownerAlreadyPaid: true,
          ownerPayoutAmount: rental.payoutAmount,
          ownerEmail: rental.owner?.email,
          ownerName: rental.owner?.firstName,
        });
        logger.warn(
          "Dispute lost - owner already paid, flagged for manual review",
          {
            rentalId: rental.id,
            payoutAmount: rental.payoutAmount,
          }
        );
      }
    }

    logger.info("Dispute closed", {
      rentalId: rental.id,
      disputeId: dispute.id,
      outcome: dispute.status,
    });

    return { processed: true, won, rentalId: rental.id };
  }
}

module.exports = DisputeService;
