const { Rental } = require("../models");
const StripeService = require("./stripeService");
const EventBridgeSchedulerService = require("./eventBridgeSchedulerService");
const { isActive } = require("../utils/rentalStatus");
const logger = require("../utils/logger");

class RefundService {
  /**
   * Calculate refund amount based on policy and who cancelled
   * @param {Object} rental - Rental instance
   * @param {string} cancelledBy - 'renter' or 'owner'
   * @returns {Object} - { refundAmount, refundPercentage, reason }
   */
  static calculateRefundAmount(rental, cancelledBy) {
    const totalAmount = rental.totalAmount;
    let refundPercentage = 0;
    let reason = "";

    if (cancelledBy === "owner") {
      // Owner cancellation = full refund
      refundPercentage = 1.0;
      reason = "Full refund - cancelled by owner";
    } else if (cancelledBy === "renter") {
      // Calculate based on time until rental start
      const now = new Date();
      const startDateTime = new Date(rental.startDateTime);
      const hoursUntilStart = (startDateTime - now) / (1000 * 60 * 60);

      if (hoursUntilStart < 24) {
        refundPercentage = 0.0;
        reason = "No refund - cancelled within 24 hours of start time";
      } else if (hoursUntilStart < 48) {
        refundPercentage = 0.5;
        reason = "50% refund - cancelled between 24-48 hours of start time";
      } else {
        refundPercentage = 1.0;
        reason = "Full refund - cancelled more than 48 hours before start time";
      }
    }

    const refundAmount = parseFloat((totalAmount * refundPercentage).toFixed(2));

    return {
      refundAmount,
      refundPercentage,
      reason,
    };
  }

  /**
   * Validate if a rental can be cancelled
   * @param {Object} rental - Rental instance
   * @param {string} userId - User ID attempting to cancel
   * @returns {Object} - { canCancel, reason, cancelledBy }
   */
  static validateCancellationEligibility(rental, userId) {
    // Check if rental is already cancelled
    if (rental.status === "cancelled") {
      return {
        canCancel: false,
        reason: "Rental is already cancelled",
        cancelledBy: null,
      };
    }

    // Check if rental is completed
    if (rental.status === "completed") {
      return {
        canCancel: false,
        reason: "Cannot cancel completed rental",
        cancelledBy: null,
      };
    }

    // Check if rental is active (computed from confirmed + start time passed)
    if (isActive(rental)) {
      return {
        canCancel: false,
        reason: "Cannot cancel active rental",
        cancelledBy: null,
      };
    }

    // Check if user has permission to cancel
    let cancelledBy = null;
    if (rental.renterId === userId) {
      cancelledBy = "renter";
    } else if (rental.ownerId === userId) {
      cancelledBy = "owner";
    } else {
      return {
        canCancel: false,
        reason: "You are not authorized to cancel this rental",
        cancelledBy: null,
      };
    }

    // Allow cancellation for pending rentals (before owner approval) or paid/free rentals
    const isPendingRequest = rental.status === "pending";
    const isPaymentSettled =
      rental.paymentStatus === "paid" || rental.paymentStatus === "not_required";

    if (!isPendingRequest && !isPaymentSettled) {
      return {
        canCancel: false,
        reason: "Cannot cancel rental that hasn't been paid",
        cancelledBy: null,
      };
    }

    return {
      canCancel: true,
      reason: "Cancellation allowed",
      cancelledBy,
    };
  }

  /**
   * Process the full cancellation and refund
   * @param {string} rentalId - Rental ID
   * @param {string} userId - User ID cancelling
   * @param {string} cancellationReason - Optional reason provided by user
   * @returns {Object} - Updated rental with refund information
   */
  static async processCancellation(rentalId, userId, cancellationReason = null) {
    const rental = await Rental.findByPk(rentalId);

    if (!rental) {
      throw new Error("Rental not found");
    }

    // Validate cancellation eligibility
    const eligibility = this.validateCancellationEligibility(rental, userId);
    if (!eligibility.canCancel) {
      throw new Error(eligibility.reason);
    }

    // Calculate refund amount
    const refundCalculation = this.calculateRefundAmount(
      rental,
      eligibility.cancelledBy
    );

    let stripeRefundId = null;
    let refundProcessedAt = null;

    // Process refund with Stripe if amount > 0 and we have payment intent ID
    if (
      refundCalculation.refundAmount > 0 &&
      rental.stripePaymentIntentId
    ) {
      try {
        const refund = await StripeService.createRefund({
          paymentIntentId: rental.stripePaymentIntentId,
          amount: refundCalculation.refundAmount,
          metadata: {
            rentalId: rental.id,
            cancelledBy: eligibility.cancelledBy,
            refundReason: refundCalculation.reason,
          },
        });

        stripeRefundId = refund.id;
        refundProcessedAt = new Date();
      } catch (error) {
        logger.error("Error processing Stripe refund", { error });
        throw new Error(`Failed to process refund: ${error.message}`);
      }
    } else if (refundCalculation.refundAmount > 0) {
      // Log warning if we should refund but don't have payment intent
      logger.warn(
        "Refund amount calculated but no payment intent ID for rental",
        { rentalId }
      );
    }

    // Update rental with cancellation and refund info
    const updatedRental = await rental.update({
      status: "cancelled",
      cancelledBy: eligibility.cancelledBy,
      cancelledAt: new Date(),
      refundAmount: refundCalculation.refundAmount,
      refundProcessedAt,
      refundReason:
        cancellationReason || refundCalculation.reason,
      stripeRefundId,
      // Reset payout status since rental is cancelled
      payoutStatus: "pending",
    });

    // Delete condition check schedules since rental is cancelled
    try {
      await EventBridgeSchedulerService.deleteConditionCheckSchedules(updatedRental);
    } catch (schedulerError) {
      logger.error("Failed to delete condition check schedules", {
        error: schedulerError.message,
        rentalId: updatedRental.id,
      });
      // Don't fail the cancellation - schedule cleanup is non-critical
    }

    return {
      rental: updatedRental,
      refund: {
        amount: refundCalculation.refundAmount,
        percentage: refundCalculation.refundPercentage,
        reason: refundCalculation.reason,
        processed: !!refundProcessedAt,
        stripeRefundId,
      },
    };
  }

  /**
   * Get refund preview without processing
   * @param {string} rentalId - Rental ID
   * @param {string} userId - User ID requesting preview
   * @returns {Object} - Preview of refund calculation
   */
  static async getRefundPreview(rentalId, userId) {
    const rental = await Rental.findByPk(rentalId);

    if (!rental) {
      throw new Error("Rental not found");
    }

    const eligibility = this.validateCancellationEligibility(rental, userId);
    if (!eligibility.canCancel) {
      throw new Error(eligibility.reason);
    }

    const refundCalculation = this.calculateRefundAmount(
      rental,
      eligibility.cancelledBy
    );

    return {
      canCancel: true,
      cancelledBy: eligibility.cancelledBy,
      refundAmount: refundCalculation.refundAmount,
      refundPercentage: refundCalculation.refundPercentage,
      reason: refundCalculation.reason,
      totalAmount: rental.totalAmount,
    };
  }
}

module.exports = RefundService;