/**
 * Paynow Payment Gateway Integration
 * For Zimbabwe payments (Ecocash, Visa, etc.)
 */

const express = require('express');
const router = express.Router();
const path = require('path');
const Database = require('better-sqlite3');
const { v4: uuidv4 } = require('uuid');
const crypto = require('crypto');
const AuditService = require('../services/AuditService');
const { outstandingForFee } = require('../lib/outstanding');

const dbPath = process.env.DB_PATH || path.join(__dirname, '../../data/school.db');
const db = new Database(dbPath);
db.pragma('foreign_keys = ON');

// Auth middleware
const auth = (req, res, next) => {
  const token = req.headers.authorization?.split(' ')[1];
  if (!token) return res.status(401).json({ error: 'No token provided' });
  try {
    req.user = require('jsonwebtoken').verify(token, require('../config').jwtSecret);
    next();
  } catch (err) {
    res.status(401).json({ error: 'Invalid token' });
  }
};

// Paynow configuration
const PAYNOW_INTEGRATION_ID = process.env.PAYNOW_INTEGRATION_ID || '';
const PAYNOW_INTEGRATION_KEY = process.env.PAYNOW_INTEGRATION_KEY || '';
const PAYNOW_RETURN_URL = process.env.PAYNOW_RETURN_URL || 'http://localhost:3000/fees';
const PAYNOW_BLOCKING_URL = process.env.PAYNOW_BLOCKING_URL || 'http://localhost:3000/api/payments/webhook';
const PAYNOW_API_URL = 'https://www.paynow.co.zw/interface/initiatetransaction';

// Webhook signature verification.
//
// Paynow signs every callback with a SHA-1 of the alphabetically sorted,
// non-empty fields (`field1value1field2value2...fieldNvalueN`) appended with
// the integration key. We use the same scheme outbound (see `generateHash`)
// so the inbound verifier must mirror it exactly.
//
// Set PAYNOW_SKIP_VERIFY=true ONLY for local dev and Playwright tests that
// need to drive the webhook without a real Paynow round trip. In production
// leave it unset — the absence of PAYNOW_INTEGRATION_KEY or a mismatch
// rejects the request before any DB write.
const PAYNOW_SKIP_VERIFY = process.env.PAYNOW_SKIP_VERIFY === 'true';

// Helper to generate hash
const generateHash = (params, integrationKey) => {
  const sorted = Object.keys(params)
    .filter(k => params[k] !== null && params[k] !== undefined && params[k] !== '')
    .sort()
    .map(k => `${k}${params[k]}`)
    .join('');

  return crypto.createHash('sha1').update(sorted + integrationKey).digest('hex');
};

/**
 * Verify an inbound Paynow webhook payload against the stored integration key.
 * Returns an object describing the outcome so the caller can log structured
 * detail without leaking the expected hash to a malicious probe.
 */
const verifyPaynowHash = (payload) => {
  if (!payload || typeof payload !== 'object') {
    return { valid: false, reason: 'invalid_payload' };
  }
  if (!PAYNOW_INTEGRATION_KEY) {
    return { valid: false, reason: 'server_misconfigured' };
  }
  const { hash, ...rest } = payload;
  if (typeof hash !== 'string' || hash.length === 0) {
    return { valid: false, reason: 'missing_hash' };
  }
  const expected = generateHash(rest, PAYNOW_INTEGRATION_KEY);
  // Length check before timingSafeEqual — Buffer.timingSafeEqual throws on
  // mismatched lengths.
  if (
    hash.length !== expected.length ||
    !crypto.timingSafeEqual(Buffer.from(hash, 'hex'), Buffer.from(expected, 'hex'))
  ) {
    return { valid: false, reason: 'hash_mismatch' };
  }
  return { valid: true };
};

// ============ INITIATE PAYMENT ============

