import { Joi } from '@arcblock/validator';
import { Router } from 'express';

import { BN } from '@ocap/util';
import { createListParamSchema, getOrder } from '../libs/api';
import logger from '../libs/logger';
import { CheckoutSession } from '../store/models/checkout-session';
import { Customer } from '../store/models/customer';
import { PaymentLink } from '../store/models/payment-link';
import { PaymentMethod } from '../store/models/payment-method';
import { Price } from '../store/models/price';
import type { DonationSettings } from '../store/models/types';
import { createPaymentLink } from './payment-links';
import { createProductAndPrices } from './products';

const router = Router();

// FIXME: add more custom validation here for amount
const donationSchema = Joi.object<DonationSettings>({
  target: Joi.string().max(128).required(),
  title: Joi.string().required(),
  description: Joi.string().required(),
  reference: Joi.string().required(),
  beneficiaries: Joi.array()
    .items(
      Joi.object({
        address: Joi.DID().required(),
        share: Joi.number().positive().required(),
        memo: Joi.string().max(64).optional(),
      })
    )
    .max(8)
    .optional()
    .default([]),
  amount: Joi.object({
    presets: Joi.array().items(Joi.number().positive()).optional().default([]),
    preset: Joi.number().empty('').positive().optional(),
    minimum: Joi.number().positive().optional(),
    maximum: Joi.number().positive().optional(),
    custom: Joi.boolean().optional().default(true),
  }),
  message: Joi.object({
    success: Joi.string().optional(),
    summary: Joi.string().optional(),
  }),
});
// prepare donation payment links
router.post('/', async (req, res) => {
  try {
    const payload = await donationSchema.validateAsync(req.body, { stripUnknown: true, convert: true });
    const link = await PaymentLink.findOne({
      where: { 'donation_settings.target': payload.target, livemode: !!req.livemode },
    });
    if (link) {
      await link.update({
        name: payload.title,
        submit_type: 'donate',
        donation_settings: payload,
        after_completion: {
          type: 'hosted_confirmation',
          hosted_confirmation: {
            custom_message: payload.message?.success || '',
          },
        },
      });
      logger.info('Payment link updated successfully', { linkId: link.id });
      res.json(link.toJSON());
      return;
    }

    logger.info('No existing payment link found, creating new one');
    const lookupKey = `${payload.target}-${req.livemode ? 'live' : 'test'}`;
    let price = await Price.findByPkOrLookupKey(lookupKey);
    if (!price) {
      logger.info('No existing price found, creating new product and price');
      const result = await createProductAndPrices({
        type: 'service',
        livemode: !!req.livemode,
        name: payload.title,
        description: payload.description,
        currency_id: req.baseCurrency.id,
        via: 'donation',
        prices: [
          {
            type: 'one_time',
            unit_amount: '0',
            billing_schema: 'per_unit',
            lookup_key: lookupKey,
            custom_unit_amount: {
              presets: payload.amount.presets || [],
              preset: payload.amount.preset || null,
              maximum: payload.amount.maximum || null,
              minimum: payload.amount.minimum || '0',
            },
          },
        ],
        metadata: {},
      });

      price = result.prices[0] as Price;
    }

    const result = await createPaymentLink({
      livemode: !!req.livemode,
      created_via: req.user?.via,
      currency_id: req.baseCurrency.id,
      name: payload.title,
      submit_type: 'donate',
      line_items: [{ price_id: price.id, quantity: 1 }],
      donation_settings: payload,
      after_completion: {
        type: 'hosted_confirmation',
        hosted_confirmation: {
          custom_message: payload.message?.success || '',
        },
      },
    });
    logger.info('New payment link created', { linkId: result.id });
    res.json(result);
  } catch (err) {
    logger.error('Failed to prepare payment link for donation', err);
    res.status(400).json({ error: err.message });
  }
});

// get donations by target
const paginationSchema = createListParamSchema<{ target: string }>({ target: Joi.string().required() }, 20);
router.get('/', async (req, res) => {
  try {
    const { page, pageSize, target } = await paginationSchema.validateAsync(req.query, {
      convert: true,
      stripUnknown: true,
    });
    const { rows, count } = await CheckoutSession.findAndCountAll({
      where: { payment_link_id: target, status: 'complete', livemode: req.livemode },
      attributes: [
        'id',
        'customer_id',
        'customer_did',
        'amount_total',
        'payment_intent_id',
        'payment_details',
        'created_at',
        'updated_at',
      ],
      order: getOrder(req.query, [['created_at', 'DESC']]),
      offset: (page - 1) * pageSize,
      include: [{ model: Customer, as: 'customer', attributes: ['id', 'did', 'name', 'metadata'] }],
      limit: pageSize,
    });

    const method = await PaymentMethod.findByPk(req.baseCurrency.payment_method_id);

    const totalAmount: string = rows
      .map((x) => x.toJSON())
      .reduce((total, x) => total.add(new BN(x.amount_total)), new BN('0'))
      .toString();

    res.json({
      supporters: rows,
      currency: req.baseCurrency,
      method,
      total: count,
      totalAmount,
      paging: { page, pageSize },
    });
  } catch (err) {
    res.status(400).json({ error: err.message, supporters: [] });
  }
});

export default router;
