import { Router } from 'express';
import Joi from 'joi';

import { CustomError } from '@blocklet/error';
import { literal } from 'sequelize';
import { authenticate } from '../libs/security';
import { createListParamSchema, getOrder, getWhereFromKvQuery, MetadataSchema } from '../libs/api';
import { formatMetadata } from '../libs/util';
import { InvoiceItem, TaxRate } from '../store/models';

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

const createTaxRateSchema = Joi.object({
  country: Joi.string().length(2).uppercase().required(),
  state: Joi.string().max(50).empty('').optional(),
  postal_code: Joi.string().max(20).empty('').optional(),
  tax_code: Joi.string().max(20).empty('').optional(),
  percentage: Joi.number().min(0).max(99.9999).required(),
  display_name: Joi.string().max(100).required(),
  description: Joi.string().max(500).empty('').optional(),
  active: Joi.boolean().default(true),
  metadata: MetadataSchema,
}).unknown(true);

const updateTaxRateSchema = Joi.object({
  active: Joi.boolean().optional(),
  percentage: Joi.number().min(0).max(99.9999).optional(),
  display_name: Joi.string().max(100).optional(),
  description: Joi.string().max(500).empty('').optional(),
  metadata: MetadataSchema,
}).unknown(true);

const listSchema = createListParamSchema<{
  country?: string;
  state?: string;
  active?: boolean;
}>({
  country: Joi.string().length(2).uppercase().optional(),
  state: Joi.string().max(50).optional(),
  active: Joi.boolean().optional(),
});

// List tax rates
router.get('/', auth, async (req, res) => {
  try {
    const { error, value: validated } = listSchema.validate(req.query, { stripUnknown: true });
    if (error) {
      res.status(400).json({ error: `Validation error: ${error.message}` });
      return;
    }

    const { page, pageSize, ...query } = validated;
    const where = getWhereFromKvQuery(query.q);

    if (typeof req.livemode === 'boolean') {
      where.livemode = req.livemode;
    }
    if (query.country) {
      where.country = query.country;
    }
    if (query.state) {
      where.state = query.state;
    }
    if (typeof query.active === 'boolean') {
      where.active = query.active;
    }

    const { rows: list, count } = await TaxRate.findAndCountAll({
      where,
      order: getOrder(req.query, [['created_at', 'DESC']]),
      offset: (page - 1) * pageSize,
      limit: pageSize,
      attributes: {
        include: [
          [
            literal(`(
              SELECT COUNT(DISTINCT ii.invoice_id)
              FROM invoice_items AS ii
              WHERE ii.tax_rate_id = TaxRate.id
            )`),
            'invoice_count',
          ],
        ],
      },
    });

    res.json({ count, list });
  } catch (error) {
    res.status(400).json({ error: error?.message });
  }
});

// Get a specific tax rate
router.get('/:id', auth, async (req, res) => {
  const taxRate = await TaxRate.findOne({
    where: {
      id: req.params.id,
      livemode: req.livemode,
    },
  });

  if (!taxRate) {
    throw new CustomError(404, 'Tax rate not found');
  }

  res.json(taxRate);
});

// Create a new tax rate
router.post('/', auth, async (req, res) => {
  const { error, value: data } = createTaxRateSchema.validate(req.body, { stripUnknown: true });
  if (error) {
    res.status(400).json({ error: `Validation failed: ${error.message}` });
    return;
  }

  const existingRate = await TaxRate.findOne({
    where: {
      livemode: req.livemode,
      country: data.country,
      state: data.state || null,
      postal_code: data.postal_code || null,
      tax_code: data.tax_code || null,
    },
  });

  if (existingRate) {
    throw new CustomError(
      400,
      `Tax rate ${existingRate.id} already exists, you can create a new tax rate with different region or update the existing tax rate`
    );
  }

  const taxRate = await TaxRate.create({
    ...data,
    livemode: req.livemode,
    metadata: formatMetadata(data.metadata),
  });

  res.json(taxRate);
});

// Update a tax rate
router.put('/:id', auth, async (req, res) => {
  const { error, value: data } = updateTaxRateSchema.validate(req.body, { stripUnknown: true });
  if (error) {
    res.status(400).json({ error: `Validation failed: ${error.message}` });
    return;
  }

  const taxRate = await TaxRate.findOne({
    where: {
      id: req.params.id,
      livemode: req.livemode,
    },
  });

  if (!taxRate) {
    throw new CustomError(404, 'Tax rate not found');
  }

  await taxRate.update({
    ...data,
    metadata: data.metadata ? formatMetadata(data.metadata) : taxRate.metadata,
  });

  res.json(taxRate);
});

// Delete a tax rate
router.delete('/:id', auth, async (req, res) => {
  const taxRate = await TaxRate.findOne({
    where: {
      id: req.params.id,
      livemode: req.livemode,
    },
  });

  if (!taxRate) {
    throw new CustomError(404, 'Tax rate not found');
  }

  const existInvoice = await InvoiceItem.findOne({ where: { tax_rate_id: taxRate.id } });
  if (existInvoice) {
    throw new CustomError(400, 'Tax rate is used in an invoice, you can not delete it');
  }

  await taxRate.destroy();

  res.json({ deleted: true, id: req.params.id });
});

// Find matching tax rate for a given location and tax code
router.post('/match', auth, async (req, res) => {
  const schema = Joi.object({
    country: Joi.string().length(2).uppercase().required(),
    state: Joi.string().max(50).empty('').optional(),
    postal_code: Joi.string().max(20).empty('').optional(),
    tax_code: Joi.string().max(20).empty('').optional(),
  });

  const { error, value: data } = schema.validate(req.body, { stripUnknown: true });
  if (error) {
    res.status(400).json({ error: error.message });
    return;
  }

  const matchedRate = await TaxRate.findMatchingRate({
    country: data.country,
    state: data.state,
    postalCode: data.postal_code,
    taxCode: data.tax_code,
    livemode: req.livemode,
  });

  res.json(matchedRate);
});

export default router;
