import { Router } from 'express';
import Joi from 'joi';
import pick from 'lodash/pick';
import type { WhereOptions } from 'sequelize';

import { createListParamSchema, getOrder, MetadataSchema } from '../libs/api';
import { authenticate } from '../libs/security';
import { formatMetadata } from '../libs/util';
import { WebhookEndpoint } from '../store/models';
import logger from '../libs/logger';

const router = Router();
const auth = authenticate<WebhookEndpoint>({ component: true, roles: ['owner', 'admin'] });

router.post('/', auth, async (req, res) => {
  const raw: Partial<WebhookEndpoint> = pick(req.body, ['url', 'description', 'metadata', 'status', 'enabled_events']);
  const exist = await WebhookEndpoint.findOne({ where: { url: raw.url } });
  if (exist) {
    return res.status(400).json({ error: 'webhook endpoint with same url already exist' });
  }

  raw.livemode = req.livemode;
  raw.api_version = '2023-09-05';
  raw.status = raw.status || 'enabled';
  if (raw.metadata) {
    const { error } = MetadataSchema.validate(raw.metadata);
    if (error) {
      return res.status(400).json({ error: `metadata invalid: ${error.message}` });
    }
    raw.metadata = formatMetadata(raw.metadata);
  }

  const doc = await WebhookEndpoint.create(raw as WebhookEndpoint);
  return res.json(doc);
});

const schema = createListParamSchema<{
  status?: string;
}>({
  status: Joi.string().empty(''),
});
router.get('/', auth, async (req, res) => {
  const { page, pageSize, status, ...query } = await schema.validateAsync(req.query, { stripUnknown: true });
  const where: WhereOptions<WebhookEndpoint> = {};

  if (status) {
    where.status = status
      .split(',')
      .map((x) => x.trim())
      .filter(Boolean);
  }
  Object.keys(query)
    .filter((x) => x.startsWith('metadata.'))
    .forEach((key: string) => {
      // @ts-ignore
      where[key] = query[key];
    });

  try {
    const { rows: list, count } = await WebhookEndpoint.findAndCountAll({
      where,
      order: getOrder(req.query, [['created_at', 'DESC']]),
      offset: (page - 1) * pageSize,
      limit: pageSize,
      include: [],
    });

    res.json({ count, list, paging: { page, pageSize } });
  } catch (err) {
    logger.error(err);
    res.json({ count: 0, list: [], paging: { page, pageSize } });
  }
});

router.get('/:id', auth, async (req, res) => {
  try {
    const doc = await WebhookEndpoint.findOne({
      where: { id: req.params.id },
      include: [],
    });
    res.json(doc);
  } catch (err) {
    logger.error(err);
    res.json(null);
  }
});

router.put('/:id', auth, async (req, res) => {
  const doc = await WebhookEndpoint.findByPk(req.params.id as string);

  if (!doc) {
    return res.status(404).json({ error: 'webhook endpoint not found' });
  }

  const updates: Partial<WebhookEndpoint> = pick(req.body, [
    'url',
    'description',
    'metadata',
    'status',
    'enabled_events',
  ]);
  if (updates.metadata) {
    const { error: metadataError } = MetadataSchema.validate(updates.metadata);
    if (metadataError) {
      return res.status(400).json({ error: `metadata invalid: ${metadataError.message}` });
    }
    updates.metadata = formatMetadata(updates.metadata);
  }

  await doc.update(updates);

  return res.json(doc);
});

router.delete('/:id', auth, async (req, res) => {
  const doc = await WebhookEndpoint.findByPk(req.params.id);

  if (!doc) {
    return res.status(404).json({ error: 'webhook endpoint not found' });
  }

  await doc.destroy();
  return res.json(doc);
});

export default router;
