import { Settings } from '@/models/Settings';
import { decryptSecret } from './security';
import { Order } from '@/models/Order';
import { Course } from '@/models/Course';
import { Subscription } from '@/models/Subscription';
import { CoursePurchaseHistory } from '@/models/CoursePurchaseHistory';
import { Student } from '@/models/Student';

export interface PaypalConfig {
  mode: 'test' | 'live';
  isEnabled: boolean;
  clientId: string;
  clientSecret: string;
  baseUrl: string;
}

/**
 * Dynamically resolves active PayPal credentials from MongoDB Settings table.
 */
export async function getPaypalConfig(): Promise<PaypalConfig> {
  const settings = await Settings.getSettings();
  const mode = (settings.paypal?.mode as 'test' | 'live') || 'test';
  const isEnabled = settings.paypal?.isEnabled ?? true;

  let clientId = '';
  let secretKey = '';

  if (mode === 'live') {
    clientId =
      settings.paypal?.liveClientId ||
      settings.paypal?.clientId ||
      process.env.PAYPAL_LIVE_CLIENT_ID ||
      '';
    const rawSecret =
      // settings.paypal?.liveClientSecretEncrypted ||
      settings.paypal?.clientSecretEncrypted ||
      process.env.PAYPAL_LIVE_CLIENT_SECRET ||
      '';
    secretKey = rawSecret ? decryptSecret(rawSecret) : '';
  } else {
    clientId =
      settings.paypal?.testClientId ||
      settings.paypal?.clientId ||
      process.env.PAYPAL_TEST_CLIENT_ID ||
      process.env.PAYPAL_CLIENT_ID ||
      '';
    const rawSecret =
      settings.paypal?.testClientSecretEncrypted ||
      settings.paypal?.clientSecretEncrypted ||
      process.env.PAYPAL_TEST_CLIENT_SECRET ||
      process.env.PAYPAL_CLIENT_SECRET ||
      '';
    secretKey = rawSecret ? decryptSecret(rawSecret) : '';
  }

  const baseUrl =
    mode === 'live'
      ? 'https://api-m.paypal.com'
      : 'https://api-m.sandbox.paypal.com';

  return {
    mode,
    isEnabled,
    clientId,
    clientSecret: secretKey,
    baseUrl,
  };
}

/**
 * Fetches an OAuth2 access token from PayPal REST API.
 */
export async function getPaypalAccessToken(): Promise<{ accessToken: string; config: PaypalConfig }> {
  const config = await getPaypalConfig();

  if (!config.clientId || !config.clientSecret) {
    throw new Error(
      `PayPal credentials (Client ID / Secret) are missing in system settings for ${config.mode.toUpperCase()} mode.`
    );
  }

  const auth = Buffer.from(`${config.clientId}:${config.clientSecret}`).toString('base64');
  const res = await fetch(`${config.baseUrl}/v1/oauth2/token`, {
    method: 'POST',
    headers: {
      Authorization: `Basic ${auth}`,
      'Content-Type': 'application/x-www-form-urlencoded',
    },
    body: 'grant_type=client_credentials',
  });

  const data = await res.json();
  if (!res.ok || !data.access_token) {
    throw new Error(data.error_description || data.message || 'Failed to authenticate with PayPal API');
  }

  return { accessToken: data.access_token, config };
}

/**
 * Creates a PayPal Checkout Order via PayPal API.
 * Does NOT store an order in database yet.
 */
