import fs from "fs";
import { FastifyInstance } from "fastify";
import multipart from "@fastify/multipart";
import { z } from "zod";
import prisma from "../../config/database";
import { config } from "../../config/env";
import { requirePermission } from "../../middleware/auth";
import { logAudit } from "../../services/audit";
import { success, error } from "../../utils/response";
import { parseBody } from "../../schemas/common";
import { NasFileManager } from "../../services/nas-file-manager";

// Body for POST / (create folder). path defaults to root; the name length cap
// counts code-points (not UTF-16 units) to match the previous handler check.
// Path-traversal safety is enforced downstream by resolveProjectPath.
const CreateFolderSchema = z.object({
  folder_name: z
    .string()
    .trim()
    .min(1, "Název složky je povinný")
    .refine((s) => [...s].length <= 100, {
      message: "Název složky je příliš dlouhý (max 100 znaků)",
    }),
  path: z.string().optional().default(""),
});

// Body for PUT / (move/rename). Both endpoints are validated by
// resolveProjectPath downstream for traversal safety.
const MoveItemSchema = z.object({
  from_path: z.string().min(1, "Zdrojová cesta je povinná"),
  to_path: z.string().min(1, "Cílová cesta je povinná"),
});

const ProjectFilesQuerySchema = z.object({
  project_id: z.string().min(1, "project_id je povinný"),
  path: z.string().optional(),
  action: z.string().optional(),
});

function parseProjectFilesQuery(
  query: unknown,
):
  | { data: { project_id: string; path?: string; action?: string } }
  | { error: string } {
  try {
    const data = ProjectFilesQuerySchema.parse(query);
    return { data };
  } catch (e) {
    if (e instanceof z.ZodError) {
      return { error: e.issues.map((err) => err.message).join(", ") };
    }
    return { error: "Neplatné parametry dotazu" };
  }
}

