import db from "../models/index.js";
import { Op } from "sequelize";
import { logAudit } from "../utils/auditLogger.js";
import { sendEmail } from "../utils/emailService.js";
import { baseEmailLayout } from "../utils/emailTemplates/baseLayout.js";

const {
  Merchant,
  SubscriptionPlan,
  MerchantSubscription,
  SubscriptionInvoice,
  sequelize,
} = db;

/**
 * Calculate the end date based on billing interval.
 */
const calculateEndDate = (startDate, billingInterval) => {
  const end = new Date(startDate);
  if (billingInterval === "yearly") {
    end.setFullYear(end.getFullYear() + 1);
  } else {
    end.setMonth(end.getMonth() + 1);
  }
  return end.toISOString().split("T")[0]; // YYYY-MM-DD
};

class SubscriptionController {
  // ═══════════════════════════════════════════════════════
  //  MERCHANT ENDPOINTS
  // ═══════════════════════════════════════════════════════

  /**
   * Get all available subscription plans (active only)
   * @route GET /merchants/subscriptions/plans
   */
  static async getAvailablePlans(req, res) {
    try {
      const plans = await SubscriptionPlan.findAll({
        where: { status: "active" },
        order: [["sortOrder", "ASC"], ["monthlyPrice", "ASC"]],
      });

      // Calculate the discounted annual price to display before payment
      const formattedPlans = plans.map((plan) => {
        const p = plan.toJSON();
        const mPrice = parseFloat(p.monthlyPrice);
        const discount = parseFloat(p.annualDiscountPercentage || 0);
        
        p.annualPrice = (mPrice * 12 * (1 - discount / 100)).toFixed(2);
        return p;
      });

      return res.status(200).json({ success: true, data: formattedPlans });
    } catch (error) {
      console.error("Error fetching subscription plans:", error);
      return res.status(500).json({ success: false, message: "Internal server error" });
    }
  }

  /**
   * Get the merchant's current active subscription
   * @route GET /merchants/subscriptions/me
   */
  static async getMySubscription(req, res) {
    try {
      const merchantId = req.merchant.id;

      const subscription = await MerchantSubscription.findOne({
        where: {
          merchantId,
          status: { [Op.in]: ["active", "cancelled"] },
        },
        include: [
          { model: SubscriptionPlan, as: "plan" },
          { model: SubscriptionPlan, as: "previousPlan", required: false },
        ],
        order: [["createdAt", "DESC"]],
      });

      if (!subscription) {
        return res.status(200).json({
          success: true,
          data: null,
          message: "No active subscription found. Browse available plans to get started.",
        });
      }

      return res.status(200).json({ success: true, data: subscription });
    } catch (error) {
      console.error("Error fetching subscription:", error);
      return res.status(500).json({ success: false, message: "Internal server error" });
    }
  }

  /**
   * Subscribe to a plan (wallet deduction)
   * @route POST /merchants/subscriptions
   */
  static async subscribe(req, res) {
    const t = await sequelize.transaction();
    try {
      const merchantId = req.merchant.id;
      const { planId, billingInterval } = req.body;

      if (!planId) {
        await t.rollback();
        return res.status(400).json({ success: false, message: "planId is required" });
      }

      if (!billingInterval || !["monthly", "yearly"].includes(billingInterval)) {
        await t.rollback();
        return res.status(400).json({ success: false, message: "billingInterval must be 'monthly' or 'yearly'" });
      }

      // 1. Check for existing active subscription
      const existingSub = await MerchantSubscription.findOne({
        where: { merchantId, status: "active" },
        transaction: t,
      });

      if (existingSub) {
        await t.rollback();
        return res.status(400).json({
          success: false,
          message: "You already have an active subscription. Use the change-plan endpoint to upgrade or downgrade.",
        });
      }

      // 2. Fetch the plan
      const plan = await SubscriptionPlan.findOne({
        where: { id: planId, status: "active" },
        transaction: t,
      });

      if (!plan) {
        await t.rollback();
        return res.status(404).json({ success: false, message: "Subscription plan not found or inactive" });
      }

      // 3. Check merchant balance
      const merchant = await Merchant.findByPk(merchantId, { transaction: t, lock: true });
      const monthlyPrice = parseFloat(plan.monthlyPrice);
      const discount = parseFloat(plan.annualDiscountPercentage || 0);
      const price = billingInterval === "yearly" 
        ? monthlyPrice * 12 * (1 - discount / 100)
        : monthlyPrice;

      if (parseFloat(merchant.balance) < price) {
        await t.rollback();
        return res.status(400).json({
          success: false,
          message: `Insufficient wallet balance. Plan costs ${plan.currency} ${price.toFixed(2)} but your balance is ${plan.currency} ${parseFloat(merchant.balance).toFixed(2)}.`,
        });
      }

      // 4. Deduct from wallet
      merchant.balance = parseFloat(merchant.balance) - price;
      await merchant.save({ transaction: t });

      // 5. Create subscription
      const today = new Date().toISOString().split("T")[0];
      const endDate = calculateEndDate(today, billingInterval);

      const subscription = await MerchantSubscription.create({
        merchantId,
        planId: plan.id,
        status: "active",
        startDate: today,
        endDate,
        autoRenew: true,
      }, { transaction: t });

      // 6. Create invoice
      const invoice = await SubscriptionInvoice.create({
        subscriptionId: subscription.id,
        merchantId,
        planId: plan.id,
        amount: price,
        currency: plan.currency,
        paymentMethod: "wallet",
        paymentStatus: "paid",
        billingPeriodStart: today,
        billingPeriodEnd: endDate,
        paidAt: new Date(),
      }, { transaction: t });

      logAudit({
        userId: merchantId,
        userType: "merchant",
        action: "SUBSCRIPTION_ACTIVATED",
        details: { planName: plan.name, price, endDate },
        req,
      });

      await t.commit();

      return res.status(201).json({
        success: true,
        message: `Successfully subscribed to the "${plan.name}" plan.`,
        data: {
          subscription,
          invoice,
          plan: {
            name: plan.name,
            monthlyPrice: plan.monthlyPrice,
            annualDiscountPercentage: plan.annualDiscountPercentage,
          },
          walletBalance: parseFloat(merchant.balance).toFixed(2),
        },
      });
    } catch (error) {
      await t.rollback();
      console.error("Error creating subscription:", error);
      return res.status(500).json({ success: false, message: "Internal server error" });
    }
  }