export async function createPaypalOrder({
  amount,
  currency = 'EUR',
  courseTitle = 'German Course',
}: {
  amount: number;
  currency?: string;
  courseTitle?: string;
}) {
  const { accessToken, config } = await getPaypalAccessToken();

  const res = await fetch(`${config.baseUrl}/v2/checkout/orders`, {
    method: 'POST',
    headers: {
      Authorization: `Bearer ${accessToken}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      intent: 'CAPTURE',
      purchase_units: [
        {
          amount: {
            currency_code: currency.toUpperCase(),
            value: Number(amount).toFixed(2),
          },
          description: `ApkaGermanSchool - ${courseTitle}`,
        },
      ],
    }),
  });

  const paypalOrder = await res.json();
  if (!res.ok || !paypalOrder.id) {
    throw new Error(paypalOrder.message || 'Failed to create PayPal order');
  }

  return {
    paypalOrderId: paypalOrder.id,
    clientId: config.clientId,
    mode: config.mode,
  };
}

/**
 * Captures a PayPal order after student approves payment,
 * creates Order record in MongoDB with status 'succeeded',
 * and grants Course Subscription to student.
 */
export async function capturePaypalOrderAndFulfill({
  paypalOrderId,
  studentId,
  email,
  studentName,
  whatsappNumber,
  courseId,
  lessons = [],
  amount,
}: {
  paypalOrderId: string;
  studentId?: string;
  email?: string;
  studentName?: string;
  whatsappNumber?: string;
  courseId: string;
  lessons?: string[];
  amount: number;
}) {
  const { accessToken, config } = await getPaypalAccessToken();

  // 1. Capture payment via PayPal API
  const captureRes = await fetch(`${config.baseUrl}/v2/checkout/orders/${paypalOrderId}/capture`, {
    method: 'POST',
    headers: {
      Authorization: `Bearer ${accessToken}`,
      'Content-Type': 'application/json',
    },
  });

  const captureData = await captureRes.json();
  const status = captureData.status;

  if (!captureRes.ok || (status !== 'COMPLETED' && status !== 'APPROVED')) {
    throw new Error(
      captureData.message ||
      captureData.details?.[0]?.description ||
      `PayPal payment capture failed with status: ${status}`
    );
  }

  // 2. Identify or create Student account
  let targetStudentId = studentId;

  if (!targetStudentId) {
    if (!email) {
      throw new Error('Student email is required for registration.');
    }

    const existingStudent = await Student.findOne({ email: email.toLowerCase().trim() });
    if (existingStudent) {
      targetStudentId = existingStudent._id.toString();
      if (studentName && existingStudent.name !== studentName) {
        existingStudent.name = studentName;
        await existingStudent.save();
      }
    } else {
      const newStudent = await Student.create({
        name: studentName || email.split('@')[0],
        email: email.toLowerCase().trim(),
        password: `Student@${Math.floor(100000 + Math.random() * 900000)}`,
        phoneNumber: whatsappNumber || '',
        status: 'active',
      });
      targetStudentId = newStudent._id.toString();
    }
  }

  // 3. Create Order in MongoDB ONLY NOW after payment is verified successful
  const targetCourse = await Course.findById(courseId);
  if (!targetCourse) {
    throw new Error('Selected course was not found.');
  }

  const captureId =
    captureData.purchase_units?.[0]?.payments?.captures?.[0]?.id || paypalOrderId;

  let order = await Order.findOne({ gatewayTransactionId: captureId });
  if (!order) {
    order = await Order.create({
      student: targetStudentId,
      courseId: targetCourse._id,
      lessons: Array.isArray(lessons) ? lessons : [],
      gateway: 'paypal',
      gatewayTransactionId: captureId,
      gatewayResponse: captureData,
      amount: Number(amount),
      currency: 'EUR',
      paymentRequired: true,
      status: 'succeeded',
      initiatedAt: new Date(),
      confirmedAt: new Date(),
    });
  }

  const settings = await Settings.getSettings();

  // 4. Create CoursePurchaseHistory
  const existingHistory = await CoursePurchaseHistory.findOne({ order: order._id });
  if (!existingHistory) {
    await CoursePurchaseHistory.create({
      student: order.student,
      order: order._id,
      courseId: order.courseId,
      lessons: order.lessons || [],
      amountPaid: order.amount,
      currency: order.currency || 'EUR',
      discountApplied: 0,
      paymentMethod: 'paypal',
      transactionId: captureId,
      paymentStatus: 'completed',
      purchasedAt: new Date(),
    });
  }

  // 5. Grant active Course Subscription
  const validityMonths = targetCourse.validityMonths || settings.defaultCourseExpiryMonths || 4;
  const startDate = new Date();
  const expiryDate = new Date();
  expiryDate.setMonth(expiryDate.getMonth() + validityMonths);

  const existingSub = await Subscription.findOne({
    student: order.student,
    course: targetCourse._id,
  });

  if (existingSub) {
    existingSub.expiryDate = expiryDate;
    existingSub.status = 'active';

    if (Array.isArray(order.lessons) && order.lessons.length > 0) {
      const mergedLessons = Array.from(
        new Set([
          ...(existingSub.lessons || []).map((id) => id.toString()),
          ...order.lessons.map((id) => id.toString()),
        ])
      );
      existingSub.lessons = mergedLessons as any;
    }
    await existingSub.save();
  } else {
    await Subscription.create({
      student: order.student,
      course: targetCourse._id,
      lessons: order.lessons || [],
      order: order._id,
      startDate,
      expiryDate,
      status: 'active',
      completedChapters: [],
      completedPercent: 0,
      lastWatchedPositionSeconds: 0,
    });
  }

  return order;
}