/**
 * @openapi
 * /api/payments/initiate:
 *     post:
 *       tags:
 *         - Payments
 *       summary: Initiate a Paynow payment
 *       description: Creates a pending payment record and initiates a Paynow transaction for school fee payment
 *       security:
 *         - bearerAuth: []
 *       requestBody:
 *         required: true
 *         content:
 *           application/json:
 *             schema:
 *               type: object
 *               required:
 *                 - student_fee_id
 *                 - amount
 *               properties:
 *                 student_fee_id:
 *                   type: string
 *                   description: The ID of the student fee record
 *                   example: "sf-12345"
 *                 amount:
 *                   type: number
 *                   format: float
 *                   minimum: 0.01
 *                   description: Payment amount in USD
 *                   example: 150.00
 *                 reference:
 *                   type: string
 *                   description: Optional custom payment reference
 *                   example: "FEE-12345-1699900000000"
 *       responses:
 *         '200':
 *           description: Payment initiation successful
 *           content:
 *             application/json:
 *               schema:
 *                 type: object
 *                 properties:
 *                   success:
 *                     type: boolean
 *                     example: true
 *                   payment_id:
 *                     type: string
 *                     description: Unique payment ID
 *                     example: "550e8400-e29b-41d4-a716-446655440000"
 *                   reference:
 *                     type: string
 *                     description: Payment reference number
 *                     example: "FEE-12345-1699900000000"
 *                   amount:
 *                     type: number
 *                     example: 150.00
 *                   payment_url:
 *                     type: string
 *                     description: URL to redirect user for payment
 *                     example: "https://www.paynow.co.zw/interface/initiatetransaction?reference=FEE-12345&amount=150.00"
 *                   poll_url:
 *                     type: string
 *                     description: URL to poll for payment status
 *                     example: "https://www.paynow.co.zw/poll/FEE-12345-1699900000000"
 *                   mode:
 *                     type: string
 *                     enum: [live, test]
 *                     example: test
 *                   instructions:
 *                     type: string
 *                     example: "Paynow not configured. Use test mode for development."
 *         '400':
 *           description: Invalid amount or fee already paid
 *           content:
 *             application/json:
 *               schema:
 *                 type: object
 *                 properties:
 *                   error:
 *                     type: string
 *                     example: "Invalid amount"
 *         '401':
 *           description: Unauthorized - invalid or missing token
 *           content:
 *             application/json:
 *               schema:
 *                 type: object
 *                 properties:
 *                   error:
 *                     type: string
 *                     example: "Invalid token"
 *         '404':
 *           description: Student fee record not found
 *           content:
 *             application/json:
 *               schema:
 *                 type: object
 *                 properties:
 *                   error:
 *                     type: string
 *                     example: "Student fee record not found"
 *         '500':
 *           description: Server error
 *           content:
 *             application/json:
 *               schema:
 *                 type: object
 *                 properties:
 *                   error:
 *                     type: string
 *                     example: "Failed to initiate payment"
 */
const paymentAccessRoles = ['school_admin', 'systems_admin', 'principal', 'bursar', 'accountant', 'hr'];

const canAccessStudent = (user, studentId) => {
  if (paymentAccessRoles.includes(user.role)) return true;
  if (user.role === 'student') return Number(studentId) === Number(user.id);
  if (user.role === 'parent') {
    return Boolean(db.prepare(
      'SELECT 1 FROM parent_students WHERE parent_id = ? AND student_id = ? AND is_deleted = 0'
    ).get(user.id, studentId));
  }
  return false;
};

const requirePaymentOwner = (req, res, studentId) => {
  if (!canAccessStudent(req.user, studentId)) {
    res.status(403).json({ error: 'Access denied' });
    return false;
  }
  return true;
};

