import { Router, json, urlencoded } from 'express';
import multer from 'multer';
import path from 'path';
import fs from 'fs';
import { authenticateToken } from '../middlewares/auth.middleware';
import { requireForm16SubmissionAccess, requireForm1626AsAccess, requireForm16ReOnly } from '../middlewares/form16Permission.middleware';
import { form16Controller } from '../controllers/form16.controller';
import { form16SapController } from '../controllers/form16Sap.controller';
import { asyncHandler } from '../middlewares/errorHandler.middleware';
import { UPLOAD_DIR } from '../config/storage';
import { ResponseHandler } from '../utils/responseHandler';

const router = Router();

// Form16 routes are mounted before global parsers in app.ts to preserve multipart streams.
// Add route-local parsers so JSON/x-www-form-urlencoded endpoints (e.g., /26as POST/PUT) still receive req.body.
router.use(json({ limit: '10mb' }));
router.use(urlencoded({ extended: true, limit: '10mb' }));

// REform16 pattern: disk storage to uploads dir (path.join(__dirname, '../../uploads') → we use UPLOAD_DIR/form16-extract)
const form16ExtractDir = path.join(UPLOAD_DIR, 'form16-extract');
if (!fs.existsSync(form16ExtractDir)) {
  fs.mkdirSync(form16ExtractDir, { recursive: true });
}

const storage = multer.diskStorage({
  destination: (_req, _file, cb) => cb(null, form16ExtractDir),
  filename: (_req, file, cb) => {
    const uniqueSuffix = Date.now() + '-' + Math.round(Math.random() * 1e9);
    cb(null, `form16a-${uniqueSuffix}${path.extname(file.originalname || '.pdf')}`);
  },
});

// Same multer config as REform16: disk storage, 10MB, PDF only, field 'document'
const uploadExtract = multer({
  storage,
  limits: { fileSize: 10 * 1024 * 1024 },
  fileFilter: (_req, file, cb) => {
    const ext = path.extname(file.originalname || '').toLowerCase();
    if (ext === '.pdf') {
      cb(null, true);
    } else {
      cb(new Error('Only PDF files are allowed'));
    }
  },
});

// Memory storage for submission (needs buffer for GCS upload)
const upload = multer({
  storage: multer.memoryStorage(),
  limits: { fileSize: 15 * 1024 * 1024 },
});

// 26AS upload: .txt only, 40MB, memory storage (parse then bulk insert)
const upload26asTxt = multer({
  storage: multer.memoryStorage(),
  limits: { fileSize: 40 * 1024 * 1024 },
  fileFilter: (_req, file, cb) => {
    const originalName = (file.originalname || '').trim();
    const ext = path.extname(originalName).toLowerCase();
    const mime = String(file.mimetype || '').toLowerCase();
    // Keep route-level filter strict and deterministic: only .txt name + known text mime types.
    // Controller still performs deep buffer validation to block renamed binaries.
    const allowedMimes = new Set([
      'text/plain',
      'text/csv',
      'application/octet-stream',
    ]);
    const isTxt = ext === '.txt' && allowedMimes.has(mime);
    if (isTxt) {
      cb(null, true);
    } else {
      cb(new Error('Only .txt files are allowed for 26AS upload'));
    }
  },
});

const upload26asTxtSingle = (req: any, res: any, next: any) => {
  upload26asTxt.single('file')(req, res, (err: any) => {
    if (!err) return next();
    const message =
      err?.message ||
      (err?.code === 'LIMIT_FILE_SIZE'
        ? 'File too large. Maximum allowed size is 40 MB for 26AS upload.'
        : 'Invalid 26AS upload file. Only .txt files are allowed.');
    return ResponseHandler.error(res, message, 400);
  });
};

router.use(authenticateToken);

// Permissions (API-driven from admin config; used by frontend to show/hide Form 16 and 26AS)
router.get(
  '/permissions',
  asyncHandler(form16Controller.getPermissions.bind(form16Controller))
);

// Form 16 submission data (who can see: submissionViewerEmails from admin config; dealers always allowed)
router.get(
  '/credit-notes',
  requireForm16SubmissionAccess,
  asyncHandler(form16Controller.listCreditNotes.bind(form16Controller))
);
// RE only: list debit notes
router.get(
  '/debit-notes',
  requireForm16ReOnly,
  requireForm16SubmissionAccess,
  asyncHandler(form16Controller.listDebitNotes.bind(form16Controller))
);
router.get(
  '/debit-notes/:id/sap-response',
  requireForm16ReOnly,
  requireForm16SubmissionAccess,
  asyncHandler(form16Controller.viewDebitNoteSapResponse.bind(form16Controller))
);
router.get(
  '/debit-notes/:id/sap-response/csv',
  requireForm16ReOnly,
  requireForm16SubmissionAccess,
  asyncHandler(form16Controller.downloadDebitNoteSapResponseCsv.bind(form16Controller))
);
router.get(
  '/credit-notes/:id/sap-response',
  requireForm16SubmissionAccess,
  asyncHandler(form16Controller.viewCreditNoteSapResponse.bind(form16Controller))
);
router.get(
  '/credit-notes/:id/sap-response/csv',
  requireForm16SubmissionAccess,
  asyncHandler(form16Controller.downloadCreditNoteSapResponseCsv.bind(form16Controller))
);
router.get(
  '/credit-notes/:id',
  requireForm16SubmissionAccess,
  asyncHandler(form16Controller.getCreditNoteById.bind(form16Controller))
);
router.get(
  '/credit-notes/:id/download',
  requireForm16SubmissionAccess,
  asyncHandler(form16Controller.downloadCreditNote.bind(form16Controller))
);
router.get(
  '/requests/:requestId/credit-note',
  requireForm16SubmissionAccess,
  asyncHandler(form16Controller.getCreditNoteByRequest.bind(form16Controller))
);
// RE only: Form 16 request actions (cancel, resubmission needed)
router.post(
  '/requests/:requestId/cancel-submission',
  requireForm16ReOnly,
  requireForm16SubmissionAccess,
  asyncHandler(form16Controller.cancelForm16Submission.bind(form16Controller))
);
router.post(
  '/requests/:requestId/resubmission-needed',
  requireForm16ReOnly,
  requireForm16SubmissionAccess,
  asyncHandler(form16Controller.setForm16ResubmissionNeeded.bind(form16Controller))
);

