import { Router, type Response } from 'express';
import { z } from 'zod';
import { requireAdmin, requireAuth, type AuthenticatedRequest } from './auth';
import { config } from './config';
import { pool } from './db';
import { authorizedContext } from './familyCallState';
import { decryptPaymentAsset, encryptPaymentAsset } from './privacy';
import { ensurePaymentObligations, feeFor, readPaymentSettings } from './paymentService';
import { notifyUsers } from './notifications';
import { paymentReviewEstimate } from './paymentReviewSchedule';

export const platformPaymentsRouter = Router();

const publicSettings = async () => {
  const settings = await readPaymentSettings();
  return {
    loadingMessage: settings.loading_message,
    feeNotice: settings.fee_notice,
    shamCashReview: paymentReviewEstimate(settings),
    methods: {
      shamCash: { enabled: settings.sham_cash_enabled, currency: settings.sham_cash_currency,
        fees: settings.sham_cash_fees, qrAvailable: Boolean(settings.sham_cash_qr_encrypted) },
      paypal: { enabled: settings.paypal_enabled && config.paypal.configured,
        currency: settings.paypal_currency, fees: settings.paypal_fees },
    },
  };
};

platformPaymentsRouter.get('/public/platform-settings', async (_request, response) => {
  response.json(await publicSettings());
});

platformPaymentsRouter.get('/v1/journeys/:id/checkout', requireAuth,
  async (request: AuthenticatedRequest, response) => {
    const context = await authorizedContext(request, response);
    if (!context) return;
    response.setHeader('Cache-Control', 'no-store');
    if (!context.meetingId) {
      response.json({ meetingId: null, sessionNumber: null, isPrimary: false,
        paymentRequiredFromActor: false,
        allPaid: false, payments: [], settings: await publicSettings() });
      return;
    }
    await ensurePaymentObligations(context.meetingId);
    const meeting = await pool.query<{ session_number: number }>(
      'SELECT session_number FROM meeting_proposals WHERE id=$1', [context.meetingId],
    );
    const payments = await pool.query(
      `SELECT payment.beneficiary_user_id AS "beneficiaryUserId", beneficiary.display_name AS "beneficiaryName",
              payment.paid_by_user_id AS "paidByUserId", payer.display_name AS "paidByName",
              payment.method, payment.status, payment.amount::text, payment.currency,
              payment.updated_at AS "updatedAt"
       FROM meeting_payment_obligations payment
       JOIN users beneficiary ON beneficiary.id=payment.beneficiary_user_id
       LEFT JOIN users payer ON payer.id=payment.paid_by_user_id
       WHERE payment.meeting_id=$1 AND payment.beneficiary_user_id=$2
       ORDER BY beneficiary.display_name`, [context.meetingId, request.authUser!.id],
    );
    const paid = await pool.query<{ ready: boolean }>(
      `SELECT EXISTS (
         SELECT 1 FROM meeting_payment_obligations WHERE meeting_id=$1 AND status='paid'
       ) AS ready`, [context.meetingId],
    );
    const allPaid = Boolean(paid.rows[0]?.ready);
    const isPrimary = [context.firstId, context.secondId].includes(request.authUser!.id);
    const paymentRequiredFromActor = isPrimary && !allPaid;
    response.json({
      meetingId: context.meetingId,
      sessionNumber: meeting.rows[0]?.session_number ?? 1,
      isPrimary,
      paymentRequiredFromActor,
      actorUserId: request.authUser!.id,
      allPaid,
      payments: paymentRequiredFromActor ? payments.rows : [],
      settings: await publicSettings(),
    });
  });

const beneficiarySchema = z.object({ beneficiaryUserId: z.uuid() });

