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

class PayoutService {
  /**
   * Attempt to process payout for a single rental immediately after completion.
   * Checks if owner's Stripe account has payouts enabled before attempting.
   * @param {string} rentalId - The rental ID to process
   * @returns {Object} - { attempted, success, reason, transferId, amount }
   */
  static async triggerPayoutOnCompletion(rentalId) {
    try {
      const rental = await Rental.findByPk(rentalId, {
        include: [
          {
            model: User,
            as: "owner",
            attributes: ["id", "email", "firstName", "lastName", "stripeConnectedAccountId", "stripePayoutsEnabled"],
          },
          { model: Item, as: "item" },
        ],
      });

      if (!rental) {
        logger.warn("Rental not found for payout trigger", { rentalId });
        return { attempted: false, success: false, reason: "rental_not_found" };
      }

      // Check eligibility conditions
      if (rental.paymentStatus !== "paid") {
        logger.info("Payout skipped: payment not paid", { rentalId, paymentStatus: rental.paymentStatus });
        return { attempted: false, success: false, reason: "payment_not_paid" };
      }

      if (rental.payoutStatus !== "pending") {
        logger.info("Payout skipped: payout not pending", { rentalId, payoutStatus: rental.payoutStatus });
        return { attempted: false, success: false, reason: "payout_not_pending" };
      }

      if (!rental.owner?.stripeConnectedAccountId) {
        logger.info("Payout skipped: owner has no Stripe account", { rentalId, ownerId: rental.ownerId });
        return { attempted: false, success: false, reason: "no_stripe_account" };
      }

      // Check if owner has payouts enabled (onboarding complete)
      if (!rental.owner.stripePayoutsEnabled) {
        logger.info("Payout deferred: owner payouts not enabled, will process when onboarding completes", {
          rentalId,
          ownerId: rental.ownerId,
        });
        return { attempted: false, success: false, reason: "payouts_not_enabled" };
      }

      // Attempt the payout
      const result = await this.processRentalPayout(rental);

      logger.info("Payout triggered successfully on completion", {
        rentalId,
        transferId: result.transferId,
        amount: result.amount,
      });

      return {
        attempted: true,
        success: true,
        transferId: result.transferId,
        amount: result.amount,
      };
    } catch (error) {
      logger.error("Error triggering payout on completion", {
        error: error.message,
        stack: error.stack,
        rentalId,
      });

      // Payout marked as failed by processRentalPayout, will be retried by daily retry job
      return {
        attempted: true,
        success: false,
        reason: "payout_failed",
        error: error.message,
      };
    }
  }

  static async getEligiblePayouts() {
    try {
      const eligibleRentals = await Rental.findAll({
        where: {
          status: "completed",
          paymentStatus: "paid",
          payoutStatus: "pending",
        },
        include: [
          {
            model: User,
            as: "owner",
            where: {
              stripeConnectedAccountId: {
                [Op.not]: null,
              },
              stripePayoutsEnabled: true,
            },
          },
          {
            model: Item,
            as: "item",
          },
        ],
      });

      return eligibleRentals;
    } catch (error) {
      logger.error("Error getting eligible payouts", { error: error.message, stack: error.stack });
      throw error;
    }
  }

  static async processRentalPayout(rental) {
    try {
      if (!rental.owner || !rental.owner.stripeConnectedAccountId) {
        throw new Error("Owner does not have a connected Stripe account");
      }

      if (rental.payoutStatus !== "pending") {
        throw new Error("Rental payout has already been processed");
      }

      if (!rental.payoutAmount || rental.payoutAmount <= 0) {
        throw new Error("Invalid payout amount");
      }

      // Create Stripe transfer
      const transfer = await StripeService.createTransfer({
        amount: rental.payoutAmount,
        destination: rental.owner.stripeConnectedAccountId,
        metadata: {
          rentalId: rental.id,
          ownerId: rental.ownerId,
          totalAmount: rental.totalAmount.toString(),
          platformFee: rental.platformFee.toString(),
          startDateTime: rental.startDateTime.toISOString(),
          endDateTime: rental.endDateTime.toISOString(),
        },
      });

      // Update rental with successful payout
      await rental.update({
        payoutStatus: "completed",
        payoutProcessedAt: new Date(),
        stripeTransferId: transfer.id,
      });

      console.log(
        `Payout completed for rental ${rental.id}: $${rental.payoutAmount} to ${rental.owner.stripeConnectedAccountId}`
      );

      // Send payout notification email to owner
      try {
        await emailServices.rentalFlow.sendPayoutReceivedEmail(rental.owner, rental);
        logger.info("Payout notification email sent to owner", {
          rentalId: rental.id,
          ownerId: rental.ownerId
        });
      } catch (emailError) {
        // Log error but don't fail the payout
        logger.error("Failed to send payout notification email", {
          error: emailError.message,
          stack: emailError.stack,
          rentalId: rental.id,
          ownerId: rental.ownerId
        });
      }

      return {
        success: true,
        transferId: transfer.id,
        amount: rental.payoutAmount,
      };
    } catch (error) {
      logger.error("Error processing payout for rental", { error: error.message, stack: error.stack, rentalId: rental.id });

      // Update status to failed
      await rental.update({
        payoutStatus: "failed",
      });

      throw error;
    }
  }

  static async processAllEligiblePayouts() {
    try {
      const eligibleRentals = await this.getEligiblePayouts();

      console.log(
        `Found ${eligibleRentals.length} eligible rentals for payout`
      );

      const results = {
        successful: [],
        failed: [],
        totalProcessed: eligibleRentals.length,
      };

      for (const rental of eligibleRentals) {
        try {
          const result = await this.processRentalPayout(rental);
          results.successful.push({
            rentalId: rental.id,
            amount: result.amount,
            transferId: result.transferId,
          });
        } catch (error) {
          results.failed.push({
            rentalId: rental.id,
            error: error.message,
          });
        }
      }

      console.log(
        `Payout processing complete: ${results.successful.length} successful, ${results.failed.length} failed`
      );

      return results;
    } catch (error) {
      logger.error("Error processing all eligible payouts", { error: error.message, stack: error.stack });
      throw error;
    }
  }

  static async retryFailedPayouts() {
    try {
      const failedRentals = await Rental.findAll({
        where: {
          status: "completed",
          paymentStatus: "paid",
          payoutStatus: "failed",
        },
        include: [
          {
            model: User,
            as: "owner",
            where: {
              stripeConnectedAccountId: {
                [Op.not]: null,
              },
              stripePayoutsEnabled: true,
            },
          },
          {
            model: Item,
            as: "item",
          },
        ],
      });

      console.log(`Found ${failedRentals.length} failed payouts to retry`);

      const results = {
        successful: [],
        failed: [],
        totalProcessed: failedRentals.length,
      };

      for (const rental of failedRentals) {
        try {
          // Reset to pending before retrying
          await rental.update({ payoutStatus: "pending" });

          const result = await this.processRentalPayout(rental);
          results.successful.push({
            rentalId: rental.id,
            amount: result.amount,
            transferId: result.transferId,
          });
        } catch (error) {
          results.failed.push({
            rentalId: rental.id,
            error: error.message,
          });
        }
      }

      console.log(
        `Retry processing complete: ${results.successful.length} successful, ${results.failed.length} failed`
      );

      return results;
    } catch (error) {
      logger.error("Error retrying failed payouts", { error: error.message, stack: error.stack });
      throw error;
    }
  }
}

module.exports = PayoutService;
