import mongoose from "mongoose";
import Stripe from "stripe";
import envVars from "../../Config/env-vars.js";
import Payment from "../../DB/Model/payment/payments.model.js";
import Transactions from "../../DB/Model/payment/transactions.model.js";
import User from "../../DB/Model/user.model.js";
import Booking from "../../DB/Model/booking.modal.js";
import Service from "../../DB/Model/service.modal.js";
import { runInTransaction } from "../../Utils/runInTransaction.js";

const stripe = new Stripe(envVars.stripeSecretKey);


//  Webhook
export const stripeWebhook = async (req, res) => {
  console.log("⚡ Webhook hit at:", new Date().toISOString());

  const sig = req.headers["stripe-signature"];
  let event;
  try {
    event = stripe.webhooks.constructEvent(
      req.body,
      sig,
      process.env.STRIPE_WEBHOOK_SECRET
    );
  } catch (err) {
    console.error("❌ Signature Verification Failed:", err.message);
    return res.status(400).send(`Webhook Error: ${err.message}`);
  }

  if (event.type === "checkout.session.completed") {
    const session = event.data.object;
    const booking_id = session.metadata.booking_id;
    const user_id = session.metadata.user_id;

    try {
      const bookingDetails = await Booking.findById(booking_id);
      if (!bookingDetails) return res.status(404).send("Booking not found");

      const service = await Service.findById(bookingDetails.serviceId);
      const provider = await User.findById(bookingDetails.accepted_by);

      let newTransaction;
      await runInTransaction(async (sessionDb) => {
        const newPayment = await Payment.create(
          [
            {
              amount: service.price,
              payment_intent_id: session.payment_intent,
              created_by: user_id,
            },
          ],
          { session: sessionDb }
        );

        await Booking.findByIdAndUpdate(
          booking_id,
          { paymentId: newPayment[0]._id, is_paid: true },
          { new: true, session: sessionDb }
        );

        // 👇 Create transaction but keep as PENDING
        newTransaction = await Transactions.create(
          [
            {
              status: "pending", // waiting for provider decision
              send_by: user_id,
              received_by: provider._id,
              booking_id,
              payment_id: newPayment[0]._id,
              payment_method: "card",
            },
          ],
          { session: sessionDb }
        );
      });

      console.log("🎯 Transaction created as PENDING:", newTransaction[0]._id);
    } catch (err) {
      console.error("🔥 Error inside webhook handler:", err);
      return res.status(500).send("Internal Server Error");
    }
  }

  res.json({ received: true });
};


/**
 * Send Payment after booking is confirmed
 */
const sentPayment = async (req, res) => {
  const { booking_id, user_id, payment_intent_id } = req.body;

  try {
    if (!mongoose.Types.ObjectId.isValid(String(booking_id))) {
      return res.status(400).json({
        status: false,
        message: "Invalid booking_id",
      });
    }

    const bookingDetails = await Booking.findById(String(booking_id)).populate("userId");
    if (!bookingDetails) {
      return res.status(404).json({
        status: false,
        message: "No Booking found with this ID",
      });
    }

    if (bookingDetails.userId._id.toString() !== user_id.toString()) {
      return res.status(403).json({
        status: false,
        message: "Booking is not requested by this user",
      });
    }

    if (bookingDetails.paymentId) {
      return res.status(400).json({
        status: false,
        message: "Booking already paid",
      });
    }

    const service = await Service.findById(bookingDetails.serviceId);
    if (!service || !service.price) {
      return res.status(400).json({
        status: false,
        message: "Invalid service or price missing",
      });
    }

    const provider = await User.findById(bookingDetails.accepted_by);
    if (!provider || !provider.stripe_id) {
      return res.status(400).json({
        status: false,
        message: "Service provider not found or Stripe not linked",
      });
    }

    const paymentIntent = await stripe.paymentIntents.retrieve(payment_intent_id);
    if (!paymentIntent) {
      return res.status(404).json({
        status: false,
        message: "No Payment Intent found with this ID",
      });
    }

    let newTransaction;
    await runInTransaction(async (session) => {
      const newPayment = await Payment.create(
        [
          {
            amount: service.price,
            payment_intent_id,
            created_by: user_id,
          },
        ],
        { session }
      );

      newTransaction = await Transactions.create(
        [
          {
            status: "pending",
            send_by: user_id,
            received_by: provider._id,
            booking_id,
            payment_id: newPayment[0]._id,
            payment_method: "card",
          },
        ],
        { session }
      );

      await Booking.findByIdAndUpdate(
        booking_id,
        { paymentId: newPayment[0]._id },
        { new: true, session }
      );

      // await sendMessageToNamespace(bookingDetails.userId, {
      //   event: "booking-payment-success",
      //   payload: {
      //     message: `Payment received from ${bookingDetails.userId.full_name}`,
      //     namespaces: {
      //       provider_id: provider._id,
      //       user_id: bookingDetails.userId._id,
      //     },
      //     data: {
      //       paymentDetails: newPayment[0],
      //       provider_id: provider._id,
      //     },
      //   },
      // });
    });

    return res.status(200).json({
      status: true,
      message: "Transaction has been created successfully",
      data: newTransaction,
    });
  } catch (err) {
    console.error("sentPayment error:", err);
    return res.status(500).json({
      status: false,
      message: "Something went wrong",
      error: err.message,
    });
  }
};

