import {
  Injectable,
  ConflictException,
  NotFoundException,
  ForbiddenException,
} from '@nestjs/common';
import { CurrencyRepository } from './repositories/currency.repository';
import { CreateCurrencyDto, UpdateCurrencyDto } from './dto';
import { CurrencyEntity, CurrencyFormat } from '../../entities/currency.entity';
import { serviceMessages } from '../../common/constants/messages';

const MSG = serviceMessages('Currency');

@Injectable()
export class CurrencyService {
  constructor(private readonly repo: CurrencyRepository) {}

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

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

  async findAllForLookup() {
    return this.repo.find({
      select: {
        id: true,
        code: true,
        name: true,
        symbol: true,
        rounding: 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: CreateCurrencyDto) {
    const existing = await this.repo.findByCode(dto.code.toUpperCase());
    if (existing) throw new ConflictException(MSG.ALREADY_EXISTS('code'));

    const item = await this.repo.save({
      name: dto.name,
      code: dto.code.toUpperCase(),
      symbol: dto.symbol,
      rounding: dto.rounding ?? 1,
      format: dto.format || CurrencyFormat.INTERNATIONAL,
      is_active: dto.is_active ?? true,
      is_system: false,
    });

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

  async update(id: string, dto: UpdateCurrencyDto) {
    const item = await this.repo.findById(id);
    if (!item) throw new NotFoundException(MSG.NOT_FOUND);

    if (dto.code) {
      const code = dto.code.toUpperCase();
      if (code !== item.code) {
        const existing = await this.repo.findByCode(code);
        if (existing && existing.id !== id) throw new ConflictException(MSG.ALREADY_EXISTS('code'));
      }
    }

    const updateData: Partial<CurrencyEntity> = {};
    const fields: (keyof UpdateCurrencyDto)[] = [
      'name',
      'code',
      'symbol',
      'rounding',
      'format',
      'is_active',
    ];
    const dtoRecord = dto as Record<string, unknown>;
    for (const field of fields) {
      const value = dtoRecord[field as string];
      if (value !== undefined) {
        (updateData as Record<string, unknown>)[field as string] =
          field === 'code' && typeof value === 'string' ? value.toUpperCase() : value;
      }
    }

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

  async remove(id: string) {
    const item = await this.repo.findById(id);
    if (!item) throw new NotFoundException(MSG.NOT_FOUND);
    if (item.is_system) throw new ForbiddenException(MSG.CANNOT_DELETE_SYSTEM);

    await this.repo.softDelete(id);
    return { message: MSG.DELETED };
  }
}
