import { Router } from 'express';
import multer from 'multer';
import path from 'node:path';
import fs from 'node:fs';
import { fileURLToPath } from 'node:url';
import db from '../db/index.js';
import { requireAuth, requireAdmin } from '../middleware/auth.js';

const __dirname = path.dirname(fileURLToPath(import.meta.url));

// ── Stockage fichiers ──────────────────────────────────────────────────────
const dataDir = process.env.DATA_DIR
  ? path.resolve(process.env.DATA_DIR)
  : path.resolve(__dirname, '../../../data');
const attachDir = path.join(dataDir, 'attachments');
fs.mkdirSync(attachDir, { recursive: true });

const storage = multer.diskStorage({
  destination: (_, __, cb) => cb(null, attachDir),
  filename: (_, file, cb) => {
    const ext  = path.extname(file.originalname);
    const base = `${Date.now()}-${Math.random().toString(36).slice(2)}`;
    cb(null, base + ext);
  },
});
const upload = multer({ storage, limits: { fileSize: 20 * 1024 * 1024 } }); // 20 Mo max

const router = Router();
router.use(requireAuth);

// ── Helpers ────────────────────────────────────────────────────────────────
function generateTicketNumber() {
  const year = new Date().getFullYear();
  const last = db.prepare(
    "SELECT ticket_number FROM tickets WHERE ticket_number LIKE ? ORDER BY id DESC LIMIT 1"
  ).get(`TK-${year}-%`);
  const seq = last ? parseInt(last.ticket_number.split('-')[2], 10) + 1 : 1;
  return `TK-${year}-${String(seq).padStart(4, '0')}`;
}

function notifyAdmins(title, body, link) {
  const admins = db.prepare("SELECT id FROM users WHERE role = 'admin'").all();
  const insert = db.prepare(
    'INSERT INTO notifications (user_id, type, title, body, link) VALUES (?, ?, ?, ?, ?)'
  );
  const tx = db.transaction((items) => {
    for (const admin of items) insert.run(admin.id, 'ticket_reply', title, body, link);
  });
  tx(admins);
}

function notifyUser(userId, title, body, link) {
  db.prepare(
    'INSERT INTO notifications (user_id, type, title, body, link) VALUES (?, ?, ?, ?, ?)'
  ).run(userId, 'ticket_reply', title, body, link);
}

function attachmentsForMessage(messageId) {
  return db.prepare(
    'SELECT id, filename, original_name, size, mime_type FROM ticket_attachments WHERE message_id = ?'
  ).all(messageId);
}

// ── GET /api/tickets ─────────────────────────────────────────────────────
// Admin : tous les tickets. User : ses tickets uniquement.
router.get('/', (req, res) => {
  const { id: userId, role } = req.user;
  const isAdmin = role === 'admin';

  const rows = isAdmin
    ? db.prepare(`
        SELECT t.*, u.display_name as user_name, u.email as user_email,
          (SELECT COUNT(*) FROM ticket_messages WHERE ticket_id = t.id) as message_count,
          (SELECT body FROM ticket_messages WHERE ticket_id = t.id ORDER BY created_at DESC LIMIT 1) as last_body,
          (SELECT created_at FROM ticket_messages WHERE ticket_id = t.id ORDER BY created_at DESC LIMIT 1) as last_message_at
        FROM tickets t
        JOIN users u ON u.id = t.user_id
        ORDER BY t.updated_at DESC
      `).all()
    : db.prepare(`
        SELECT t.*, u.display_name as user_name, u.email as user_email,
          (SELECT COUNT(*) FROM ticket_messages WHERE ticket_id = t.id) as message_count,
          (SELECT body FROM ticket_messages WHERE ticket_id = t.id ORDER BY created_at DESC LIMIT 1) as last_body,
          (SELECT created_at FROM ticket_messages WHERE ticket_id = t.id ORDER BY created_at DESC LIMIT 1) as last_message_at
        FROM tickets t
        JOIN users u ON u.id = t.user_id
        WHERE t.user_id = ?
        ORDER BY t.updated_at DESC
      `).all(userId);

  res.json({ tickets: rows });
});