/**
 * Confirm Payment → transfer money to provider
 */
const confirmPayment = async (req, res) => {
  const { booking_id } = req.body;

  try {
    if (!mongoose.Types.ObjectId.isValid(String(booking_id))) {
      return res.status(400).json({
        status: false,
        message: "Invalid booking_id",
      });
    }

    const transactionDetails = await Transactions.findOne({
      booking_id: new mongoose.Types.ObjectId(booking_id),
      status: "pending",
    }).populate([{ path: "received_by", select: "stripe_id" }]);

    if (!transactionDetails) {
      return res.status(404).json({
        status: false,
        message: "No Transaction found for this booking",
      });
    }

    const paymentDetails = await Payment.findById(transactionDetails.payment_id._id);
    if (!paymentDetails) {
      return res.status(404).json({
        status: false,
        message: "Payment details not found",
      });
    }

    // ✅ Transfer amount to provider’s Stripe
    const transfer = await stripe.transfers.create({
      amount: parseInt(paymentDetails.amount) * 100, // cents
      currency: "usd",
      destination: transactionDetails.received_by.stripe_id,
    });

    // ✅ Update Transaction Status
    await Transactions.findByIdAndUpdate(transactionDetails._id, {
      $set: { status: "completed", stripe_tr_id: transfer.id },
    });

    return res.status(200).json({
      status: true,
      message: "Payment transferred to provider successfully",
      transferId: transfer.id,
    });
  } catch (e) {
    console.error("confirmPayment error:", e);
    return res.status(500).json({
      status: false,
      message: "Something went wrong while completing the transaction",
      error: e.message,
    });
  }
};

/**
 * API Handler for sending payment manually
 */
const sentPaymentApi = async (req, res) => {
  try {
    const { booking_id, user_id, payment_intent_id } = req.body;
    if (!booking_id || !user_id || !payment_intent_id) {
      return res.status(401).json({ status: "error", message: "All fields are required" });
    }

    const response = await sentPayment(booking_id, user_id, payment_intent_id);
    return res.status(200).json({
      status: "success",
      message: "Send payment function run successfully",
      data: response,
    });
  } catch (error) {
    return res.status(500).json({
      status: "error",
      message: "Internal server error",
      trace: error.message,
    });
  }
};

const getAllPayment = async (req, res) => {
  try {
    const { user } = req;
    const fetchPayment = await Payment.find({ created_by: user._id });
    return res.status(200).send({
      success: true,
      message: "All Payment has been fetched of a User",
      data: fetchPayment,
    });
  } catch (e) {
    console.log(e);
    return res.status(400).send(e);
  }
};