  /**
   * Upgrade or downgrade plan
   * @route PATCH /merchants/subscriptions/change-plan
   */
  static async changePlan(req, res) {
    const t = await sequelize.transaction();
    try {
      const merchantId = req.merchant.id;
      const { newPlanId, newBillingInterval } = req.body;

      if (!newPlanId || !newBillingInterval || !["monthly", "yearly"].includes(newBillingInterval)) {
        await t.rollback();
        return res.status(400).json({ success: false, message: "newPlanId and newBillingInterval ('monthly' or 'yearly') are required" });
      }

      // 1. Find current active subscription
      const currentSub = await MerchantSubscription.findOne({
        where: { merchantId, status: "active" },
        include: [{ model: SubscriptionPlan, as: "plan" }],
        transaction: t,
      });

      if (!currentSub) {
        await t.rollback();
        return res.status(400).json({
          success: false,
          message: "No active subscription to change. Subscribe first.",
        });
      }

      // 2. Fetch new plan
      const newPlan = await SubscriptionPlan.findOne({
        where: { id: newPlanId, status: "active" },
        transaction: t,
      });

      if (!newPlan) {
        await t.rollback();
        return res.status(404).json({ success: false, message: "New subscription plan not found or inactive" });
      }

      // Check what billing interval the current subscription is actually using
      const currentStartDate = new Date(currentSub.startDate);
      const currentEndDate = new Date(currentSub.endDate);
      const currentDaysDiff = Math.round((currentEndDate - currentStartDate) / (1000 * 60 * 60 * 24));
      const currentInterval = currentDaysDiff > 35 ? "yearly" : "monthly";

      if (currentSub.planId === newPlanId && currentInterval === newBillingInterval) {
        await t.rollback();
        return res.status(400).json({
          success: false,
          message: "You are already subscribed to this plan with this billing interval.",
        });
      }

      // 3. Calculate prorated cost
      const getPlanPrice = (p, interval) => {
        const mPrice = parseFloat(p.monthlyPrice);
        const discount = parseFloat(p.annualDiscountPercentage || 0);
        return interval === "yearly" ? mPrice * 12 * (1 - discount / 100) : mPrice;
      };

      const oldPrice = getPlanPrice(currentSub.plan, currentInterval);
      const newPrice = getPlanPrice(newPlan, newBillingInterval);
      const isUpgrade = newPrice > oldPrice;

      // Calculate remaining days on current plan
      const today = new Date();
      const endDate = new Date(currentSub.endDate);
      const startDate = new Date(currentSub.startDate);
      const totalDays = Math.max((endDate - startDate) / (1000 * 60 * 60 * 24), 1);
      const remainingDays = Math.max((endDate - today) / (1000 * 60 * 60 * 24), 0);
      const usedFraction = 1 - (remainingDays / totalDays);

      // Prorated credit from old plan + charge for new plan
      const unusedCredit = oldPrice * (remainingDays / totalDays);
      const proratedCost = Math.max(newPrice - unusedCredit, 0);

      // 4. Check balance for upgrade
      const merchant = await Merchant.findByPk(merchantId, { transaction: t, lock: true });

      if (isUpgrade && parseFloat(merchant.balance) < proratedCost) {
        await t.rollback();
        return res.status(400).json({
          success: false,
          message: `Insufficient balance for upgrade. Prorated cost is ${newPlan.currency} ${proratedCost.toFixed(2)}.`,
        });
      }

      // 5. Deduct prorated cost (or credit for downgrade)
      if (proratedCost > 0) {
        merchant.balance = parseFloat(merchant.balance) - proratedCost;
      } else {
        // Downgrade: credit the difference back
        merchant.balance = parseFloat(merchant.balance) + Math.abs(proratedCost);
      }
      await merchant.save({ transaction: t });

      // 6. Expire current subscription
      const previousPlanId = currentSub.planId;
      currentSub.status = "expired";
      await currentSub.save({ transaction: t });

      // 7. Create new subscription
      const newStartDate = new Date().toISOString().split("T")[0];
      const newEndDate = calculateEndDate(newStartDate, newBillingInterval);

      const newSubscription = await MerchantSubscription.create({
        merchantId,
        planId: newPlan.id,
        status: "active",
        startDate: newStartDate,
        endDate: newEndDate,
        autoRenew: true,
        previousPlanId,
      }, { transaction: t });

      // 8. Create invoice for the change
      await SubscriptionInvoice.create({
        subscriptionId: newSubscription.id,
        merchantId,
        planId: newPlan.id,
        amount: Math.abs(proratedCost),
        currency: newPlan.currency,
        paymentMethod: "wallet",
        paymentStatus: "paid",
        billingPeriodStart: newStartDate,
        billingPeriodEnd: newEndDate,
        paidAt: new Date(),
      }, { transaction: t });

      logAudit({
        userId: merchantId,
        userType: "merchant",
        action: isUpgrade ? "SUBSCRIPTION_UPGRADED" : "SUBSCRIPTION_DOWNGRADED",
        details: {
          from: currentSub.plan.name,
          to: newPlan.name,
          proratedCost: proratedCost.toFixed(2),
        },
        req,
      });

      await t.commit();

      return res.status(200).json({
        success: true,
        message: `Successfully ${isUpgrade ? "upgraded" : "downgraded"} to the "${newPlan.name}" plan.`,
        data: {
          subscription: newSubscription,
          previousPlan: currentSub.plan.name,
          newPlan: newPlan.name,
          proratedCost: proratedCost.toFixed(2),
          walletBalance: parseFloat(merchant.balance).toFixed(2),
        },
      });
    } catch (error) {
      await t.rollback();
      console.error("Error changing subscription plan:", error);
      return res.status(500).json({ success: false, message: "Internal server error" });
    }
  }