// ── POST /api/tickets — créer un ticket ───────────────────────────────────
router.post('/', upload.array('attachments', 10), (req, res) => {
  const { id: userId } = req.user;
  const { subject, body, ticket_type, ticket_pages } = req.body;
  if (!subject?.trim() || !body?.trim()) {
    return res.status(400).json({ error: 'Sujet et message requis' });
  }

  const ticketNumber = generateTicketNumber();

  const tx = db.transaction(() => {
    const ticketResult = db.prepare(
      'INSERT INTO tickets (user_id, ticket_number, subject, ticket_type, ticket_pages) VALUES (?, ?, ?, ?, ?)'
    ).run(userId, ticketNumber, subject.trim(), ticket_type || null, ticket_pages || null);
    const ticketId = ticketResult.lastInsertRowid;

    const msgResult = db.prepare(
      'INSERT INTO ticket_messages (ticket_id, user_id, body, is_admin) VALUES (?, ?, ?, 0)'
    ).run(ticketId, userId, body.trim());
    const messageId = msgResult.lastInsertRowid;

    // Pièces jointes
    if (req.files?.length) {
      const insertAttach = db.prepare(
        'INSERT INTO ticket_attachments (message_id, filename, original_name, size, mime_type) VALUES (?, ?, ?, ?, ?)'
      );
      for (const f of req.files) {
        insertAttach.run(messageId, f.filename, f.originalname, f.size, f.mimetype);
      }
    }

    return { ticketId, ticketNumber };
  });

  const { ticketId, ticketNumber: tn } = tx();

  // Notifier les admins
  const user = db.prepare('SELECT display_name as name FROM users WHERE id = ?').get(userId);
  notifyAdmins(
    `[${tn}] Nouveau ticket : ${subject.trim()}`,
    `${user?.name ?? 'Utilisateur'} a ouvert un ticket de support.`,
    `/communication?ticket=${ticketId}`
  );

  res.status(201).json({ ok: true, ticketId, ticketNumber: tn });
});

// ── GET /api/tickets/:id — détail ticket + messages ───────────────────────
router.get('/:id', (req, res) => {
  const { id: userId, role } = req.user;
  const isAdmin = role === 'admin';

  const ticket = db.prepare(`
    SELECT t.*, u.display_name as user_name, u.email as user_email,
           a.display_name as assigned_name
    FROM tickets t
    JOIN users u ON u.id = t.user_id
    LEFT JOIN users a ON a.id = t.assigned_to
    WHERE t.id = ?
  `).get(req.params.id);

  if (!ticket) return res.status(404).json({ error: 'Ticket introuvable' });
  if (!isAdmin && ticket.user_id !== userId) return res.status(403).json({ error: 'Accès refusé' });

  const messages = db.prepare(`
    SELECT m.*, u.display_name as author_name, u.email as author_email
    FROM ticket_messages m JOIN users u ON u.id = m.user_id
    WHERE m.ticket_id = ?
    ORDER BY m.created_at ASC
  `).all(req.params.id);

  for (const msg of messages) {
    msg.attachments = attachmentsForMessage(msg.id);
  }

  res.json({ ticket, messages });
});

// ── POST /api/tickets/:id/messages — répondre à un ticket ─────────────────
router.post('/:id/messages', upload.array('attachments', 10), (req, res) => {
  const { id: userId, role } = req.user;
  const isAdmin = role === 'admin';

  const ticket = db.prepare('SELECT * FROM tickets WHERE id = ?').get(req.params.id);
  if (!ticket) return res.status(404).json({ error: 'Ticket introuvable' });
  if (!isAdmin && ticket.user_id !== userId) return res.status(403).json({ error: 'Accès refusé' });
  if (ticket.status === 'resolved' && !isAdmin) return res.status(400).json({ error: 'Ticket résolu' });

  const { body } = req.body;
  if (!body?.trim()) return res.status(400).json({ error: 'Message vide' });

  const tx = db.transaction(() => {
    const msgResult = db.prepare(
      'INSERT INTO ticket_messages (ticket_id, user_id, body, is_admin) VALUES (?, ?, ?, ?)'
    ).run(ticket.id, userId, body.trim(), isAdmin ? 1 : 0);
    const messageId = msgResult.lastInsertRowid;

    if (req.files?.length) {
      const insertAttach = db.prepare(
        'INSERT INTO ticket_attachments (message_id, filename, original_name, size, mime_type) VALUES (?, ?, ?, ?, ?)'
      );
      for (const f of req.files) {
        insertAttach.run(messageId, f.filename, f.originalname, f.size, f.mimetype);
      }
    }

    // Réouverture automatique si l'émetteur répond sur un ticket en attente
    if (!isAdmin && ticket.status === 'pending') {
      db.prepare("UPDATE tickets SET status = 'open', updated_at = datetime('now') WHERE id = ?").run(ticket.id);
    } else {
      db.prepare("UPDATE tickets SET updated_at = datetime('now') WHERE id = ?").run(ticket.id);
    }
    return messageId;
  });

  tx();

  // Notifications croisées
  if (isAdmin) {
    notifyUser(
      ticket.user_id,
      `[${ticket.ticket_number}] Réponse à votre ticket`,
      `L'équipe support a répondu à votre ticket "${ticket.subject}".`,
      `/communication?ticket=${ticket.id}`
    );
    window?.dispatchEvent; // no-op server side
  } else {
    const user = db.prepare('SELECT display_name as name FROM users WHERE id = ?').get(userId);
    notifyAdmins(
      `[${ticket.ticket_number}] Nouvelle réponse`,
      `${user?.name ?? 'Utilisateur'} a répondu sur le ticket "${ticket.subject}".`,
      `/communication?ticket=${ticket.id}`
    );
  }

  res.json({ ok: true });
});