const paymentContext = async (request: AuthenticatedRequest, response: Response) => {
  const context = await authorizedContext(request, response);
  const parsed = beneficiarySchema.safeParse(request.body);
  if (!context || !parsed.success || !context.meetingId) {
    if (context) response.status(400).json({ error: 'Invalid payment request' });
    return null;
  }
  const actorId = request.authUser!.id;
  const primaryUsers = [context.firstId, context.secondId];
  if (!primaryUsers.includes(actorId) || parsed.data.beneficiaryUserId !== actorId) {
    response.status(403).json({ error: 'Only a primary participant can pay their checkout attempt' });
    return null;
  }
  await ensurePaymentObligations(context.meetingId);
  const meeting = await pool.query<{ session_number: number; status: string; guardian_confirmations: number }>(
    `SELECT meeting.session_number,meeting.status,
       (SELECT COUNT(*)::int FROM meeting_schedule_confirmations confirmation
        WHERE confirmation.meeting_id=meeting.id) AS guardian_confirmations
     FROM meeting_proposals meeting WHERE meeting.id=$1`, [context.meetingId],
  );
  if (meeting.rows[0]?.status !== 'confirmed' || meeting.rows[0].guardian_confirmations < 2) {
    response.status(409).json({ error: 'The meeting is not ready for payment' }); return null;
  }
  return { context, actorId, beneficiaryId: parsed.data.beneficiaryUserId,
    sessionNumber: meeting.rows[0].session_number };
};

platformPaymentsRouter.get('/v1/journeys/:id/payments/sham-cash/qr', requireAuth,
  async (request: AuthenticatedRequest, response) => {
    const context = await authorizedContext(request, response);
    if (!context || ![context.firstId, context.secondId].includes(request.authUser!.id)) {
      if (context) response.status(403).json({ error: 'Payment QR is restricted to primary participants' });
      return;
    }
    const settings = await readPaymentSettings();
    if (!settings.sham_cash_enabled || !settings.sham_cash_qr_encrypted) {
      response.status(404).json({ error: 'Sham Cash payment QR is unavailable' }); return;
    }
    response.setHeader('Cache-Control', 'no-store');
    response.json({ dataUrl: decryptPaymentAsset(settings.sham_cash_qr_encrypted) });
  });

platformPaymentsRouter.post('/v1/journeys/:id/payments/sham-cash/submit', requireAuth,
  async (request: AuthenticatedRequest, response) => {
    const parsed = beneficiarySchema.extend({ reference: z.string().trim().min(3).max(120),
      proofDataUrl: z.string().max(1_500_000).optional() }).safeParse(request.body);
    if (!parsed.success) { response.status(400).json({ error: 'Invalid payment evidence' }); return; }
    const payment = await paymentContext(request, response);
    if (!payment) return;
    const settings = await readPaymentSettings();
    if (!settings.sham_cash_enabled || !settings.sham_cash_qr_encrypted) {
      response.status(409).json({ error: 'Sham Cash is not available' }); return;
    }
    const fee = feeFor(settings, 'sham_cash', payment.sessionNumber);
    const updated = await pool.query(
      `UPDATE meeting_payment_obligations SET paid_by_user_id=$1,method='sham_cash',status='submitted',
         amount=$2,currency=$3,payment_reference_encrypted=$4,proof_encrypted=$5,
         reviewed_by=NULL,reviewed_at=NULL,admin_note='',updated_at=NOW()
       WHERE meeting_id=$6 AND beneficiary_user_id=$7 AND status<>'paid' RETURNING status`,
      [payment.actorId, fee.amount, fee.currency, encryptPaymentAsset(parsed.data.reference),
        parsed.data.proofDataUrl ? encryptPaymentAsset(parsed.data.proofDataUrl) : null,
        payment.context.meetingId, payment.beneficiaryId],
    );
    if (!updated.rowCount) { response.status(409).json({ error: 'This fee is already paid' }); return; }
    response.status(202).json({ status: 'submitted' });
  });

