
import {
  Injectable,
  Logger,
  NotFoundException,
  BadRequestException,
} from '@nestjs/common';
import { InjectDataSource } from '@nestjs/typeorm';
import { DataSource } from 'typeorm';
import { createReadStream, existsSync, statSync } from 'fs';
import { join, normalize, basename, extname } from 'path';
import { Readable } from 'stream';

import { AUDIT_TABLES, AuditTable } from '../shared/audit-query.helper';

type Stage = 1 | 2;

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

  // ── configure these per environment (env vars recommended) ──
  private readonly PUBLIC_BASE: Record<'QRS' | 'TQS', string> = {
    QRS: process.env.QRS_FILE_BASE_URL || 'https://crm.qrs.ae/',
    TQS: process.env.TQS_FILE_BASE_URL || 'https://crm.tqs.ae/',
  };
  // Absolute folder the legacy `assets/...` paths are relative to (Strategy B).
  private readonly DISK_ROOT: Record<'QRS' | 'TQS', string> = {
    QRS: process.env.QRS_FILE_ROOT || '/var/www/qrs/public',
    TQS: process.env.TQS_FILE_ROOT || '/var/www/tqs/public',
  };

  constructor(
    @InjectDataSource('qrs') private readonly qrs: DataSource,
    @InjectDataSource('tqs') private readonly tqs: DataSource,
  ) {}

  /** Read the stored path for one record's stage report. */
  private async lookupPath(
    source: 'QRS' | 'TQS',
    table: AuditTable,
    recordId: number,
    stage: Stage,
  ): Promise<string> {
    if (!AUDIT_TABLES.includes(table)) {
      throw new BadRequestException(`Invalid table "${table}"`);
    }
    const col = stage === 1 ? 'stg1_audit_report' : 'stg2_audit_report';
    const ds = source === 'QRS' ? this.qrs : this.tqs;
    const rows = await ds.query(
      `SELECT ${col} AS path FROM ${table} WHERE id = ? LIMIT 1`,
      [recordId],
    );
    const path = (rows?.[0]?.path || '').trim();
    if (!path) {
      throw new NotFoundException(
        `No stage-${stage} report uploaded for ${source}/${table}#${recordId}`,
      );
    }
    return path;
  }

  /**
   * STRATEGY A — return a public URL the browser can open directly.
   * The frontend just does window.open(url).
   */
  async getPublicUrl(
    source: 'QRS' | 'TQS',
    table: AuditTable,
    recordId: number,
    stage: Stage,
  ): Promise<{ url: string; filename: string }> {
    const rel = await this.lookupPath(source, table, recordId, stage);
    const base = this.PUBLIC_BASE[source].replace(/\/+$/, '');
    const clean = rel.replace(/^\/+/, '');
    return { url: `${base}/${clean}`, filename: basename(clean) };
  }

  /**
   * STRATEGY B — stream the file bytes from disk.
   * Returns a stream the controller wraps in a StreamableFile.
   */
  async openFile(
    source: 'QRS' | 'TQS',
    table: AuditTable,
    recordId: number,
    stage: Stage,
  ): Promise<{ stream: Readable; size: number; mimeType: string; filename: string }> {
    const rel = await this.lookupPath(source, table, recordId, stage);

    // Resolve safely under the configured root (prevent path traversal).
    const root = this.DISK_ROOT[source];
    const abs = normalize(join(root, rel.replace(/^\/+/, '')));
    if (!abs.startsWith(normalize(root))) {
      throw new BadRequestException('Resolved path escapes storage root');
    }
    if (!existsSync(abs)) {
      this.logger.warn(`[AUDIT-FILE] missing on disk: ${abs}`);
      throw new NotFoundException(`File not found on server: ${basename(rel)}`);
    }

    const size = statSync(abs).size;
    const filename = basename(abs);
    return {
      stream: createReadStream(abs),
      size,
      mimeType: this.mimeFor(extname(abs)),
      filename,
    };
  }

  private mimeFor(ext: string): string {
    const e = ext.toLowerCase();
    if (e === '.pdf') return 'application/pdf';
    if (e === '.doc') return 'application/msword';
    if (e === '.docx')
      return 'application/vnd.openxmlformats-officedocument.wordprocessingml.document';
    if (e === '.xls') return 'application/vnd.ms-excel';
    if (e === '.xlsx')
      return 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
    if (e === '.png') return 'image/png';
    if (e === '.jpg' || e === '.jpeg') return 'image/jpeg';
    return 'application/octet-stream';
  }
}