// ── PATCH /api/tickets/:id/status — résoudre / rouvrir (admin) ────────────
router.patch('/:id/status', requireAdmin, (req, res) => {
  const { status } = req.body;
  if (!['open', 'resolved', 'pending'].includes(status)) return res.status(400).json({ error: 'Statut invalide' });

  const ticket = db.prepare('SELECT id FROM tickets WHERE id = ?').get(req.params.id);
  if (!ticket) return res.status(404).json({ error: 'Ticket introuvable' });

  db.prepare("UPDATE tickets SET status = ?, updated_at = datetime('now') WHERE id = ?")
    .run(status, req.params.id);
  res.json({ ok: true });
});

// ── PATCH /api/tickets/:id/categorize — modifier type et pages ───────────────
router.patch('/:id/categorize', (req, res) => {
  const { id: userId, role } = req.user;
  const isAdmin = role === 'admin';

  const ticket = db.prepare('SELECT * FROM tickets WHERE id = ?').get(req.params.id);
  if (!ticket) return res.status(404).json({ error: 'Ticket introuvable' });
  if (!isAdmin && ticket.user_id !== userId) return res.status(403).json({ error: 'Accès refusé' });

  const { ticket_type, ticket_pages } = req.body;
  db.prepare("UPDATE tickets SET ticket_type = ?, ticket_pages = ?, updated_at = datetime('now') WHERE id = ?")
    .run(ticket_type ?? null, ticket_pages ?? null, req.params.id);
  res.json({ ok: true });
});

// ── PATCH /api/tickets/:id/assign — assigner à un admin ──────────────────────
router.patch('/:id/assign', requireAdmin, (req, res) => {
  const { assigned_to } = req.body; // null pour désassigner
  if (assigned_to !== null && assigned_to !== undefined) {
    const admin = db.prepare("SELECT id FROM users WHERE id = ? AND role = 'admin'").get(assigned_to);
    if (!admin) return res.status(400).json({ error: 'Utilisateur invalide ou non admin' });
  }
  const ticket = db.prepare('SELECT id FROM tickets WHERE id = ?').get(req.params.id);
  if (!ticket) return res.status(404).json({ error: 'Ticket introuvable' });

  db.prepare("UPDATE tickets SET assigned_to = ?, updated_at = datetime('now') WHERE id = ?")
    .run(assigned_to ?? null, req.params.id);
  res.json({ ok: true });
});

// ── PUT /api/tickets/:id/messages/:msgId — éditer un message (5 min) ────────
router.put('/:id/messages/:msgId', (req, res) => {
  const { id: userId } = req.user;
  const { body } = req.body;
  if (!body?.trim()) return res.status(400).json({ error: 'Message vide' });

  const msg = db.prepare('SELECT * FROM ticket_messages WHERE id = ?').get(req.params.msgId);
  if (!msg) return res.status(404).json({ error: 'Message introuvable' });

  // Seul l'auteur peut modifier son propre message
  if (msg.user_id !== userId) return res.status(403).json({ error: 'Accès refusé' });

  // Vérifier la fenêtre de 5 minutes depuis created_at
  const created = new Date(msg.created_at + 'Z');
  const ageSeconds = (Date.now() - created.getTime()) / 1000;
  if (ageSeconds > 300) {
    return res.status(403).json({ error: 'Délai de modification expiré (5 minutes)' });
  }

  db.prepare("UPDATE ticket_messages SET body = ?, updated_at = datetime('now') WHERE id = ?")
    .run(body.trim(), msg.id);

  res.json({ ok: true });
});

// ── GET /api/tickets/attachments/:filename — servir un fichier ────────────
router.get('/attachments/:filename', (req, res) => {
  const { id: userId, role } = req.user;
  const isAdmin = role === 'admin';

  // Vérifier que l'utilisateur a accès à cette pièce jointe
  const attach = db.prepare(`
    SELECT ta.*, tm.ticket_id FROM ticket_attachments ta
    JOIN ticket_messages tm ON tm.id = ta.message_id
    JOIN tickets t ON t.id = tm.ticket_id
    WHERE ta.filename = ? AND (t.user_id = ? OR ? = 1)
  `).get(req.params.filename, userId, isAdmin ? 1 : 0);

  if (!attach) return res.status(404).json({ error: 'Fichier introuvable' });

  const filePath = path.join(attachDir, req.params.filename);
  res.setHeader('Content-Disposition', `inline; filename="${attach.original_name}"`);
  res.sendFile(filePath);
});

export default router;
