import { Injectable, ConflictException, NotFoundException } from '@nestjs/common';
import { BankRepository } from './repositories/bank.repository';
import { CreateBankDto, UpdateBankDto } from './dto';
import { BankEntity } from '../../entities/bank.entity';
import { serviceMessages } from '../../common/constants/messages';

const MSG = serviceMessages('Bank');
const HAS_ACCOUNTS =
  'Cannot modify this bank while bank accounts still reference it. Remove or reassign those accounts first.';

@Injectable()
export class BankService {
  constructor(private readonly repo: BankRepository) {}

  async findAll(query?: { page?: number; limit?: number; search?: string }) {
    return this.repo.findPaginated({
      page: query?.page,
      limit: query?.limit,
      search: query?.search,
      searchColumns: ['name'],
      order: { name: 'ASC' },
    });
  }

  async findAllActive() {
    return this.repo.findAllActive();
  }

  async findAllForLookup() {
    return this.repo.find({
      where: { is_active: true },
      select: { id: true, name: true },
      order: { name: 'ASC' },
    });
  }

  async findById(id: string) {
    const item = await this.repo.findById(id);
    if (!item) throw new NotFoundException(MSG.NOT_FOUND);
    return item;
  }

  async create(dto: CreateBankDto) {
    const name = dto.name.trim();
    const existing = await this.repo.findByName(name);
    if (existing) throw new ConflictException(MSG.ALREADY_EXISTS('name'));

    const item = await this.repo.save({
      name,
      is_active: dto.is_active ?? true,
    });

    return { id: item.id, message: MSG.CREATED };
  }

  async update(id: string, dto: UpdateBankDto) {
    const item = await this.findById(id);

    if (dto.name !== undefined) {
      const name = dto.name.trim();
      if (name.toLowerCase() !== item.name.toLowerCase()) {
        const existing = await this.repo.findByName(name);
        if (existing && existing.id !== id) throw new ConflictException(MSG.ALREADY_EXISTS('name'));
      }
    }

    if (dto.is_active === false && item.is_active !== false) {
      await this.assertNoAccounts(id);
    }

    const updateData: Partial<BankEntity> = {};
    if (dto.name !== undefined) updateData.name = dto.name.trim();
    if (dto.is_active !== undefined) updateData.is_active = dto.is_active;

    await this.repo.update(id, updateData);
    return { id, message: MSG.UPDATED };
  }

  async remove(id: string) {
    await this.findById(id);
    await this.assertNoAccounts(id);
    await this.repo.softDelete(id);
    return { message: MSG.DELETED };
  }

  private async assertNoAccounts(bankId: string) {
    const count = await this.repo.countAccounts(bankId);
    if (count > 0) throw new ConflictException(HAS_ACCOUNTS);
  }
}