export default async function projectFilesRoutes(
  fastify: FastifyInstance,
): Promise<void> {
  await fastify.register(multipart, {
    limits: { fileSize: config.nas.maxUploadSize },
  });

  const fm = new NasFileManager();

  async function getProjectForFiles(projectId: number) {
    if (!projectId || isNaN(projectId) || projectId <= 0) return null;
    return prisma.projects.findUnique({
      where: { id: projectId },
      select: { id: true, project_number: true, name: true },
    });
  }

  // GET / — list files or download
  fastify.get(
    "/",
    { preHandler: requirePermission("projects.view") },
    async (request, reply) => {
      const parsedQuery = parseProjectFilesQuery(request.query);
      if ("error" in parsedQuery) return error(reply, parsedQuery.error, 400);
      const { project_id: projectIdStr, path: subPath = "" } = parsedQuery.data;
      const projectId = Number(projectIdStr);
      const project = await getProjectForFiles(projectId);
      if (!project) return error(reply, "Projekt nenalezen", 404);

      if (!fm.isConfigured()) {
        return error(reply, "Souborový systém není nakonfigurován", 500);
      }

      if (parsedQuery.data.action === "download") {
        if (!subPath) return error(reply, "Cesta k souboru je povinná");
        if (!project.project_number)
          return error(reply, "Projekt nemá číslo projektu");

        const result = fm.downloadFile(project.project_number, subPath);
        if (!result) return error(reply, "Soubor nenalezen", 404);

        const stream = fs.createReadStream(result.filePath);
        const encodedName = encodeURIComponent(result.fileName);
        return reply
          .header(
            "Content-Disposition",
            `attachment; filename*=UTF-8''${encodedName}`,
          )
          .header("Content-Type", result.mime)
          .header("X-Content-Type-Options", "nosniff")
          .send(stream);
      }

      if (!project.project_number)
        return error(reply, "Projekt nemá číslo projektu");

      const result = fm.listFiles(project.project_number, subPath);
      if (result === null) {
        return error(reply, "Složka nenalezena", 404);
      }

      return success(reply, {
        ...result,
        project_number: project.project_number,
        folder_exists: true,
      });
    },
  );

  // POST / — create folder (JSON body)
  fastify.post(
    "/",
    { preHandler: requirePermission("projects.files") },
    async (request, reply) => {
      const parsedQuery = parseProjectFilesQuery(request.query);
      if ("error" in parsedQuery) return error(reply, parsedQuery.error, 400);
      const projectId = Number(parsedQuery.data.project_id);
      const project = await getProjectForFiles(projectId);
      if (!project) return error(reply, "Projekt nenalezen", 404);
      if (!project.project_number)
        return error(reply, "Projekt nemá číslo projektu");

      if (!fm.isConfigured()) {
        return error(reply, "Souborový systém není nakonfigurován", 500);
      }

      const parsedBody = parseBody(CreateFolderSchema, request.body);
      if ("error" in parsedBody) return error(reply, parsedBody.error, 400);
      const folderName = parsedBody.data.folder_name;
      const path = parsedBody.data.path;

      // Auto-create project folder if it doesn't exist
      if (!fm.projectFolderExists(project.project_number)) {
        fm.createProjectFolder(project.project_number, project.name || "");
      }

      const err = await fm.createFolder(
        project.project_number,
        path,
        folderName,
      );
      if (err !== null) return error(reply, err);

      await logAudit({
        request,
        authData: request.authData,
        action: "create",
        entityType: "project_file",
        entityId: project.id,
        description: `Vytvořena složka '${folderName}' v projektu '${project.project_number}'`,
        newValues: { folder: folderName, path },
      });

      return success(reply, null, 200, "Složka byla vytvořena");
    },
  );

  // POST /upload — upload file (multipart)
  fastify.post(
    "/upload",
    {
      preHandler: requirePermission("projects.files"),
    },
    async (request, reply) => {
      const parsedQuery = parseProjectFilesQuery(request.query);
      if ("error" in parsedQuery) return error(reply, parsedQuery.error, 400);
      const projectId = Number(parsedQuery.data.project_id);
      const project = await getProjectForFiles(projectId);
      if (!project) return error(reply, "Projekt nenalezen", 404);
      if (!project.project_number)
        return error(reply, "Projekt nemá číslo projektu");

      if (!fm.isConfigured()) {
        return error(reply, "Souborový systém není nakonfigurován", 500);
      }

      // Auto-create project folder if it doesn't exist
      if (!fm.projectFolderExists(project.project_number)) {
        fm.createProjectFolder(project.project_number, project.name || "");
      }

      const file = await request.file();
      if (!file) return error(reply, "Nebyl nahrán žádný soubor");

      const subPath = parsedQuery.data.path || "";
      const rawFileName = file.filename;
      const fileName = rawFileName.replace(/[\/\\:*?"<>|]/g, "_");

      const buffer = await file.toBuffer();
      const err = await fm.uploadFile(
        project.project_number,
        subPath,
        buffer,
        fileName,
      );
      if (err !== null) {
        request.log.error(
          {
            err,
            project_number: project.project_number,
            subPath,
            fileName,
          },
          "uploadFile failed",
        );
        return error(reply, err);
      }

      await logAudit({
        request,
        authData: request.authData,
        action: "create",
        entityType: "project_file",
        entityId: project.id,
        description: `Nahrán soubor do projektu '${project.project_number}'`,
        newValues: { file: fileName, path: subPath },
      });

      return success(reply, null, 200, "Soubor byl nahrán");
    },
  );

  // PUT / — move/rename
  fastify.put(
    "/",
    { preHandler: requirePermission("projects.files") },
    async (request, reply) => {
      const parsedQuery = parseProjectFilesQuery(request.query);
      if ("error" in parsedQuery) return error(reply, parsedQuery.error, 400);
      const projectId = Number(parsedQuery.data.project_id);
      const project = await getProjectForFiles(projectId);
      if (!project) return error(reply, "Projekt nenalezen", 404);
      if (!project.project_number)
        return error(reply, "Projekt nemá číslo projektu");

      if (!fm.isConfigured()) {
        return error(reply, "Souborový systém není nakonfigurován", 500);
      }

      const parsedBody = parseBody(MoveItemSchema, request.body);
      if ("error" in parsedBody) return error(reply, parsedBody.error, 400);
      const fromPath = parsedBody.data.from_path;
      const toPath = parsedBody.data.to_path;

      const err = await fm.moveItem(project.project_number, fromPath, toPath);
      if (err !== null) return error(reply, err);

      await logAudit({
        request,
        authData: request.authData,
        action: "update",
        entityType: "project_file",
        entityId: project.id,
        description: `Přesun/přejmenování v projektu '${project.project_number}'`,
        oldValues: { path: fromPath },
        newValues: { path: toPath },
      });

      return success(reply, null, 200, "Soubor byl přesunut");
    },
  );

  // DELETE / — delete file/folder
  fastify.delete(
    "/",
    { preHandler: requirePermission("projects.files") },
    async (request, reply) => {
      const parsedQuery = parseProjectFilesQuery(request.query);
      if ("error" in parsedQuery) return error(reply, parsedQuery.error, 400);
      const projectId = Number(parsedQuery.data.project_id);
      const project = await getProjectForFiles(projectId);
      if (!project) return error(reply, "Projekt nenalezen", 404);
      if (!project.project_number)
        return error(reply, "Projekt nemá číslo projektu");

      if (!fm.isConfigured()) {
        return error(reply, "Souborový systém není nakonfigurován", 500);
      }

      const filePath = parsedQuery.data.path || "";
      if (!filePath) return error(reply, "Cesta k souboru je povinná");

      const err = await fm.deleteItem(project.project_number, filePath);
      if (err !== null) return error(reply, err);

      await logAudit({
        request,
        authData: request.authData,
        action: "delete",
        entityType: "project_file",
        entityId: project.id,
        description: `Smazán soubor/složka v projektu '${project.project_number}'`,
        oldValues: { path: filePath },
      });

      return success(reply, null, 200, "Soubor byl smazán");
    },
  );
}
