import { PassThrough } from 'stream';
import csv from 'csv-parser';
import db from '@/db/models';
import DbApi from '@/db/api/permissions';
import ValidationError from '@/shared/errors/validation';
import type { CurrentUser } from '@/db/api/types';

type CreateData = Parameters<typeof DbApi.create>[0];
type UpdateData = Parameters<typeof DbApi.update>[1];
type ListFilter = Parameters<typeof DbApi.findAll>[0];
type BulkRow = Parameters<typeof DbApi.bulkImport>[0][number];

/** Parses an uploaded CSV buffer into bulk-import rows. */
function parseCsvRows(fileBuffer: Buffer): Promise<BulkRow[]> {
  const bufferStream = new PassThrough();
  const results: BulkRow[] = [];
  bufferStream.end(Buffer.from(fileBuffer));

  return new Promise((resolve, reject) => {
    bufferStream
      .pipe(csv())
      .on('data', (row: BulkRow) => results.push(row))
      .on('end', () => resolve(results))
      .on('error', reject);
  });
}

class PermissionsService {
  static async create(data: CreateData, currentUser?: CurrentUser) {
    const transaction = await db.sequelize.transaction();
    try {
      await DbApi.create(data, { currentUser, transaction });
      await transaction.commit();
    } catch (error) {
      await transaction.rollback();
      throw error;
    }
  }

  static async bulkImport(fileBuffer: Buffer, currentUser?: CurrentUser) {
    const rows = await parseCsvRows(fileBuffer);
    const transaction = await db.sequelize.transaction();
    try {
      await DbApi.bulkImport(rows, {
        transaction,
        ignoreDuplicates: true,
        validate: true,
        currentUser,
      });
      await transaction.commit();
    } catch (error) {
      await transaction.rollback();
      throw error;
    }
  }

  static async update(data: UpdateData, id: string, currentUser?: CurrentUser) {
    const transaction = await db.sequelize.transaction();
    try {
      const updated = await DbApi.update(id, data, { currentUser, transaction });

      if (!updated) {
        throw new ValidationError('permissionsNotFound');
      }

      await transaction.commit();
      return updated;
    } catch (error) {
      await transaction.rollback();
      throw error;
    }
  }

  static async deleteByIds(ids: string[], currentUser?: CurrentUser) {
    const transaction = await db.sequelize.transaction();
    try {
      await DbApi.deleteByIds(ids, { currentUser, transaction });
      await transaction.commit();
    } catch (error) {
      await transaction.rollback();
      throw error;
    }
  }

  static async remove(id: string, currentUser?: CurrentUser) {
    const transaction = await db.sequelize.transaction();
    try {
      await DbApi.remove(id, { currentUser, transaction });
      await transaction.commit();
    } catch (error) {
      await transaction.rollback();
      throw error;
    }
  }

  static list(filter: ListFilter, currentUser?: CurrentUser) {
    return DbApi.findAll(filter, { currentUser });
  }

  static count(filter: ListFilter, currentUser?: CurrentUser) {
    return DbApi.findAll(filter, { countOnly: true, currentUser });
  }

  static autocomplete(
    query: string | undefined,
    limit: number | undefined,
    offset: number | undefined,
  ) {
    return DbApi.findAllAutocomplete(query, limit, offset);
  }

  static findById(id: string) {
    return DbApi.findBy({ id });
  }
}

export default PermissionsService;