  /**
   * Cancel subscription (stays active until endDate, then expires)
   * @route POST /merchants/subscriptions/cancel
   */
  static async cancelSubscription(req, res) {
    try {
      const merchantId = req.merchant.id;

      const subscription = await MerchantSubscription.findOne({
        where: { merchantId, status: "active" },
        include: [{ model: SubscriptionPlan, as: "plan" }],
      });

      if (!subscription) {
        return res.status(400).json({
          success: false,
          message: "No active subscription to cancel.",
        });
      }

      subscription.status = "cancelled";
      subscription.autoRenew = false;
      subscription.cancelledAt = new Date();
      await subscription.save();

      logAudit({
        userId: merchantId,
        userType: "merchant",
        action: "SUBSCRIPTION_CANCELLED",
        details: { planName: subscription.plan.name, expiresOn: subscription.endDate },
        req,
      });

      return res.status(200).json({
        success: true,
        message: `Subscription cancelled. Your "${subscription.plan.name}" plan benefits remain active until ${subscription.endDate}.`,
        data: {
          planName: subscription.plan.name,
          expiresOn: subscription.endDate,
          feeDiscountPercent: subscription.plan.feeDiscountPercent,
        },
      });
    } catch (error) {
      console.error("Error cancelling subscription:", error);
      return res.status(500).json({ success: false, message: "Internal server error" });
    }
  }

  /**
   * Get subscription history
   * @route GET /merchants/subscriptions/history
   */
  static async getSubscriptionHistory(req, res) {
    try {
      const merchantId = req.merchant.id;

      const subscriptions = await MerchantSubscription.findAll({
        where: { merchantId },
        include: [
          { model: SubscriptionPlan, as: "plan", attributes: ["name", "monthlyPrice", "currency", "annualDiscountPercentage"] },
          { model: SubscriptionPlan, as: "previousPlan", required: false, attributes: ["name"] },
        ],
        order: [["createdAt", "DESC"]],
      });

      return res.status(200).json({ success: true, data: subscriptions });
    } catch (error) {
      console.error("Error fetching subscription history:", error);
      return res.status(500).json({ success: false, message: "Internal server error" });
    }
  }

  /**
   * Get subscription invoices
   * @route GET /merchants/subscriptions/invoices
   */
  static async getInvoices(req, res) {
    try {
      const merchantId = req.merchant.id;

      const invoices = await SubscriptionInvoice.findAll({
        where: { merchantId },
        include: [
          { model: SubscriptionPlan, as: "plan", attributes: ["name"] },
        ],
        order: [["createdAt", "DESC"]],
      });

      return res.status(200).json({ success: true, data: invoices });
    } catch (error) {
      console.error("Error fetching invoices:", error);
      return res.status(500).json({ success: false, message: "Internal server error" });
    }
  }
}

export default SubscriptionController;
