import { Router } from "express";
import { Readable } from "node:stream";
import multer from "multer";
import { google } from "googleapis";
import { z } from "zod";
import { requireAuth, requireCapability } from "../../middleware/auth";
import { getOwnerGoogleClient, sendGoogleError } from "../../lib/google-user";

export const driveRouter = Router();
driveRouter.use(requireAuth);

const upload = multer({ storage: multer.memoryStorage(), limits: { fileSize: 100 * 1024 * 1024 } });

const FOLDER_MIME = "application/vnd.google-apps.folder";

// Drive du propriétaire (Super Admin) — compte partagé pour tous les utilisateurs.
async function driveClient() {
  const auth = await getOwnerGoogleClient();
  return google.drive({ version: "v3", auth });
}

// GET /drive/files — liste le contenu d'un dossier (folderId, défaut "root") ou recherche (q).
driveRouter.get("/files", requireCapability("drive:read"), async (req, res) => {
  try {
    const drive = await driveClient();
    const folderId = req.query.folderId ? String(req.query.folderId) : "root";
    const search = req.query.q ? String(req.query.q).trim() : "";
    const pageToken = req.query.pageToken ? String(req.query.pageToken) : undefined;

    const q = search
      ? `name contains '${search.replace(/'/g, "\\'")}' and trashed = false`
      : `'${folderId}' in parents and trashed = false`;

    const { data } = await drive.files.list({
      q,
      fields: "nextPageToken, files(id, name, mimeType, size, modifiedTime, webViewLink)",
      orderBy: "folder,name",
      pageSize: 100,
      pageToken,
      spaces: "drive",
    });

    const files = (data.files ?? []).map((f) => ({
      id: f.id,
      name: f.name,
      mimeType: f.mimeType,
      isFolder: f.mimeType === FOLDER_MIME,
      size: f.size ? Number(f.size) : null,
      modifiedTime: f.modifiedTime ?? null,
      webViewLink: f.webViewLink ?? null,
    }));

    res.json({ files, nextPageToken: data.nextPageToken ?? null });
  } catch (err) {
    sendGoogleError(res, err, "Google Drive");
  }
});

// Sert un fichier Drive en flux (les docs Google natifs sont exportés en PDF).
// disposition = "attachment" (téléchargement) ou "inline" (affichage dans le visualiseur).
async function streamDriveFile(res: import("express").Response, id: string, disposition: "attachment" | "inline") {
  const drive = await driveClient();
  const meta = await drive.files.get({ fileId: id, fields: "name, mimeType" });
  const name = meta.data.name ?? "fichier";
  const mime = meta.data.mimeType ?? "application/octet-stream";

  if (mime.startsWith("application/vnd.google-apps")) {
    res.setHeader("Content-Disposition", `${disposition}; filename="${encodeURIComponent(name)}.pdf"`);
    res.setHeader("Content-Type", "application/pdf");
    const resp = await drive.files.export({ fileId: id, mimeType: "application/pdf" }, { responseType: "stream" });
    (resp.data as Readable).pipe(res);
  } else {
    res.setHeader("Content-Disposition", `${disposition}; filename="${encodeURIComponent(name)}"`);
    res.setHeader("Content-Type", mime);
    const resp = await drive.files.get({ fileId: id, alt: "media" }, { responseType: "stream" });
    (resp.data as Readable).pipe(res);
  }
}

// GET /drive/files/:id/download — téléchargement (pièce jointe).
driveRouter.get("/files/:id/download", requireCapability("drive:read"), async (req, res) => {
  try {
    await streamDriveFile(res, String(req.params.id), "attachment");
  } catch (err) {
    if (!res.headersSent) sendGoogleError(res, err, "Google Drive");
    else res.end();
  }
});

// GET /drive/files/:id/view — affichage en ligne (pour le visualiseur intégré).
driveRouter.get("/files/:id/view", requireCapability("drive:read"), async (req, res) => {
  try {
    await streamDriveFile(res, String(req.params.id), "inline");
  } catch (err) {
    if (!res.headersSent) sendGoogleError(res, err, "Google Drive");
    else res.end();
  }
});