// Dealer: contact admin when 26AS missing for quarter
router.post(
  '/requests/:requestId/contact-admin',
  requireForm16SubmissionAccess,
  asyncHandler(form16Controller.contactAdmin.bind(form16Controller))
);

// Pull SAP outgoing responses now (credit/debit). Used by Pull button in UI.
router.post(
  '/sap/pull',
  requireForm16SubmissionAccess,
  asyncHandler(form16SapController.pull.bind(form16SapController))
);

// Form 16 SAP simulation (credit note / debit note). Replace with real SAP when integrating.
router.post(
  '/sap-simulate/credit-note',
  requireForm16ReOnly,
  requireForm16SubmissionAccess,
  asyncHandler(form16Controller.sapSimulateCreditNote.bind(form16Controller))
);
router.post(
  '/sap-simulate/debit-note',
  requireForm16ReOnly,
  requireForm16SubmissionAccess,
  asyncHandler(form16Controller.sapSimulateDebitNote.bind(form16Controller))
);

// Dealer-only: pending submissions and pending quarters (Form 16 Pending Submissions page)
router.get(
  '/dealer/submissions',
  requireForm16SubmissionAccess,
  asyncHandler(form16Controller.listDealerSubmissions.bind(form16Controller))
);
router.get(
  '/dealer/pending-quarters',
  requireForm16SubmissionAccess,
  asyncHandler(form16Controller.listDealerPendingQuarters.bind(form16Controller))
);

router.get(
  '/non-submitted-dealers',
  requireForm16ReOnly,
  requireForm16SubmissionAccess,
  asyncHandler(form16Controller.listNonSubmittedDealers.bind(form16Controller))
);
router.post(
  '/non-submitted-dealers/notify',
  requireForm16ReOnly,
  requireForm16SubmissionAccess,
  asyncHandler(form16Controller.notifyNonSubmittedDealer.bind(form16Controller))
);

// 26AS (who can see: twentySixAsViewerEmails from admin config)
router.get(
  '/26as/dashboard',
  requireForm16ReOnly,
  requireForm1626AsAccess,
  asyncHandler(form16Controller.get26asDashboard.bind(form16Controller))
);
router.get(
  '/26as',
  requireForm16ReOnly,
  requireForm1626AsAccess,
  asyncHandler(form16Controller.list26as.bind(form16Controller))
);
router.post(
  '/26as',
  requireForm16ReOnly,
  requireForm1626AsAccess,
  asyncHandler(form16Controller.create26as.bind(form16Controller))
);
router.put(
  '/26as/:id',
  requireForm16ReOnly,
  requireForm1626AsAccess,
  asyncHandler(form16Controller.update26as.bind(form16Controller))
);
router.delete(
  '/26as/:id',
  requireForm16ReOnly,
  requireForm1626AsAccess,
  asyncHandler(form16Controller.delete26as.bind(form16Controller))
);
router.get(
  '/26as/upload-history',
  requireForm16ReOnly,
  requireForm1626AsAccess,
  asyncHandler(form16Controller.get26asUploadHistory.bind(form16Controller))
);
router.post(
  '/26as/upload',
  requireForm16ReOnly,
  requireForm1626AsAccess,
  upload26asTxtSingle,
  asyncHandler(form16Controller.upload26as.bind(form16Controller))
);

// Extract – dealers/RE with Form 16 submission access
router.post(
  '/extract',
  requireForm16SubmissionAccess,
  uploadExtract.single('document'),
  asyncHandler(form16Controller.extractOcr.bind(form16Controller))
);

// Create Form 16 submission – dealers/RE with Form 16 submission access
router.post(
  '/submissions',
  requireForm16SubmissionAccess,
  upload.single('document'),
  asyncHandler(form16Controller.createSubmission.bind(form16Controller))
);

// Dev only: send a Form 16 test notification (in-app + email via Ethereal) to current user
if (process.env.NODE_ENV !== 'production') {
  router.post(
    '/test-notification',
    asyncHandler(form16Controller.testNotification.bind(form16Controller))
  );
}

export default router;