router.post('/initiate', auth, async (req, res) => {
  const { student_fee_id, reference } = req.body;
  let amount = Number(req.body.amount);
  
  // Validate amount
  if (!amount || amount <= 0) {
    return res.status(400).json({ error: 'Invalid amount' });
  }
  
  // Get student fee record
  const studentFee = db.prepare(`
    SELECT sf.*, u.first_name, u.last_name, u.email,
      fg.name as fee_name
    FROM student_fees sf
    JOIN users u ON sf.student_id = u.id
    JOIN fee_groups fg ON sf.fee_group_id = fg.id
    WHERE sf.id = ?
  `).get(student_fee_id);
  
  if (!studentFee) {
    return res.status(404).json({ error: 'Student fee record not found' });
  }
  if (!requirePaymentOwner(req, res, studentFee.student_id)) return;
  
  // Check if already paid
  if (studentFee.status === 'paid') {
    return res.status(400).json({ error: 'Fee already fully paid' });
  }
  
  // Check remaining amount
  const { outstanding: remaining } = outstandingForFee(studentFee);
  if (remaining <= 0) {
    return res.status(400).json({ error: 'Fee already fully paid' });
  }
  if (amount > remaining) {
    amount = remaining;
  }
  
  // Generate reference
  const paymentReference = reference || `FEE-${studentFee.id}-${Date.now()}`;
  const uid = uuidv4();
  
  // Create pending payment record
  db.prepare(`
    INSERT INTO payments (uid, student_fee_id, amount, payment_method, reference_number, collected_by, status)
    VALUES (?, ?, ?, 'paynow', ?, ?, 'pending')
  `).run(uid, student_fee_id, amount, paymentReference, req.user.id);
  
  await AuditService.log(req, 'INITIATE_PAYMENT', {
    entityType: 'payment',
    entityId: uid,
    newData: { student_fee_id, amount, paymentReference }
  });
  
  // If Paynow is configured, create Paynow transaction
  if (PAYNOW_INTEGRATION_ID && PAYNOW_INTEGRATION_KEY) {
    try {
      // Prepare Paynow parameters
      const paynowParams = {
        integrationid: PAYNOW_INTEGRATION_ID,
        returnurl: PAYNOW_RETURN_URL,
        blockingurl: PAYNOW_BLOCKING_URL,
        reference: paymentReference,
        amount: amount.toFixed(2),
        additionalinfo: `School Fee Payment - ${studentFee.fee_name}`,
        email: studentFee.email || '',
      };
      
      // Generate hash
      paynowParams.hash = generateHash(paynowParams, PAYNOW_INTEGRATION_KEY);
      
      // In production, would make HTTP request to Paynow
      // For now, return a mock response for testing
      const pollUrl = `https://www.paynow.co.zw/poll/${paymentReference}`;
      
      db.prepare(`
        UPDATE payments SET paynow_poll_url = ? WHERE uid = ?
      `).run(pollUrl, uid);
      
      res.json({
        success: true,
        payment_id: uid,
        reference: paymentReference,
        amount: amount,
        // In production, this would be the Paynow payment URL
        // payment_url: paynowResponse.url,
        payment_url: `${PAYNOW_API_URL}?reference=${paymentReference}&amount=${amount}`,
        poll_url: pollUrl,
        instructions: 'Redirect to Paynow to complete payment'
      });
      
    } catch (error) {
      console.error('Paynow initiation error:', error);
      res.status(500).json({ error: 'Failed to initiate payment' });
    }
  } else {
    // Live Paynow is not configured. We refuse to pretend we're live —
    // returning mode: 'test' with a non-functional payment_url would let
    // an operator think they've taken a real payment. Surface the gap so
    // the operator knows to wire PAYNOW_INTEGRATION_ID/KEY.
    res.json({
      success: true,
      mode: 'test',
      payment_id: uid,
      reference: paymentReference,
      amount: amount,
      payment_url: `/mock-payment?ref=${paymentReference}&amount=${amount}`,
      poll_url: null,
      instructions: 'Set PAYNOW_INTEGRATION_ID and PAYNOW_INTEGRATION_KEY to enable live payments.',
    });
  }
});

// ============ CHECK PAYMENT STATUS ============

