import fs from 'fs';
import path from 'path';
import { NextFunction, Response } from 'express';
import { ResultSetHeader, RowDataPacket } from 'mysql2/promise';
import { pool } from '../config/db.js';
import { AuthRequest } from '../middleware/auth.middleware.js';

const DOC_TYPES = new Set(['LOI', 'Contract', 'LOA']);

interface DocumentRow extends RowDataPacket {
  id: number;
  doc_type: 'LOI' | 'Contract' | 'LOA';
  file_path: string;
  original_filename: string;
  uploaded_by: number | null;
  uploaded_by_name: string | null;
  uploaded_at: string;
}

function toDto(row: DocumentRow) {
  return {
    id: row.id,
    docType: row.doc_type,
    url: `/uploads/${row.file_path}`,
    originalFilename: row.original_filename,
    uploadedByName: row.uploaded_by_name,
    uploadedAt: row.uploaded_at,
  };
}

export async function list(req: AuthRequest, res: Response, next: NextFunction) {
  const projectId = Number(req.params.id);
  try {
    const [rows] = await pool.query<DocumentRow[]>(
      `SELECT pd.id, pd.doc_type, pd.file_path, pd.original_filename, pd.uploaded_by, pd.uploaded_at, u.name AS uploaded_by_name
       FROM project_documents pd
       LEFT JOIN users u ON u.id = pd.uploaded_by
       WHERE pd.project_id = ?
       ORDER BY pd.uploaded_at DESC`,
      [projectId],
    );
    res.json(rows.map(toDto));
  } catch (err) {
    next(err);
  }
}

export async function upload(req: AuthRequest, res: Response, next: NextFunction) {
  const projectId = Number(req.params.id);
  const docType = req.body?.docType;
  const file = req.file;

  try {
    if (!file) return res.status(400).json({ message: 'A file is required.' });
    if (!DOC_TYPES.has(docType)) {
      fs.unlink(file.path, () => {});
      return res.status(400).json({ message: 'docType must be one of LOI, Contract, LOA.' });
    }

    const [projectRows] = await pool.query<RowDataPacket[]>('SELECT id FROM projects WHERE id = ?', [projectId]);
    if (!projectRows[0]) {
      fs.unlink(file.path, () => {});
      return res.status(404).json({ message: 'Project not found' });
    }

    const [result] = await pool.query<ResultSetHeader>(
      `INSERT INTO project_documents (project_id, doc_type, file_path, original_filename, uploaded_by)
       VALUES (?, ?, ?, ?, ?)`,
      [projectId, docType, `projects/${file.filename}`, file.originalname, req.userId],
    );

    const [rows] = await pool.query<DocumentRow[]>(
      `SELECT pd.id, pd.doc_type, pd.file_path, pd.original_filename, pd.uploaded_by, pd.uploaded_at, u.name AS uploaded_by_name
       FROM project_documents pd
       LEFT JOIN users u ON u.id = pd.uploaded_by
       WHERE pd.id = ?`,
      [result.insertId],
    );
    res.status(201).json(toDto(rows[0]));
  } catch (err) {
    if (file) fs.unlink(file.path, () => {});
    next(err);
  }
}

export async function remove(req: AuthRequest, res: Response, next: NextFunction) {
  const projectId = Number(req.params.id);
  const docId = Number(req.params.docId);
  try {
    const [rows] = await pool.query<DocumentRow[]>(
      'SELECT id, file_path FROM project_documents WHERE id = ? AND project_id = ?',
      [docId, projectId],
    );
    const doc = rows[0];
    if (!doc) return res.status(404).json({ message: 'Document not found' });

    await pool.query('DELETE FROM project_documents WHERE id = ?', [docId]);
    fs.unlink(path.join(process.cwd(), 'uploads', doc.file_path), () => {});

    res.status(204).send();
  } catch (err) {
    next(err);
  }
}