const paypalAccessToken = async (): Promise<string> => {
  if (!config.paypal.configured) throw new Error('PayPal is not configured');
  const response = await fetch(`${config.paypal.apiBase}/v1/oauth2/token`, {
    method: 'POST', headers: { Authorization: `Basic ${Buffer.from(`${config.paypal.clientId}:${config.paypal.clientSecret}`).toString('base64')}`,
      'Content-Type': 'application/x-www-form-urlencoded' }, body: 'grant_type=client_credentials',
  });
  const body = await response.json() as { access_token?: string };
  if (!response.ok || !body.access_token) throw new Error('PayPal authentication failed');
  return body.access_token;
};

platformPaymentsRouter.post('/v1/journeys/:id/payments/paypal/order', requireAuth,
  async (request: AuthenticatedRequest, response) => {
    const payment = await paymentContext(request, response);
    if (!payment) return;
    const settings = await readPaymentSettings();
    if (!settings.paypal_enabled || !config.paypal.configured) {
      response.status(409).json({ error: 'PayPal is not configured' }); return;
    }
    const fee = feeFor(settings, 'paypal', payment.sessionNumber);
    const token = await paypalAccessToken();
    const paypal = await fetch(`${config.paypal.apiBase}/v2/checkout/orders`, {
      method: 'POST', headers: { Authorization: `Bearer ${token}`, 'Content-Type': 'application/json',
        'PayPal-Request-Id': `${payment.context.meetingId}:${payment.beneficiaryId}` },
      body: JSON.stringify({ intent: 'CAPTURE', purchase_units: [{ custom_id: `${payment.context.meetingId}:${payment.beneficiaryId}`,
        amount: { currency_code: fee.currency, value: fee.amount } }] }),
    });
    const body = await paypal.json() as { id?: string; links?: Array<{ rel: string; href: string }> };
    const approvalUrl = body.links?.find((item) => item.rel === 'approve')?.href;
    if (!paypal.ok || !body.id || !approvalUrl) { response.status(502).json({ error: 'PayPal order creation failed' }); return; }
    await pool.query(
      `UPDATE meeting_payment_obligations SET paid_by_user_id=$1,method='paypal',status='pending',
       amount=$2,currency=$3,provider_order_id=$4,updated_at=NOW()
       WHERE meeting_id=$5 AND beneficiary_user_id=$6 AND status<>'paid'`,
      [payment.actorId, fee.amount, fee.currency, body.id, payment.context.meetingId, payment.beneficiaryId],
    );
    response.status(201).json({ orderId: body.id, approvalUrl });
  });

platformPaymentsRouter.post('/v1/journeys/:id/payments/paypal/capture', requireAuth,
  async (request: AuthenticatedRequest, response) => {
    const payment = await paymentContext(request, response);
    if (!payment) return;
    const obligation = await pool.query<{ provider_order_id: string | null; amount: string; currency: string }>(
      `SELECT provider_order_id,amount::text,currency FROM meeting_payment_obligations
       WHERE meeting_id=$1 AND beneficiary_user_id=$2 AND method='paypal'`,
      [payment.context.meetingId, payment.beneficiaryId],
    );
    const row = obligation.rows[0];
    if (!row?.provider_order_id) { response.status(409).json({ error: 'PayPal order is unavailable' }); return; }
    const token = await paypalAccessToken();
    const capture = await fetch(`${config.paypal.apiBase}/v2/checkout/orders/${encodeURIComponent(row.provider_order_id)}/capture`, {
      method: 'POST', headers: { Authorization: `Bearer ${token}`, 'Content-Type': 'application/json' }, body: '{}',
    });
    const body = await capture.json() as { status?: string; purchase_units?: Array<{ payments?: { captures?: Array<{ amount?: { value?: string; currency_code?: string } }> } }> };
    const amount = body.purchase_units?.[0]?.payments?.captures?.[0]?.amount;
    if (!capture.ok || body.status !== 'COMPLETED' || amount?.value !== row.amount
      || amount?.currency_code !== row.currency) {
      response.status(409).json({ error: 'PayPal payment is not completed or does not match' }); return;
    }
    await pool.query(
      `UPDATE meeting_payment_obligations SET status='paid',reviewed_at=NOW(),updated_at=NOW()
       WHERE meeting_id=$1 AND beneficiary_user_id=$2 AND provider_order_id=$3`,
      [payment.context.meetingId, payment.beneficiaryId, row.provider_order_id],
    );
    response.json({ status: 'paid' });
  });

