import {
  IBankAccount,
  IBankAccountDoc,
  NewCreatedBankAccount,
  QueryFilter,
  UpdateBankAccountBody,
} from '@/modules/bankAccount/bankAccount.interface';
import { BankAccount } from '@/modules/bankAccount/bankAccount.model';
import responseCodes from '@/shared/utils/responseCode/responseCode';
import { ApiError } from '@/shared/utils/errors';
import { defaultStatus } from '@/shared/utils/responseCode/httpStatusAlias';
import { PaginateOptions } from '@/shared/utils/plugins/paginate/paginate';
import { FilterQuery } from 'mongoose';
import { getObjectId } from '@/shared/utils/commonHelper';
import { safeDeleteById } from '@/shared/utils/guard/ref-guard';

const { BankAccountResponseCodes } = responseCodes;

export const createBankAccount = async (
  payload: NewCreatedBankAccount,
): Promise<boolean> => {
  const existing = await BankAccount.findOne({
    company: getObjectId(payload.company),
    accountNumber: payload.accountNumber,
    bankName: payload.bankName,
    isDeleted: false,
  });
  if (existing)
    throw new ApiError(
      defaultStatus.BAD_REQUEST,
      'BankAccount already exists',
      true,
      '',
      BankAccountResponseCodes.BANKACCOUNT_ALREADY_EXISTS,
    );
  const bankAccount = await BankAccount.create(payload);
  if (!bankAccount)
    throw new ApiError(
      defaultStatus.OK,
      'Failed to create bankAccount',
      true,
      '',
      BankAccountResponseCodes.BANKACCOUNT_ERROR,
    );
  return true;
};

export const getBankAccountById = async (
  id: string,
): Promise<IBankAccountDoc | null> => {
  const bankAccount = await BankAccount.findById(id);
  if (!bankAccount)
    throw new ApiError(
      defaultStatus.OK,
      'BankAccount not found',
      true,
      '',
      BankAccountResponseCodes.BANKACCOUNT_NOT_FOUND,
    );

  return bankAccount;
};

export const updateBankAccount = async (
  id: string,
  updateData: UpdateBankAccountBody,
): Promise<void> => {
  try {
    const result = await BankAccount.updateOne(
      { _id: getObjectId(id), isDeleted: false },
      { $set: updateData },
    );

    if (result.matchedCount === 0)
      throw new ApiError(
        defaultStatus.OK,
        'Bank account not found',
        false,
        '',
        BankAccountResponseCodes.BANKACCOUNT_NOT_FOUND,
      );
  } catch (err: unknown) {
    if (err instanceof ApiError) throw err;
    throw new ApiError(
      defaultStatus.OK,
      'Failed to update bank account',
      true,
      '',
      BankAccountResponseCodes.BANKACCOUNT_ERROR,
    );
  }
};

export const queryBankAccount = async (
  rawFilter: QueryFilter,
  options: PaginateOptions,
) => {
  const { search, company, isDeleted, status, accountType, ...baseFilters } =
    rawFilter;

  const merged = {
    isDeleted: isDeleted ?? false,
    ...baseFilters,
    ...(company && { company: getObjectId(company) }),
    ...(status && status !== 'all' && { status }),
    ...(accountType && accountType !== 'all' && { accountType }),
  };

  const filter: FilterQuery<IBankAccount> = Object.fromEntries(
    Object.entries(merged).filter(([, v]) => v != null),
  ) as FilterQuery<IBankAccount>;

  if (search && typeof search === 'string')
    filter.$or = [
      { bankName: { $regex: search, $options: 'i' } },
      { ifscCode: { $regex: search, $options: 'i' } },
    ];

  return BankAccount.paginate(filter, options);
};

export const deleteBankAccount = async (id: string): Promise<void> => {
  try {
    await safeDeleteById(
      BankAccount,
      id,
      BankAccountResponseCodes.BANKACCOUNT_IN_USE,
    );
  } catch (err: unknown) {
    if (err instanceof ApiError) throw err;

    throw new ApiError(
      defaultStatus.INTERNAL_SERVER_ERROR,
      'Failed to delete bank account',
      true,
      '',
      BankAccountResponseCodes.BANKACCOUNT_ERROR,
    );
  }
};