// POST /drive/files — upload d'un fichier dans un dossier (écriture).
driveRouter.post("/files", requireCapability("drive:write"), upload.single("file"), async (req, res) => {
  if (!req.file) return res.status(400).json({ error: "invalid", message: "Aucun fichier reçu." });
  try {
    const drive = await driveClient();
    const folderId = req.body.folderId ? String(req.body.folderId) : "root";
    const { data } = await drive.files.create({
      requestBody: { name: req.file.originalname, parents: [folderId] },
      media: { mimeType: req.file.mimetype, body: Readable.from(req.file.buffer) },
      fields: "id, name",
    });
    res.status(201).json({ file: { id: data.id, name: data.name } });
  } catch (err) {
    sendGoogleError(res, err, "Google Drive");
  }
});

// POST /drive/folders — créer un dossier (écriture).
const folderSchema = z.object({ name: z.string().min(1).max(255), parentId: z.string().optional() });
driveRouter.post("/folders", requireCapability("drive:write"), async (req, res) => {
  const parsed = folderSchema.safeParse(req.body);
  if (!parsed.success) return res.status(400).json({ error: "invalid", message: "Nom de dossier invalide." });
  try {
    const drive = await driveClient();
    const { data } = await drive.files.create({
      requestBody: {
        name: parsed.data.name,
        mimeType: FOLDER_MIME,
        parents: [parsed.data.parentId ?? "root"],
      },
      fields: "id, name",
    });
    res.status(201).json({ folder: { id: data.id, name: data.name } });
  } catch (err) {
    sendGoogleError(res, err, "Google Drive");
  }
});

// PATCH /drive/files/:id — renommer (écriture).
const renameSchema = z.object({ name: z.string().min(1).max(255) });
driveRouter.patch("/files/:id", requireCapability("drive:write"), async (req, res) => {
  const parsed = renameSchema.safeParse(req.body);
  if (!parsed.success) return res.status(400).json({ error: "invalid", message: "Nom invalide." });
  try {
    const drive = await driveClient();
    const { data } = await drive.files.update({
      fileId: String(req.params.id),
      requestBody: { name: parsed.data.name },
      fields: "id, name",
    });
    res.json({ file: { id: data.id, name: data.name } });
  } catch (err) {
    sendGoogleError(res, err, "Google Drive");
  }
});

// POST /drive/files/:id/move — déplacer vers un autre dossier (écriture).
const moveSchema = z.object({ targetFolderId: z.string().min(1) });
driveRouter.post("/files/:id/move", requireCapability("drive:write"), async (req, res) => {
  const parsed = moveSchema.safeParse(req.body);
  if (!parsed.success) return res.status(400).json({ error: "invalid", message: "Dossier cible requis." });
  const id = String(req.params.id);
  const target = parsed.data.targetFolderId;
  if (id === target) {
    return res.status(400).json({ error: "invalid", message: "Impossible de déplacer un dossier dans lui-même." });
  }
  try {
    const drive = await driveClient();
    const meta = await drive.files.get({ fileId: id, fields: "parents" });
    const removeParents = (meta.data.parents ?? []).join(",");
    if (removeParents.split(",").includes(target)) {
      return res.json({ ok: true }); // déjà dans ce dossier
    }
    await drive.files.update({
      fileId: id,
      addParents: target,
      removeParents: removeParents || undefined,
      fields: "id, parents",
    });
    res.json({ ok: true });
  } catch (err) {
    sendGoogleError(res, err, "Google Drive");
  }
});

// DELETE /drive/files/:id — mettre à la corbeille (écriture).
driveRouter.delete("/files/:id", requireCapability("drive:write"), async (req, res) => {
  try {
    const drive = await driveClient();
    await drive.files.update({ fileId: String(req.params.id), requestBody: { trashed: true } });
    res.json({ ok: true });
  } catch (err) {
    sendGoogleError(res, err, "Google Drive");
  }
});
