import { Router, type Request, type Response } from 'express';
import path from 'path';
import fs from 'fs';
import { prisma } from '../lib/prisma';
import { resolveTaskActorId } from '../lib/resolveUser';
import { upload } from '../middleware/upload';
import { AppError } from '../middleware/errorHandler';
import { cleanupUploadedFile, DISK_FULL_MESSAGE, isDiskFullError } from '../lib/uploadErrors';
import { toHtml } from '@ohah/hwpjs';
import { resolveFileMime } from '../lib/fileMime';

const router = Router();

const PREVIEW_FRAME_ANCESTORS = [
  "'self'",
  'https://eene-dashboard.vercel.app',
  'http://localhost:3000',
  'https://localhost:3000',
  'http://127.0.0.1:3000',
  'https://127.0.0.1:3000',
];

function isPrivateDevOrigin(origin: string): boolean {
  return (
    /^https?:\/\/(localhost|127\.0\.0\.1)(:\d+)?$/i.test(origin) ||
    /^https?:\/\/172\.(1[6-9]|2\d|3[01])\.\d+\.\d+(:\d+)?$/.test(origin) ||
    /^https?:\/\/192\.168\.\d+\.\d+(:\d+)?$/.test(origin) ||
    /^https?:\/\/10\.\d+\.\d+\.\d+(:\d+)?$/.test(origin)
  );
}

/** PDF 등 iframe 미리보기 — localhost·LAN https 포함 */
function allowCrossOriginPreview(req: Request, res: Response) {
  res.setHeader('Cross-Origin-Resource-Policy', 'cross-origin');
  const ancestors = [...PREVIEW_FRAME_ANCESTORS];
  const origin = req.headers.origin;
  if (origin && isPrivateDevOrigin(origin) && !ancestors.includes(origin)) {
    ancestors.push(origin);
  }
  res.setHeader('Content-Security-Policy', `frame-ancestors ${ancestors.join(' ')}`);
  res.removeHeader('X-Frame-Options');
}

/** multer가 latin1로 전달하는 한글 파일명 복원 */
function fixOriginalName(name: string): string {
  try {
    return Buffer.from(name, 'latin1').toString('utf8') || name;
  } catch {
    return name;
  }
}

// POST /api/files/upload/:taskId  — 파일 업로드
router.post('/upload/:taskId', upload.single('file'), async (req, res, next) => {
  try {
    if (!req.file) throw new AppError(400, '파일이 없습니다.');
    const taskId = String(req.params.taskId);

    const task = await prisma.task.findUnique({ where: { id: taskId } });
    if (!task) throw new AppError(404, '업무를 찾을 수 없습니다.');

    const body = req.body as Record<string, string>;
    const milestoneId = body.milestoneId?.trim() || null;

    if (milestoneId) {
      const milestone = await prisma.milestone.findFirst({
        where: { id: milestoneId, taskId },
      });
      if (!milestone) throw new AppError(404, '단계를 찾을 수 없습니다.');
    }

    const uploadedBy = await resolveTaskActorId(taskId);

    const displayName = body.displayName?.trim() || null;
    const sortOrder = body.sortOrder !== undefined ? Number(body.sortOrder) : 0;

    const fileRecord = await prisma.file.create({
      data: {
        taskId,
        milestoneId,
        filename: req.file.filename,
        originalName: fixOriginalName(req.file.originalname),
        displayName,
        sortOrder: Number.isNaN(sortOrder) ? 0 : sortOrder,
        mimetype: resolveFileMime(fixOriginalName(req.file.originalname), req.file.mimetype),
        size: req.file.size,
        path: req.file.path,
        uploadedBy,
      },
    });

    res.status(201).json(fileRecord);
  } catch (err) {
    if (isDiskFullError(err)) {
      cleanupUploadedFile(req.file);
      next(new AppError(507, DISK_FULL_MESSAGE));
      return;
    }
    next(err);
  }
});

function isHwpOriginalName(originalName: string): boolean {
  const ext = originalName.split('.').pop()?.toLowerCase() ?? '';
  return ext === 'hwp' || ext === 'hwpx';
}