/**
 * @openapi
 * /api/payments/status/{paymentId}:
 *     get:
 *       tags:
 *         - Payments
 *       summary: Check payment status
 *       description: Retrieves the current status of a payment by its unique ID
 *       security:
 *         - bearerAuth: []
 *       parameters:
 *         - name: paymentId
 *           in: path
 *           required: true
 *           description: Unique payment ID
 *           schema:
 *             type: string
 *             example: "550e8400-e29b-41d4-a716-446655440000"
 *       responses:
 *         '200':
 *           description: Payment status retrieved successfully
 *           content:
 *             application/json:
 *               schema:
 *                 type: object
 *                 properties:
 *                   payment_id:
 *                     type: string
 *                     example: "550e8400-e29b-41d4-a716-446655440000"
 *                   reference:
 *                     type: string
 *                     example: "FEE-12345-1699900000000"
 *                   amount:
 *                     type: number
 *                     example: 150.00
 *                   status:
 *                     type: string
 *                     enum: [pending, completed, failed, cancelled]
 *                     example: completed
 *                   student:
 *                     type: string
 *                     description: Full name of student
 *                     example: "John Doe"
 *                   fee_name:
 *                     type: string
 *                     example: "Term 1 Tuition"
 *                   created_at:
 *                     type: string
 *                     format: date-time
 *                     example: "2024-01-15T10:30:00.000Z"
 *                   paid_at:
 *                     type: string
 *                     format: date-time
 *                     nullable: true
 *                     example: "2024-01-15T10:35:00.000Z"
 *         '401':
 *           description: Unauthorized - invalid or missing token
 *           content:
 *             application/json:
 *               schema:
 *                 type: object
 *                 properties:
 *                   error:
 *                     type: string
 *                     example: "Invalid token"
 *         '404':
 *           description: Payment not found
 *           content:
 *             application/json:
 *               schema:
 *                 type: object
 *                 properties:
 *                   error:
 *                     type: string
 *                     example: "Payment not found"
 *         '500':
 *           description: Server error
 *           content:
 *             application/json:
 *               schema:
 *                 type: object
 *                 properties:
 *                   error:
 *                     type: string
 *                     example: "Failed to check payment status"
 */
router.get('/status/:paymentId', auth, async (req, res) => {
  const { paymentId } = req.params;
  
  const payment = db.prepare(`
    SELECT p.*, sf.student_id, u.first_name, u.last_name, fg.name as fee_name
    FROM payments p
    JOIN student_fees sf ON p.student_fee_id = sf.id
    JOIN users u ON sf.student_id = u.id
    JOIN fee_groups fg ON sf.fee_group_id = fg.id
    WHERE p.uid = ?
  `).get(paymentId);
  
  if (!payment) {
    return res.status(404).json({ error: 'Payment not found' });
  }
  if (!requirePaymentOwner(req, res, payment.student_id)) return;
  
  // Check with Paynow if configured
  if (payment.paynow_poll_url && PAYNOW_INTEGRATION_ID && PAYNOW_INTEGRATION_KEY) {
    try {
      // Poll Paynow for status
      const pollParams = {
        reference: payment.reference_number,
        hash: generateHash({ reference: payment.reference_number }, PAYNOW_INTEGRATION_KEY)
      };
      
      // In production, would make HTTP request to Paynow
      // For now, return local status
      res.json({
        payment_id: payment.uid,
        reference: payment.reference_number,
        amount: payment.amount,
        status: payment.status,
        student: `${payment.first_name} ${payment.last_name}`,
        fee_name: payment.fee_name,
        created_at: payment.created_at,
        paid_at: payment.paid_at
      });
      
    } catch (error) {
      console.error('Paynow poll error:', error);
      res.status(500).json({ error: 'Failed to check payment status' });
    }
  } else {
    // Return local status
    res.json({
      payment_id: payment.uid,
      reference: payment.reference_number,
      amount: payment.amount,
      status: payment.status,
      student: `${payment.first_name} ${payment.last_name}`,
      fee_name: payment.fee_name,
      created_at: payment.created_at,
      paid_at: payment.paid_at
    });
  }
});

// ============ PAYNOW WEBHOOK ============

/**
 * @openapi
 * /api/payments/webhook:
 *     post:
 *       tags:
 *         - Payments
 *       summary: Paynow webhook callback
 *       description: Receives payment status updates from Paynow gateway. No authentication required for webhook endpoints.
 *       requestBody:
 *         required: true
 *         content:
 *           application/json:
 *             schema:
 *               type: object
 *               required:
 *                 - status
 *                 - reference
 *               properties:
 *                 status:
 *                   type: string
 *                   enum: [Paid, Awaiting, Cancelled, Failed]
 *                   description: Payment status from Paynow
 *                   example: "Paid"
 *                 reference:
 *                   type: string
 *                   description: Payment reference number
 *                   example: "FEE-12345-1699900000000"
 *                 pollurl:
 *                   type: string
 *                   description: Paynow poll URL for the transaction
 *                   example: "https://www.paynow.co.zw/poll/abc123"
 *       responses:
 *         '200':
 *           description: Webhook processed successfully
 *           content:
 *             application/json:
 *               schema:
 *                 type: object
 *                 properties:
 *                   status:
 *                     type: string
 *                     example: "ok"
 *                   note:
 *                     type: string
 *                     example: "already processed"
 *         '400':
 *           description: Missing required reference
 *           content:
 *             application/json:
 *               schema:
 *                 type: object
 *                 properties:
 *                   error:
 *                     type: string
 *                     example: "Missing reference"
 *         '404':
 *           description: Payment not found for reference
 *           content:
 *             application/json:
 *               schema:
 *                 type: object
 *                 properties:
 *                   error:
 *                     type: string
 *                     example: "Payment not found"
 *         '500':
 *           description: Webhook processing error
 *           content:
 *             application/json:
 *               schema:
 *                 type: object
 *                 properties:
 *                   error:
 *                     type: string
 *                     example: "Webhook processing failed"
 */