const moneyMap = z.object({ '1': z.string().regex(/^\d{1,8}(\.\d{1,2})?$/),
  '2': z.string().regex(/^\d{1,8}(\.\d{1,2})?$/), '3': z.string().regex(/^\d{1,8}(\.\d{1,2})?$/) });
const adminSettingsSchema = z.object({ loadingMessage: z.string().trim().min(3).max(180),
  feeNotice: z.string().trim().min(10).max(500), shamCashEnabled: z.boolean(),
  shamCashCurrency: z.string().regex(/^[A-Z]{3}$/), shamCashFees: moneyMap,
  paypalEnabled: z.boolean(), paypalCurrency: z.string().regex(/^[A-Z]{3}$/), paypalFees: moneyMap,
  adminWorkTimezone: z.string().trim().min(3).max(80),
  adminWorkDays: z.array(z.number().int().min(1).max(7)).min(1).max(7),
  adminWorkStartMinutes: z.number().int().min(0).max(1438),
  adminWorkEndMinutes: z.number().int().min(1).max(1439),
  shamCashReviewMinutes: z.number().int().min(1).max(120),
  shamCashAfterOpenMinutes: z.number().int().min(1).max(240),
}).refine((value) => value.adminWorkStartMinutes < value.adminWorkEndMinutes);

platformPaymentsRouter.get('/admin/platform-settings', requireAuth, requireAdmin,
  async (_request, response) => {
    const settings = await readPaymentSettings();
    response.json({ settings: await publicSettings(), paypalConfigured: config.paypal.configured,
      paypalSettingEnabled: settings.paypal_enabled,
      workSchedule: {
        timeZone: settings.admin_work_timezone, days: settings.admin_work_days,
        startMinutes: settings.admin_work_start_minutes, endMinutes: settings.admin_work_end_minutes,
        reviewMinutes: settings.sham_cash_review_minutes,
        afterOpenMinutes: settings.sham_cash_after_open_minutes,
      } });
  });

platformPaymentsRouter.put('/admin/platform-settings', requireAuth, requireAdmin,
  async (request: AuthenticatedRequest, response) => {
    const parsed = adminSettingsSchema.safeParse(request.body);
    if (!parsed.success) { response.status(400).json({ error: 'Invalid platform settings' }); return; }
    const value = parsed.data;
    try { new Intl.DateTimeFormat('en', { timeZone: value.adminWorkTimezone }).format(); }
    catch { response.status(400).json({ error: 'Invalid work time zone' }); return; }
    await pool.query(
      `UPDATE platform_settings SET loading_message=$1,fee_notice=$2,sham_cash_enabled=$3,
       sham_cash_currency=$4,sham_cash_fees=$5::jsonb,paypal_enabled=$6,paypal_currency=$7,
       paypal_fees=$8::jsonb,admin_work_timezone=$9,admin_work_days=$10::smallint[],
       admin_work_start_minutes=$11,admin_work_end_minutes=$12,sham_cash_review_minutes=$13,
       sham_cash_after_open_minutes=$14,updated_at=NOW(),updated_by=$15 WHERE singleton=TRUE`,
      [value.loadingMessage, value.feeNotice, value.shamCashEnabled, value.shamCashCurrency,
        JSON.stringify(value.shamCashFees), value.paypalEnabled, value.paypalCurrency,
        JSON.stringify(value.paypalFees), value.adminWorkTimezone, value.adminWorkDays,
        value.adminWorkStartMinutes, value.adminWorkEndMinutes, value.shamCashReviewMinutes,
        value.shamCashAfterOpenMinutes, request.authUser!.id],
    );
    response.json({ saved: true });
  });