// GET /api/files/:id/hwp-preview — 한글(.hwp/.hwpx) HTML 미리보기
router.get('/:id/hwp-preview', async (req, res, next) => {
  try {
    const fileId = String(req.params.id);
    const file = await prisma.file.findUnique({ where: { id: fileId } });
    if (!file) throw new AppError(404, '파일을 찾을 수 없습니다.');
    if (!fs.existsSync(file.path)) throw new AppError(404, '파일이 서버에 없습니다.');
    if (!isHwpOriginalName(file.originalName)) {
      throw new AppError(400, '한글 파일만 미리보기할 수 있습니다.');
    }

    const data = fs.readFileSync(file.path);
    const html = toHtml(data, {
      includeVersion: false,
      includePageInfo: false,
    });

    res.json({ html });
  } catch (err) {
    if (err instanceof AppError) {
      next(err);
      return;
    }
    next(new AppError(422, '한글 파일 미리보기 변환에 실패했습니다.'));
  }
});

// GET /api/files/:id/view  — 파일 미리보기 (브라우저에서 바로 열기)
router.get('/:id/view', async (req, res, next) => {
  try {
    const fileId = String(req.params.id);
    const file = await prisma.file.findUnique({ where: { id: fileId } });
    if (!file) throw new AppError(404, '파일을 찾을 수 없습니다.');
    if (!fs.existsSync(file.path)) throw new AppError(404, '파일이 서버에 없습니다.');

    allowCrossOriginPreview(req, res);
    const mime = resolveFileMime(file.originalName, file.mimetype);
    res.setHeader('Content-Type', mime);
    res.setHeader('Content-Disposition', `inline; filename="${encodeURIComponent(file.originalName)}"`);
    fs.createReadStream(file.path).pipe(res);
  } catch (err) {
    next(err);
  }
});

// GET /api/files/:id/download  — 파일 다운로드
router.get('/:id/download', async (req, res, next) => {
  try {
    const fileId = String(req.params.id);
    const file = await prisma.file.findUnique({ where: { id: fileId } });
    if (!file) throw new AppError(404, '파일을 찾을 수 없습니다.');
    if (!fs.existsSync(file.path)) throw new AppError(404, '파일이 서버에 없습니다.');

    res.setHeader('Content-Disposition', `attachment; filename="${encodeURIComponent(file.originalName)}"`);
    res.download(file.path, file.originalName);
  } catch (err) {
    next(err);
  }
});

// POST /api/files/:id/replace  — 파일 교체
router.post('/:id/replace', upload.single('file'), async (req, res, next) => {
  try {
    if (!req.file) throw new AppError(400, '파일이 없습니다.');

    const fileId = String(req.params.id);
    const existing = await prisma.file.findUnique({ where: { id: fileId } });
    if (!existing) throw new AppError(404, '파일을 찾을 수 없습니다.');

    if (fs.existsSync(existing.path)) {
      fs.unlinkSync(existing.path);
    }

    const file = await prisma.file.update({
      where: { id: fileId },
      data: {
        filename: req.file.filename,
        originalName: fixOriginalName(req.file.originalname),
        mimetype: resolveFileMime(fixOriginalName(req.file.originalname), req.file.mimetype),
        size: req.file.size,
        path: req.file.path,
      },
    });

    res.json(file);
  } catch (err) {
    if (isDiskFullError(err)) {
      cleanupUploadedFile(req.file);
      next(new AppError(507, DISK_FULL_MESSAGE));
      return;
    }
    next(err);
  }
});

// PATCH /api/files/:id  — 표시명·순서 수정
router.patch('/:id', async (req, res, next) => {
  try {
    const fileId = String(req.params.id);
    const { displayName, sortOrder } = req.body as { displayName?: string; sortOrder?: number };
    const existing = await prisma.file.findUnique({ where: { id: fileId } });
    if (!existing) throw new AppError(404, '파일을 찾을 수 없습니다.');

    const file = await prisma.file.update({
      where: { id: fileId },
      data: {
        ...(displayName !== undefined && { displayName: displayName?.trim() || null }),
        ...(sortOrder !== undefined && { sortOrder: Number(sortOrder) }),
      },
    });

    res.json(file);
  } catch (err) {
    next(err);
  }
});

// DELETE /api/files/:id  — 파일 삭제
router.delete('/:id', async (req, res, next) => {
  try {
    const fileId = String(req.params.id);
    const file = await prisma.file.findUnique({ where: { id: fileId } });
    if (!file) throw new AppError(404, '파일을 찾을 수 없습니다.');

    // 실제 파일 삭제
    if (fs.existsSync(file.path)) {
      fs.unlinkSync(file.path);
    }

    await prisma.file.delete({ where: { id: fileId } });
    res.status(204).send();
  } catch (err) {
    next(err);
  }
});

export default router;
