// backend/controllers/subscriptionController.js

const Subscription = require("../models/subscriptionModel");
const Plan = require("../models/planModel");

// A helper function to calculate the duration of a subscription in milliseconds
const getSubscriptionDuration = (sub) => {
  const start = new Date(sub.start_date);
  const end = new Date(sub.end_date);
  // Add one day to the duration because dates are inclusive
  return end.getTime() - start.getTime() + 24 * 60 * 60 * 1000;
};

/**
 * @desc    Create a new subscription for an institution (With Pending/Stacking Logic)
 * @route   POST /api/subscriptions
 * @access  Private (Super Admin)
 */
exports.createSubscription = async (req, res) => {
  try {
    const { institution_id, plan_id } = req.body;
    if (!institution_id || !plan_id) {
      return res
        .status(400)
        .json({ message: "Institution ID and Plan ID are required." });
    }

    const plan = await Plan.findById(plan_id);
    if (!plan) {
      return res
        .status(404)
        .json({ message: "The selected plan was not found." });
    }

    const existingActiveSub = await Subscription.findActiveByInstitutionId(
      institution_id
    );

    let startDate = new Date();
    let status = "active"; // Default status is active

    if (existingActiveSub) {
      // If an active sub exists, the new one starts after it ends and is 'pending'
      const dayAfterEndDate = new Date(existingActiveSub.end_date);
      dayAfterEndDate.setDate(dayAfterEndDate.getDate() + 1);
      startDate = dayAfterEndDate;
      status = "pending"; // CHANGED: Set status to pending
    }

    const endDate = new Date(startDate);
    // Calculate end date based on the plan's duration
    switch (plan.billing_interval) {
      case "day":
        endDate.setDate(endDate.getDate() + plan.interval_count);
        break;
      case "week":
        endDate.setDate(endDate.getDate() + plan.interval_count * 7);
        break;
      case "month":
        endDate.setMonth(endDate.getMonth() + plan.interval_count);
        break;
      case "year":
        endDate.setFullYear(endDate.getFullYear() + plan.interval_count);
        break;
      default:
        return res
          .status(500)
          .json({ message: "Invalid billing interval on plan." });
    }
    endDate.setDate(endDate.getDate() - 1); // Subtract one day to make dates inclusive

    const newSubData = {
      institution_id,
      plan_id,
      start_date: startDate.toISOString().split("T")[0],
      end_date: endDate.toISOString().split("T")[0],
      status, // ADDED: Use the determined status
    };

    const newSub = await Subscription.create(newSubData);
    res.status(201).json({
      message: "Subscription created successfully.",
      subscription: newSub,
    });
  } catch (error) {
    res
      .status(500)
      .json({ message: "Error creating subscription.", error: error.message });
  }
};

/**
 * @desc    Update a subscription (with cascading logic for pending subs)
 * @route   PUT /api/subscriptions/:id
 * @access  Private (Super Admin)
 */