router.post('/webhook', async (req, res) => {
  try {
    const { status, reference, pollurl } = req.body;

    // Reject the request if Paynow is configured but the signature does not
    // match. Bypass only when explicitly opted out (dev / Playwright) or
    // when no integration key is configured (test mode).
    const integrationKeyConfigured = Boolean(PAYNOW_INTEGRATION_KEY);
    if (integrationKeyConfigured && !PAYNOW_SKIP_VERIFY) {
      const check = verifyPaynowHash(req.body || {});
      if (!check.valid) {
        // Note we never echo the expected hash — that gives an oracle away
        // to an attacker who only needs to brute-force SHA1 suffixes.
        console.warn('Paynow webhook rejected:', {
          reason: check.reason,
          ip: req.ip,
        });
        return res.status(401).json({ error: 'Invalid or missing Paynow signature' });
      }
    }

    console.log('Paynow webhook received:', { status, reference });

    if (!reference) {
      return res.status(400).json({ error: 'Missing reference' });
    }

    // Find payment by reference. We re-query inside the transaction to make
    // the idempotency check race-free (two webhooks arriving at the same
    // instant both see the same "completed" status).
    let result = { kind: 'no_match', reference };
    try {
      db.transaction(() => {
        const payment = db.prepare(`
          SELECT p.*, sf.student_id, sf.fee_group_id, sf.amount as total_amount,
                 sf.paid_amount, sf.fine_amount, sf.discount_amount
          FROM payments p
          JOIN student_fees sf ON p.student_fee_id = sf.id
          WHERE p.reference_number = ?
        `).get(reference);

        if (!payment) {
          result = { kind: 'no_match', reference };
          return;
        }

        // ---- Completed / Paid / Delivered ----
        if (status === 'Paid' || status === 'Completed' || status === 'Delivered') {
          // Idempotency: if already completed, short-circuit with no writes.
          if (payment.status === 'completed') {
            result = { kind: 'already_done', payment, note: 'already processed' };
            return;
          }

          // Compute new fee totals using the same formula as the manual
          // payment path: cap paid_amount at (amount + fine - discount).
          const totalDue = (payment.total_amount || 0)
            + (payment.fine_amount || 0)
            - (payment.discount_amount || 0);
          const newPaidAmount = Math.min(
            (payment.paid_amount || 0) + (payment.amount || 0),
            totalDue,
          );
          const newStatus = newPaidAmount >= totalDue
            ? 'paid'
            : (newPaidAmount > 0 ? 'partial' : 'partial');
          const paidAtClause = newStatus === 'paid' ? `paid_at = datetime('now','localtime'),` : '';

          db.prepare(`
            UPDATE payments SET
              status = 'completed',
              paid_at = datetime('now','localtime'),
              updated_at = datetime('now','localtime'),
              sync_status = 'pending'
            WHERE uid = ?
          `).run(payment.uid);

          db.prepare(`
            UPDATE student_fees SET
              paid_amount = ?,
              status = ?,
              ${paidAtClause}
              updated_at = datetime('now','localtime'),
              sync_status = 'pending'
            WHERE id = ?
          `).run(newPaidAmount, newStatus, payment.student_fee_id);

          // Best-effort invoice link — same logic as the manual pay path.
          const invoiceRow = db.prepare(`
            SELECT uid, total, paid_amount
            FROM invoices
            WHERE student_id = ? AND fee_group_id = ? AND is_deleted = 0
            ORDER BY issued_at DESC, id DESC
            LIMIT 1
          `).get(payment.student_id, payment.fee_group_id);

          if (invoiceRow) {
            const newInvoicePaid = Math.min(
              (invoiceRow.paid_amount || 0) + (payment.amount || 0),
              invoiceRow.total || 0,
            );
            const newInvoiceStatus = newInvoicePaid >= (invoiceRow.total || 0)
              ? 'paid'
              : (newInvoicePaid > 0 ? 'partial' : 'issued');
            db.prepare(`
              UPDATE invoices SET
                paid_amount = ?,
                status = ?,
                updated_at = datetime('now','localtime'),
                sync_status = 'pending'
              WHERE uid = ?
            `).run(newInvoicePaid, newInvoiceStatus, invoiceRow.uid);
          }

          result = {
            kind: 'completed',
            payment,
            newPaidAmount,
            invoice_updated: invoiceRow ? invoiceRow.uid : null,
          };
          return;
        }

        // ---- Awaiting / anything non-terminal: pending only ----
        // Paynow will call us again when status changes to Paid/Cancelled.
        // Do NOT touch student_fees or invoices here — that would make
        // partial reversals on cancellation produce phantom credits.
        if (status === 'Awaiting' || status === 'Pending') {
          db.prepare(`
            UPDATE payments SET
              status = 'pending',
              updated_at = datetime('now','localtime')
            WHERE uid = ?
          `).run(payment.uid);
          result = { kind: 'pending', payment };
          return;
        }

        // ---- Cancelled / Failed ----
        if (status === 'Cancelled' || status === 'Failed') {
          db.prepare(`
            UPDATE payments SET
              status = 'failed',
              updated_at = datetime('now','localtime')
            WHERE uid = ?
          `).run(payment.uid);
          result = { kind: 'failed', payment };
          return;
        }

        // Unrecognised status: no-op (don't invent state for a value we
        // don't know). Logged below.
        result = { kind: 'unknown_status', payment, status };
      })();
    } catch (err) {
      console.error('Webhook DB transaction failed:', err);
      return res.status(500).json({ error: 'Webhook processing failed' });
    }

    // Preserve the existing logger output shape — just enrich with kind.
    if (result.kind === 'no_match') {
      console.log('Payment not found for reference:', reference);
      return res.status(404).json({ error: 'Payment not found' });
    }
    if (result.kind === 'already_done') {
      console.log(`Payment already completed: ${reference}`);
      return res.json({ status: 'ok', note: 'already processed' });
    }
    if (result.kind === 'completed') {
      console.log(`Payment confirmed: ${reference}, Amount: ${result.payment.amount}`, {
        new_paid_amount: result.newPaidAmount,
        invoice_updated: result.invoice_updated,
      });
    } else if (result.kind === 'failed') {
      console.log(`Payment failed: ${reference}`);
    } else if (result.kind === 'pending') {
      console.log(`Payment awaiting confirmation: ${reference}`);
    } else if (result.kind === 'unknown_status') {
      console.log(`Webhook for ${reference} had unrecognised status: ${result.status}`);
    }

    res.json({ status: 'ok' });

  } catch (error) {
    console.error('Webhook processing error:', error);
    res.status(500).json({ error: 'Webhook processing failed' });
  }
});