const getTransactionHistory = async (req, res) => {
  try {
    const { user } = req;
    const fetchTransaction = await Transactions.aggregate([
      {
        $match: {
          $or: [{ send_by: user._id }, { received_by: user._id }],
        },
      },
      {
        $lookup: {
          from: "payments",
          localField: "payment_id",
          foreignField: "_id",
          as: "payment_id",
        },
      },
      { $unwind: { path: "$payment_id", preserveNullAndEmptyArrays: true } },
      {
        $lookup: {
          from: "users",
          localField: "send_by",
          foreignField: "_id",
          as: "send_by",
        },
      },
      { $unwind: { path: "$send_by", preserveNullAndEmptyArrays: true } },
      {
        $lookup: {
          from: "users",
          localField: "received_by",
          foreignField: "_id",
          as: "received_by",
        },
      },
      { $unwind: { path: "$received_by", preserveNullAndEmptyArrays: true } },
      {
        $lookup: {
          from: "bookings",
          localField: "booking_id",
          foreignField: "_id",
          as: "booking_id",
        },
      },
      { $unwind: { path: "$booking_id", preserveNullAndEmptyArrays: true } },
      {
        $lookup: {
          from: "services",
          localField: "booking_id.serviceId",
          foreignField: "_id",
          as: "booking_id.serviceId",
        },
      },
      { $unwind: { path: "$booking_id.serviceId", preserveNullAndEmptyArrays: true } },
      {
        $lookup: {
          from: "servicecategories",
          localField: "booking_id.serviceId.service_category",
          foreignField: "_id",
          as: "booking_id.serviceId.service_category",
        },
      },
      { $unwind: { path: "$booking_id.serviceId.service_category", preserveNullAndEmptyArrays: true } },
      {
        $project: {
          "payment_id.payment_intent_id": 0,
          "payment_id.payment_gateway": 0,
        },
      },
    ]);

    fetchTransaction.forEach((transaction) => {
      if (transaction.payment_id) {
        delete transaction.payment_id.payment_intent_id;
        delete transaction.payment_id.payment_gateway;
      }
    });

    return res.status(200).send({
      success: true,
      message: "All transactions for the user have been fetched",
      data: fetchTransaction,
    });
  } catch (e) {
    console.error(e);
    return res.status(400).send({
      success: false,
      message: "Failed to fetch transaction history",
      error: e.message,
    });
  }
};

const paymentWithWallet = async (req, res) => {
  try {
    const user = req.user;
    const { booking_id } = req.body;

    if (!booking_id) {
      return res.status(400).json({
        status: "Error",
        message: "Booking ID is required",
      });
    }

    const bookingData = await Booking.findOne({ _id: booking_id }).populate("accepted_by");
    if (!bookingData) {
      return res.status(404).json({
        status: "Error",
        message: "Booking not found",
      });
    }

    if (!bookingData.accepted_by || !bookingData.accepted_by.stripe_id) {
      return res.status(400).json({
        status: "Error",
        message: "User does not have a valid Stripe account.",
      });
    }

    // Verify if Stripe account exists
    try {
      await stripe.accounts.retrieve(bookingData.accepted_by.stripe_id);
    } catch (err) {
      return res.status(400).json({
        status: "Error",
        message: "Invalid Stripe account ID.",
      });
    }

    const userWallet = await Wallet.findOne({ user: user._id });
    if (!userWallet || userWallet.amount < bookingData.price) {
      return res.status(400).json({
        status: "Error",
        message: "Insufficient wallet balance.",
      });
    }

    userWallet.amount -= parseFloat(bookingData.price);
    await userWallet.save();

    const transfer = await stripe.transfers.create({
      amount: parseInt(bookingData.price),
      currency: "usd",
      destination: bookingData.accepted_by.stripe_id,
      transfer_group: `ride_${ride_id}`,
    });

    bookingData.is_paid = true;
    await bookingData.save();

    // * Creating Transaction for the ride
    const newTransaction = await Transactions.create({
      status: "completed",
      send_by: user._id,
      received_by: bookingData.accepted_by._id,
      booking_id: bookingData._id,
      payment_method: "wallet",
    });

    console.log("🚀 ~ sentPayment ~ newTransaction:", newTransaction);


    await sendMessageToNamespace(user, {
      event: "ride-payment-success",
      payload: {
        message: `Payment received from ${user.full_name}`,
        namespaces: {
          driver_id: bookingData?.accepted_by?._id,
          user_id: user._id,
        },
        data: {
          paymentDetails: userWallet,
          accepted_by: bookingData?.accepted_by?._id,
        },
      },
    });

    return res.status(200).json({
      status: "Success",
      message: "Payment successful",
      data: {
        booking_id: bookingData._id,
        transfer_id: transfer.id,
      },
    });
  } catch (error) {
    return res.status(500).json({
      status: "Error",
      message: "Internal server error",
      trace: error.message,
    });
  }
};


const PaymentController = {
  sentPayment,
  confirmPayment,
  sentPaymentApi,
  getAllPayment,
  getTransactionHistory,
  paymentWithWallet,
  stripeWebhook
};

export default PaymentController;