exports.updateSubscription = async (req, res) => {
  try {
    const { id } = req.params;
    // The only valid inputs are 'status' (for cancellation) or 'plan_id'
    const { status, plan_id } = req.body;

    const subToUpdate = await Subscription.findById(id);
    if (!subToUpdate) {
      return res.status(404).json({ message: "Subscription not found." });
    }

    const { institution_id } = subToUpdate;
    const pendingSub = await Subscription.findPendingByInstitutionId(
      institution_id
    );

    // --- SCENARIO 1: CANCELLING a subscription ---
    if (status === "cancelled") {
      // This action is only valid for 'active' or 'pending' subscriptions
      if (subToUpdate.status !== "active" && subToUpdate.status !== "pending") {
        return res
          .status(400)
          .json({
            message: `Cannot cancel a subscription with status '${subToUpdate.status}'.`,
          });
      }

      // Set end date to yesterday for active subs, or just delete pending ones
      if (subToUpdate.status === "active") {
        const yesterday = new Date();
        yesterday.setDate(yesterday.getDate() - 1);
        await Subscription.update(id, {
          status: "cancelled",
          end_date: yesterday.toISOString().split("T")[0],
        });

        // If an active sub was cancelled, activate the next pending one
        if (pendingSub) {
          const newStartDate = new Date(); // Starts today
          const duration = getSubscriptionDuration(pendingSub);
          const newEndDate = new Date(
            newStartDate.getTime() + duration - 24 * 60 * 60 * 1000
          );
          await Subscription.update(pendingSub.id, {
            status: "active",
            start_date: newStartDate.toISOString().split("T")[0],
            end_date: newEndDate.toISOString().split("T")[0],
          });
        }
      } else {
        // It's a pending subscription
        // For pending subs, 'cancel' means 'delete'
        await Subscription.delete(id); // Assuming you have a hard delete function for this case
      }
    }

    // --- SCENARIO 2: CHANGING a subscription's PLAN ---
    else if (plan_id) {
      const newPlan = await Plan.findById(plan_id);
      if (!newPlan)
        return res
          .status(404)
          .json({ message: "The selected new plan was not found." });

      let finalUpdatePayload = { plan_id: newPlan.id };
      let finalEndDateFormatted = null;

      if (subToUpdate.status === "active") {
        // Logic for updating an ACTIVE subscription
        const originalStartDate = new Date(subToUpdate.start_date);
        const idealEndDate = new Date(originalStartDate);

        // Calculate ideal end date from original start date + new plan duration
        switch (newPlan.billing_interval) {
          case "day":
            idealEndDate.setDate(
              idealEndDate.getDate() + newPlan.interval_count
            );
            break;
          case "week":
            idealEndDate.setDate(
              idealEndDate.getDate() + newPlan.interval_count * 7
            );
            break;
          case "month":
            idealEndDate.setMonth(
              idealEndDate.getMonth() + newPlan.interval_count
            );
            break;
          case "year":
            idealEndDate.setFullYear(
              idealEndDate.getFullYear() + newPlan.interval_count
            );
            break;
        }
        idealEndDate.setDate(idealEndDate.getDate() - 1); // Make it inclusive

        // HARD STOP: If the calculated end date is in the past, return an error.
        const today = new Date();
        today.setHours(0, 0, 0, 0); // Normalize for accurate comparison
        if (idealEndDate < today) {
          return res.status(400).json({
            message:
              "The new plan is too short and would have already expired. Please choose a plan with a longer duration.",
          });
        }

        finalEndDateFormatted = idealEndDate.toISOString().split("T")[0];
        finalUpdatePayload.end_date = finalEndDateFormatted;
      } else if (subToUpdate.status !== "pending") {
        return res
          .status(400)
          .json({
            message: `Cannot change the plan of a subscription with status '${subToUpdate.status}'.`,
          });
      }
      // If status is 'pending', we only need to update the plan_id, which is already in the payload.

      // Perform the main update
      await Subscription.update(id, finalUpdatePayload);

      // If an active sub's end date was changed, we must adjust the subsequent pending one
      if (subToUpdate.status === "active" && pendingSub) {
        const newPendingStartDate = new Date(finalEndDateFormatted);
        newPendingStartDate.setDate(newPendingStartDate.getDate() + 1);

        const duration = getSubscriptionDuration(pendingSub);
        const newPendingEndDate = new Date(
          newPendingStartDate.getTime() + duration - 24 * 60 * 60 * 1000
        );

        await Subscription.update(pendingSub.id, {
          start_date: newPendingStartDate.toISOString().split("T")[0],
          end_date: newPendingEndDate.toISOString().split("T")[0],
        });
      }
    } else {
      return res
        .status(400)
        .json({
          message:
            "No valid update operation provided (e.g., status or plan_id).",
        });
    }

    res.status(200).json({ message: "Subscription updated successfully." });
  } catch (error) {
    // Ensure to log the actual error on the server for debugging
    console.error("Error in updateSubscription:", error);
    res
      .status(500)
      .json({ message: "Error updating subscription.", error: error.message });
  }
};

/**
 * @desc    Get all subscriptions for a specific institution
 * @route   GET /api/subscriptions/institution/:id
 * @access  Private (Super Admin)
 */
exports.getSubscriptionsForInstitution = async (req, res) => {
  try {
    const subscriptions = await Subscription.findAllByInstitutionId(
      req.params.id
    );
    res.status(200).json(subscriptions);
  } catch (error) {
    res
      .status(500)
      .json({ message: "Error fetching subscriptions.", error: error.message });
  }
};

// =========================================================
// == For Institution Admins to View Their Subscription   ==
// =========================================================

/**
 * @desc    Get the current active subscription for the user's institution
 * @route   GET /api/subscriptions/my-subscription
 * @access  Private (Admin)
 */
exports.getMyActiveSubscription = async (req, res) => {
  try {
    const { institution_id } = req.user;
    if (!institution_id) {
      return res
        .status(403)
        .json({ message: "User is not associated with an institution." });
    }
    const subscription = await Subscription.findActiveByInstitutionId(
      institution_id
    );
    if (!subscription) {
      return res.status(404).json({
        message: "No active subscription found for your institution.",
      });
    }
    res.status(200).json(subscription);
  } catch (error) {
    res.status(500).json({
      message: "Error fetching subscription details.",
      error: error.message,
    });
  }
};