// ============ GET PAYMENTS FOR STUDENT ============

/**
 * @openapi
 * /api/payments/student/{studentId}:
 *     get:
 *       tags:
 *         - Payments
 *       summary: Get payments for a student
 *       description: Retrieves all payment records for a specific student. Accessible by school admins, the student themselves, or parents.
 *       security:
 *         - bearerAuth: []
 *       parameters:
 *         - name: studentId
 *           in: path
 *           required: true
 *           description: Student user ID
 *           schema:
 *             type: integer
 *             example: 123
 *       responses:
 *         '200':
 *           description: Student payments retrieved successfully
 *           content:
 *             application/json:
 *               schema:
 *                 type: array
 *                 items:
 *                   type: object
 *                   properties:
 *                     uid:
 *                       type: string
 *                       example: "550e8400-e29b-41d4-a716-446655440000"
 *                     student_fee_id:
 *                       type: string
 *                       example: "sf-12345"
 *                     amount:
 *                       type: number
 *                       example: 150.00
 *                     payment_method:
 *                       type: string
 *                       example: "paynow"
 *                     reference_number:
 *                       type: string
 *                       example: "FEE-12345-1699900000000"
 *                     status:
 *                       type: string
 *                       enum: [pending, completed, failed, cancelled]
 *                       example: completed
 *                     created_at:
 *                       type: string
 *                       format: date-time
 *                       example: "2024-01-15T10:30:00.000Z"
 *                     paid_at:
 *                       type: string
 *                       format: date-time
 *                       nullable: true
 *                       example: "2024-01-15T10:35:00.000Z"
 *                     fee_name:
 *                       type: string
 *                       example: "Term 1 Tuition"
 *                     fee_amount:
 *                       type: number
 *                       example: 500.00
 *         '401':
 *           description: Unauthorized - invalid or missing token
 *           content:
 *             application/json:
 *               schema:
 *                 type: object
 *                 properties:
 *                   error:
 *                     type: string
 *                     example: "Invalid token"
 */