platformPaymentsRouter.put('/admin/platform-settings/sham-cash-qr', requireAuth, requireAdmin,
  async (request: AuthenticatedRequest, response) => {
    const parsed = z.object({ dataUrl: z.string().regex(/^data:image\/(png|jpeg|webp);base64,/).max(1_500_000) })
      .safeParse(request.body);
    if (!parsed.success) { response.status(400).json({ error: 'Invalid QR image' }); return; }
    const mime = parsed.data.dataUrl.slice(5, parsed.data.dataUrl.indexOf(';'));
    await pool.query(
      `UPDATE platform_settings SET sham_cash_qr_encrypted=$1,sham_cash_qr_mime=$2,
       updated_at=NOW(),updated_by=$3 WHERE singleton=TRUE`,
      [encryptPaymentAsset(parsed.data.dataUrl), mime, request.authUser!.id],
    );
    response.json({ saved: true });
  });

platformPaymentsRouter.get('/admin/payment-claims', requireAuth, requireAdmin, async (_request, response) => {
  response.setHeader('Cache-Control', 'no-store');
  const result = await pool.query(
    `SELECT payment.meeting_id AS "meetingId",payment.beneficiary_user_id AS "beneficiaryUserId",
       beneficiary.display_name AS "beneficiaryName",payer.display_name AS "payerName",payment.status,
       payment.amount::text,payment.currency,payment.method,payment.updated_at AS "updatedAt",
       CASE WHEN payment.payment_reference_encrypted IS NULL THEN NULL
         ELSE payment.payment_reference_encrypted END AS "encryptedReference",
       payment.proof_encrypted AS "encryptedProof"
     FROM meeting_payment_obligations payment
     JOIN users beneficiary ON beneficiary.id=payment.beneficiary_user_id
     LEFT JOIN users payer ON payer.id=payment.paid_by_user_id
     WHERE payment.status='submitted' ORDER BY payment.updated_at`,
  );
  response.json({ claims: result.rows.map((row) => ({ ...row,
    reference: row.encryptedReference ? decryptPaymentAsset(row.encryptedReference) : null,
    proofDataUrl: row.encryptedProof ? decryptPaymentAsset(row.encryptedProof) : null,
    encryptedReference: undefined, encryptedProof: undefined })) });
});

