import fs from 'fs';
import path from 'path';
import logger from '../utils/logger';
import { wfmFileService } from '../services/wfmFile.service';
import {
  Form16CreditNote,
  Form16DebitNote,
  Form16SapResponse,
  From16SapReadFile,
} from '../models';

type CsvRow = Record<string, string | undefined>;

function extractCsvFields(r: CsvRow) {
  const trnsUniqNo = (r.TRNS_UNIQ_NO || r.TRNSUNIQNO || '').trim() || null;
  const tdsTransId = (r.TDS_TRNS_ID || '').trim() || null;
  const docNo = (r.DOC_NO || r.DOCNO || '').trim() || null;
  const msgTyp = (r.MSG_TYP || r.MSGTYP || '').trim() || null;
  const message = (r.MESSAGE || '').trim() || null;
  return { trnsUniqNo, tdsTransId, docNo, msgTyp, message };
}

function isUsableRow(r: CsvRow): boolean {
  const { tdsTransId } = extractCsvFields(r);
  if (!tdsTransId) return false;
  const upper = tdsTransId.toUpperCase();
  if (upper === 'TDS_TRNS_ID' || upper === 'MSG_TYP' || upper === 'MESSAGE') return false;
  return true;
}

async function saveRowsAndUpdateNotes(rows: CsvRow[]): Promise<{ totalRecords: number; totalCreditNotes: number; totalDebitNotes: number }> {
  let totalRecords = 0;
  let totalCreditNotes = 0;
  let totalDebitNotes = 0;

  for (const row of rows) {
    if (!isUsableRow(row)) continue;
    const parsed = extractCsvFields(row);
    if (!parsed.tdsTransId) continue;

    await (Form16SapResponse as any).create({
      trnsUniqNo: parsed.trnsUniqNo,
      tdsTransId: parsed.tdsTransId,
      docNo: parsed.docNo,
      msgTyp: parsed.msgTyp,
      message: parsed.message,
      createdAt: new Date(),
      updatedAt: new Date(),
    });
    totalRecords++;

    const idUpper = parsed.tdsTransId.toUpperCase();
    if (idUpper.startsWith('CN')) {
      totalCreditNotes++;
      await (Form16CreditNote as any).update(
        {
          sapDocumentNumber: parsed.docNo,
          status: 'completed',
        },
        { where: { creditNoteNumber: parsed.tdsTransId } }
      );
    } else if (idUpper.startsWith('DN')) {
      totalDebitNotes++;
      await (Form16DebitNote as any).update(
        {
          sapDocumentNumber: parsed.docNo,
          status: 'completed',
        },
        { where: { debitNoteNumber: parsed.tdsTransId } }
      );
    }
  }

  return { totalRecords, totalCreditNotes, totalDebitNotes };
}

async function processOutgoingFile(fileName: string, resolvedOutgoingDir: string): Promise<{ totalRecords: number; totalCreditNotes: number; totalDebitNotes: number } | null> {
  const alreadyRead = await (From16SapReadFile as any).findOne({
    where: { fileName },
    attributes: ['id'],
  });
  if (alreadyRead) {
    logger.debug(`[Form16 SAP Job] Skipping already-read file: ${fileName}`);
    return null;
  }

  const rows = (await wfmFileService.readForm16OutgoingResponseByPath(path.join(resolvedOutgoingDir, fileName))) as CsvRow[];
  const counts = await saveRowsAndUpdateNotes(rows || []);

  await (From16SapReadFile as any).create({
    fileName,
    totalRecords: counts.totalRecords,
    totalCreditNotes: counts.totalCreditNotes,
    totalDebitNotes: counts.totalDebitNotes,
    createdAt: new Date(),
    updatedAt: new Date(),
  });

  // Delete source CSV only after successful DB persistence + read-marking.
  // SAP team keeps a parallel archive copy, so main OUTGOING can be safely cleaned.
  const sourcePath = path.join(resolvedOutgoingDir, fileName);
  try {
    if (fs.existsSync(sourcePath)) {
      fs.unlinkSync(sourcePath);
      logger.info(`[Form16 SAP Job] Deleted processed OUTGOING file: ${sourcePath}`);
    }
  } catch (e) {
    // Keep processing successful even if cleanup fails; next pull will skip due to read marker.
    logger.warn(`[Form16 SAP Job] Could not delete processed file: ${sourcePath}`, e);
  }

  return counts;
}

// ─── Public API (called by Pull button controller) ────────────────────────────

/**
 * Scan both OUTGOING dirs, read every CSV, match to a DB note via TDS_TRNS_ID (primary),
 * TRNS_UNIQ_NO, CLAIM_NUMBER, or filename (fallbacks), save all known CSV columns to their
 * own DB columns and any extra columns to raw_row.
 *
 * Called by POST /form16/sap/pull  –  no scheduler, Pull button is the only trigger.
 */
export async function runForm16SapResponseIngestionOnce(): Promise<{
  processed: number;
  creditProcessed: number;
  debitProcessed: number;
  filesProcessed: number;
}> {
  let creditProcessed = 0;
  let debitProcessed  = 0;
  let filesProcessed = 0;

  const RELATIVE_FORM16_OUT = path.join('WFM-QRE', 'OUTGOING', 'WFM_SAP_MAIN', 'FORM16');
  const resolvedDirs = [
    path.dirname(wfmFileService.getForm16OutgoingPath('__probe__.csv', 'credit')),
    path.dirname(wfmFileService.getForm16OutgoingPath('__probe__.csv', 'debit')),
  ];
  const dirs: Array<{ dir: string; relSubdir: string }> = [...new Set(resolvedDirs)].map((d) => ({
    dir: d,
    relSubdir: RELATIVE_FORM16_OUT,
  }));

  try {
    const base = process.env.WFM_BASE_PATH || 'C:\\WFM';

    for (const { dir, relSubdir } of dirs) {
      let abs = path.isAbsolute(dir) ? dir : path.join(base, dir);

      if (!fs.existsSync(abs)) {
        const cwdFallback = path.join(process.cwd(), relSubdir);
        if (fs.existsSync(cwdFallback)) {
          abs = cwdFallback;
          logger.info(`[Form16 SAP Job] OUTGOING dir resolved via cwd: ${abs}`);
        } else {
          logger.warn(
            `[Form16 SAP Job] OUTGOING dir not found. Tried: ${abs} | ${cwdFallback}. ` +
            `Set WFM_BASE_PATH to the folder containing WFM-QRE.`
          );
          continue;
        }
      }

      const files = fs.readdirSync(abs).filter((f) => f.toLowerCase().endsWith('.csv'));
      logger.info(`[Form16 SAP Job] OUTGOING dir: ${abs} → ${files.length} CSV file(s)${files.length ? ': ' + files.join(', ') : ''}`);

      for (const f of files) {
        try {
          const counts = await processOutgoingFile(f, abs);
          if (!counts) continue;
          filesProcessed++;
          creditProcessed += counts.totalCreditNotes;
          debitProcessed += counts.totalDebitNotes;
        } catch (e) {
          logger.error(`[Form16 SAP Job] Error processing file ${f}:`, e);
        }
      }
    }
  } catch (e) {
    logger.error('[Form16 SAP Job] Ingestion error:', e);
  }

  logger.info(
    `[Form16 SAP Job] Pull complete – credit: ${creditProcessed}, debit: ${debitProcessed}, total: ${creditProcessed + debitProcessed}`
  );

  return {
    processed: creditProcessed + debitProcessed,
    creditProcessed,
    debitProcessed,
    filesProcessed,
  };
}
