import {
  BadRequestException,
  Injectable,
  Logger,
  NotFoundException,
  StreamableFile,
} from '@nestjs/common';
import { BookingService } from '../booking.service';
import { BookingRepository } from '../repositories/booking.repository';
import { PrivateDocumentStorage } from '../../../common/storage/private-document.storage';
import { sniffPanDocument } from '../../../common/storage/file-sniff';

export const PAN_CARD_MAX_BYTES = 5 * 1024 * 1024;

@Injectable()
export class BookingPanService {
  private readonly logger = new Logger(BookingPanService.name);

  constructor(
    private readonly bookings: BookingService,
    private readonly repo: BookingRepository,
    private readonly files: PrivateDocumentStorage,
  ) {}

  async upload(bookingId: string, file: Express.Multer.File): Promise<{ has_pan_card: true }> {
    await this.bookings.findById(bookingId);
    if (!file?.buffer?.length) {
      throw new BadRequestException('No file uploaded');
    }
    if (file.size > PAN_CARD_MAX_BYTES) {
      throw new BadRequestException('PAN card must be 5MB or smaller');
    }
    const sniffed = sniffPanDocument(file.buffer);
    if (!sniffed) {
      throw new BadRequestException('PAN card must be an image or PDF');
    }

    const row = await this.repo.findById(bookingId);
    if (!row) throw new NotFoundException('Booking not found');

    const filename = await this.files.save(sniffed.ext, file.buffer);
    const previous = row.pan_card_filename;
    try {
      await this.repo.update(bookingId, { pan_card_filename: filename } as never);
    } catch (err) {
      await this.files.delete(filename);
      throw err;
    }
    if (previous && previous !== filename) {
      await this.files.delete(previous);
    }

    this.logger.log(`PAN card uploaded booking=${bookingId} file=${filename} size=${file.size}`);
    return { has_pan_card: true };
  }

  async download(bookingId: string): Promise<StreamableFile> {
    await this.bookings.findById(bookingId);
    const row = await this.repo.findById(bookingId);
    if (!row?.pan_card_filename) {
      throw new NotFoundException('PAN card not uploaded');
    }
    if (!(await this.files.exists(row.pan_card_filename))) {
      throw new NotFoundException('PAN card not uploaded');
    }
    const body = await this.files.read(row.pan_card_filename);
    const sniffed = sniffPanDocument(body);
    const mime = sniffed?.mime ?? 'application/octet-stream';
    const downloadName = `pan-card${sniffed?.ext ?? ''}`;
    return new StreamableFile(body, {
      type: mime,
      disposition: `inline; filename="${downloadName}"`,
    });
  }
}