platformPaymentsRouter.get('/admin/payments', requireAuth, requireAdmin, async (request, response) => {
  const parsed = z.object({
    query: z.string().trim().max(120).default(''),
    method: z.enum(['all', 'sham_cash', 'paypal']).default('all'),
    status: z.enum(['all', 'submitted', 'paid']).default('all'),
    month: z.coerce.number().int().min(1).max(12).optional(),
    year: z.coerce.number().int().min(2020).max(2100).optional(),
  }).safeParse(request.query);
  if (!parsed.success) { response.status(400).json({ error: 'Invalid payment filters' }); return; }
  const search = parsed.data.query ? `%${parsed.data.query}%` : null;
  const result = await pool.query(
    `SELECT payment.meeting_id AS "meetingId",payment.beneficiary_user_id AS "beneficiaryUserId",
       beneficiary.display_name AS "beneficiaryName",beneficiary.username AS "beneficiaryUsername",
       beneficiary.email AS "beneficiaryEmail",payer.display_name AS "payerName",
       payer.username AS "payerUsername",payment.status,payment.amount::text,payment.currency,
       payment.method,meeting.session_number AS "sessionNumber",journey.id AS "journeyId",
       reviewer.display_name AS "reviewedByName",payment.reviewed_at AS "reviewedAt",
       payment.admin_note AS "adminNote",payment.created_at AS "createdAt",
       payment.updated_at AS "updatedAt",payment.payment_reference_encrypted AS "encryptedReference",
       payment.proof_encrypted AS "encryptedProof"
     FROM meeting_payment_obligations payment
     JOIN meeting_proposals meeting ON meeting.id=payment.meeting_id
     JOIN journeys journey ON journey.id=meeting.journey_id
     JOIN users beneficiary ON beneficiary.id=payment.beneficiary_user_id
     LEFT JOIN users payer ON payer.id=payment.paid_by_user_id
     LEFT JOIN users reviewer ON reviewer.id=payment.reviewed_by
     WHERE ($1::text IS NULL OR beneficiary.display_name ILIKE $1 OR beneficiary.username ILIKE $1
       OR beneficiary.email ILIKE $1 OR payer.display_name ILIKE $1 OR payer.username ILIKE $1)
       AND ($2='all' OR payment.method=$2)
       AND ($3='all' OR payment.status=$3)
       AND ((payment.method='paypal' AND payment.status='paid')
         OR (payment.method='sham_cash' AND payment.status IN ('submitted','paid')))
       AND ($4::integer IS NULL OR EXTRACT(MONTH FROM payment.updated_at)=$4)
       AND ($5::integer IS NULL OR EXTRACT(YEAR FROM payment.updated_at)=$5)
     ORDER BY payment.updated_at DESC LIMIT 500`,
    [search, parsed.data.method, parsed.data.status, parsed.data.month ?? null, parsed.data.year ?? null],
  );
  response.setHeader('Cache-Control', 'no-store');
  response.json({ payments: result.rows.map((row) => ({ ...row,
    reference: row.encryptedReference ? decryptPaymentAsset(row.encryptedReference) : null,
    proofDataUrl: row.encryptedProof ? decryptPaymentAsset(row.encryptedProof) : null,
    encryptedReference: undefined, encryptedProof: undefined })) });
});

platformPaymentsRouter.put('/admin/payment-claims/:meetingId/:beneficiaryId', requireAuth, requireAdmin,
  async (request: AuthenticatedRequest, response) => {
    const ids = z.object({ meetingId: z.uuid(), beneficiaryId: z.uuid() }).safeParse(request.params);
    const body = z.object({ decision: z.enum(['approved', 'rejected']), note: z.string().trim().max(500).default('') })
      .safeParse(request.body);
    if (!ids.success || !body.success) { response.status(400).json({ error: 'Invalid payment review' }); return; }
    const status = body.data.decision === 'approved' ? 'paid' : 'rejected';
    const result = await pool.query(
      `UPDATE meeting_payment_obligations SET status=$1,reviewed_by=$2,reviewed_at=NOW(),
       admin_note=$3,updated_at=NOW() WHERE meeting_id=$4 AND beneficiary_user_id=$5
       AND status='submitted' RETURNING status`,
      [status, request.authUser!.id, body.data.note, ids.data.meetingId, ids.data.beneficiaryId],
    );
    if (!result.rowCount) { response.status(409).json({ error: 'Payment claim is no longer pending' }); return; }
    const meeting = await pool.query<{ journey_id: string }>(
      'SELECT journey_id FROM meeting_proposals WHERE id=$1', [ids.data.meetingId],
    );
    await notifyUsers([ids.data.beneficiaryId], {
      type: 'payment_reviewed',
      title: body.data.decision === 'approved' ? 'تم اعتماد الدفع' : 'تعذر اعتماد الدفع',
      body: body.data.decision === 'approved'
        ? 'تحقق فريقنا من تحويل شام كاش وأصبحت الجلسة متاحة.'
        : 'لم نتمكن من اعتماد التحويل. راجع بيانات العملية وأرسلها مجددًا.',
      journeyId: meeting.rows[0]?.journey_id,
      dedupeKey: `payment-reviewed:${ids.data.meetingId}:${ids.data.beneficiaryId}:${status}`,
    });
    response.json({ status });
  });