// Local ownership middleware for GET /api/payments/student/:studentId.
// The generic requireRole.requireOwnerOrAdmin factory expects a
// getOwnerId(req) callback, which doesn't fit a path-param-only route;
// we reuse requirePaymentOwner (defined above) so the gate has the same
// semantics as the rest of /api/payments/*.
const requireOwnerOrAdmin = (req, res, next) => {
  if (!requirePaymentOwner(req, res, req.params.studentId)) return;
  next();
};

router.get('/student/:studentId', auth, requireOwnerOrAdmin, (req, res) => {
  const studentId = req.params.studentId;

  const payments = db.prepare(`
    SELECT p.*, fg.name as fee_name, sf.amount as fee_amount
    FROM payments p
    JOIN student_fees sf ON p.student_fee_id = sf.id
    JOIN fee_groups fg ON sf.fee_group_id = fg.id
    WHERE sf.student_id = ?
    ORDER BY p.created_at DESC
  `).all(studentId);

  res.json(payments);
});

// ============ CANCEL PENDING PAYMENT ============

/**
 * @openapi
 * /api/payments/{paymentId}:
 *     delete:
 *       tags:
 *         - Payments
 *       summary: Cancel a pending payment
 *       description: Cancels a payment that is still in pending status. Only pending payments can be cancelled.
 *       security:
 *         - bearerAuth: []
 *       parameters:
 *         - name: paymentId
 *           in: path
 *           required: true
 *           description: Unique payment ID to cancel
 *           schema:
 *             type: string
 *             example: "550e8400-e29b-41d4-a716-446655440000"
 *       responses:
 *         '200':
 *           description: Payment cancelled successfully
 *           content:
 *             application/json:
 *               schema:
 *                 type: object
 *                 properties:
 *                   success:
 *                     type: boolean
 *                     example: true
 *         '400':
 *           description: Cannot cancel non-pending payment
 *           content:
 *             application/json:
 *               schema:
 *                 type: object
 *                 properties:
 *                   error:
 *                     type: string
 *                     example: "Can only cancel pending payments"
 *         '401':
 *           description: Unauthorized - invalid or missing token
 *           content:
 *             application/json:
 *               schema:
 *                 type: object
 *                 properties:
 *                   error:
 *                     type: string
 *                     example: "Invalid token"
 *         '404':
 *           description: Payment not found
 *           content:
 *             application/json:
 *               schema:
 *                 type: object
 *                 properties:
 *                   error:
 *                     type: string
 *                     example: "Payment not found"
 */
router.delete('/:paymentId', auth, (req, res) => {
  const { paymentId } = req.params;
  
  const payment = db.prepare('SELECT * FROM payments WHERE uid = ?').get(paymentId);
  
  if (!payment) {
    return res.status(404).json({ error: 'Payment not found' });
  }
  if (!requirePaymentOwner(req, res, payment.student_id)) return;
  
  // Only cancel pending payments
  if (payment.status !== 'pending') {
    return res.status(400).json({ error: 'Can only cancel pending payments' });
  }
  
  db.prepare(`
    UPDATE payments SET status = 'cancelled', updated_at = CURRENT_TIMESTAMP
    WHERE uid = ?
  `).run(paymentId);
  
  res.json({ success: true });
});

module.exports = router;