import { Router, Request, Response, NextFunction } from 'express';
import { authenticate } from '../../middleware/auth.middleware';
import { requireNonTemp, requireRole } from '../../middleware/rbac.middleware';
import { CONTENT_ROLES } from '../../utils/roles';
import { prisma } from '../../config/database';
import { logger } from '../../utils/logger';
import { docsAccessService, ShareLinkError } from './docs-access.service';
import { docsFilesService } from './docs-files.service';
import { upsertPolicySchema, createShareLinkSchema } from './docs-access.schemas';

const router = Router();

// --- Access Policy Routes (authenticated, CONTENT_ROLES) ---

// GET /api/docs-access/policy?path=... — get effective policy for a file
router.get(
  '/policy',
  authenticate,
  requireNonTemp,
  requireRole(...CONTENT_ROLES),
  async (req: Request, res: Response, next: NextFunction) => {
    try {
      const path = String(req.query['path'] ?? '').trim();
      if (!path) {
        res.status(400).json({ error: { message: 'Path query parameter required', code: 'VALIDATION_ERROR' } });
        return;
      }
      const policy = await docsAccessService.getEffectivePolicy(path);
      res.json(policy);
    } catch (err) {
      logger.error('Failed to get doc access policy', err);
      next(err);
    }
  },
);

// PUT /api/docs-access/policy — create or update policy for a path
router.put(
  '/policy',
  authenticate,
  requireNonTemp,
  requireRole(...CONTENT_ROLES),
  async (req: Request, res: Response, next: NextFunction) => {
    try {
      const parsed = upsertPolicySchema.safeParse(req.body);
      if (!parsed.success) {
        res.status(400).json({ error: { message: 'Invalid policy data', code: 'VALIDATION_ERROR', details: parsed.error.flatten().fieldErrors } });
        return;
      }

      const { documentPath, isDirectory, allowedEditors } = parsed.data;

      const policy = await prisma.docAccessPolicy.upsert({
        where: { documentPath },
        create: {
          documentPath,
          isDirectory,
          allowedEditors: allowedEditors as unknown as import('@prisma/client').Prisma.InputJsonValue,
          createdById: req.user!.id,
        },
        update: {
          isDirectory,
          allowedEditors: allowedEditors as unknown as import('@prisma/client').Prisma.InputJsonValue,
        },
      });

      res.json(policy);
    } catch (err) {
      logger.error('Failed to upsert doc access policy', err);
      next(err);
    }
  },
);

// DELETE /api/docs-access/policy?path=... — remove policy (revert to default)
router.delete(
  '/policy',
  authenticate,
  requireNonTemp,
  requireRole(...CONTENT_ROLES),
  async (req: Request, res: Response, next: NextFunction) => {
    try {
      const path = String(req.query['path'] ?? '').trim();
      if (!path) {
        res.status(400).json({ error: { message: 'Path query parameter required', code: 'VALIDATION_ERROR' } });
        return;
      }

      await prisma.docAccessPolicy.deleteMany({
        where: { documentPath: path },
      });

      res.json({ success: true });
    } catch (err) {
      logger.error('Failed to delete doc access policy', err);
      next(err);
    }
  },
);

// GET /api/docs-access/policies — list all policies
router.get(
  '/policies',
  authenticate,
  requireNonTemp,
  requireRole(...CONTENT_ROLES),
  async (_req: Request, res: Response, next: NextFunction) => {
    try {
      const policies = await prisma.docAccessPolicy.findMany({
        orderBy: { documentPath: 'asc' },
        include: { createdBy: { select: { id: true, name: true, email: true } } },
      });
      res.json({ policies });
    } catch (err) {
      logger.error('Failed to list doc access policies', err);
      next(err);
    }
  },
);

// --- Share Link Routes ---

// POST /api/docs-access/share/create — generate share link (authenticated, CONTENT_ROLES)
router.post(
  '/share/create',
  authenticate,
  requireNonTemp,
  requireRole(...CONTENT_ROLES),
  async (req: Request, res: Response, next: NextFunction) => {
    try {
      const parsed = createShareLinkSchema.safeParse(req.body);
      if (!parsed.success) {
        res.status(400).json({ error: { message: 'Invalid share link data', code: 'VALIDATION_ERROR', details: parsed.error.flatten().fieldErrors } });
        return;
      }

      // Verify the file exists
      try {
        docsFilesService.safeResolve(parsed.data.documentPath);
      } catch {
        res.status(404).json({ error: { message: 'Document not found', code: 'NOT_FOUND' } });
        return;
      }

      const link = await docsAccessService.generateShareLink(
        req.user!.id,
        parsed.data.documentPath,
        {
          canEdit: parsed.data.canEdit,
          expiresInHours: parsed.data.expiresInHours,
          maxUses: parsed.data.maxUses,
          guestName: parsed.data.guestName,
        },
      );

      res.status(201).json(link);
    } catch (err) {
      logger.error('Failed to create share link', err);
      next(err);
    }
  },
);

// GET /api/docs-access/share/links?path=... — list share links for a document
router.get(
  '/share/links',
  authenticate,
  requireNonTemp,
  requireRole(...CONTENT_ROLES),
  async (req: Request, res: Response, next: NextFunction) => {
    try {
      const path = String(req.query['path'] ?? '').trim();
      const where = path ? { documentPath: path } : {};

      const links = await prisma.docShareLink.findMany({
        where,
        orderBy: { createdAt: 'desc' },
        include: { createdBy: { select: { id: true, name: true, email: true } } },
      });

      res.json({ links });
    } catch (err) {
      logger.error('Failed to list share links', err);
      next(err);
    }
  },
);

// PATCH /api/docs-access/share/:id/revoke — revoke a share link
router.patch(
  '/share/:id/revoke',
  authenticate,
  requireNonTemp,
  requireRole(...CONTENT_ROLES),
  async (req: Request, res: Response, next: NextFunction) => {
    try {
      const id = req.params.id as string;
      const link = await prisma.docShareLink.update({
        where: { id },
        data: { status: 'REVOKED' },
      });
      res.json(link);
    } catch (err) {
      logger.error('Failed to revoke share link', err);
      next(err);
    }
  },
);

// DELETE /api/docs-access/share/:id — delete a share link record
router.delete(
  '/share/:id',
  authenticate,
  requireNonTemp,
  requireRole(...CONTENT_ROLES),
  async (req: Request, res: Response, next: NextFunction) => {
    try {
      const id = req.params.id as string;
      await prisma.docShareLink.delete({ where: { id } });
      res.json({ success: true });
    } catch (err) {
      logger.error('Failed to delete share link', err);
      next(err);
    }
  },
);

// --- Public Share Redemption (no auth required) ---

// GET /api/docs-access/share/public/:shareToken — validate token, return doc metadata + collab JWT
router.get(
  '/share/public/:shareToken',
  async (req: Request, res: Response, next: NextFunction) => {
    try {
      const shareToken = req.params.shareToken as string;
      if (!shareToken || shareToken.length < 32) {
        res.status(400).json({ error: { message: 'Invalid share token', code: 'VALIDATION_ERROR' } });
        return;
      }

      const shareLink = await docsAccessService.validateShareToken(shareToken);

      // Read document content to get the filename
      let documentName = shareLink.documentPath.split('/').pop() || 'Document';
      try {
        // Check file still exists
        docsFilesService.safeResolve(shareLink.documentPath);
      } catch {
        res.status(404).json({ error: { message: 'Shared document no longer exists', code: 'NOT_FOUND' } });
        return;
      }

      // Generate a short-lived collab JWT for WebSocket access
      const collabToken = docsAccessService.generateShareCollabToken({
        shareToken,
        documentPath: shareLink.documentPath,
        canEdit: shareLink.canEdit,
        guestName: shareLink.guestName,
      });

      // Deterministic color for guest
      const colorPalette = [
        '#E57373', '#81C784', '#64B5F6', '#FFB74D', '#BA68C8',
        '#4DD0E1', '#FF8A65', '#AED581', '#9575CD', '#4DB6AC',
        '#F06292', '#FFD54F', '#7986CB', '#A1887F', '#90A4AE',
      ];
      const colorIndex = shareToken.charCodeAt(0) % colorPalette.length;

      res.json({
        documentPath: shareLink.documentPath,
        documentName,
        canEdit: shareLink.canEdit,
        collabToken,
        guestIdentity: {
          id: `share:${shareToken.substring(0, 8)}`,
          name: shareLink.guestName || 'Guest',
          color: colorPalette[colorIndex],
        },
      });
    } catch (err) {
      if (err instanceof ShareLinkError) {
        const statusMap: Record<string, number> = {
          SHARE_LINK_NOT_FOUND: 404,
          SHARE_LINK_REVOKED: 410,
          SHARE_LINK_EXPIRED: 410,
          SHARE_LINK_MAX_USES: 429,
        };
        res.status(statusMap[err.code] || 400).json({ error: { message: err.message, code: err.code } });
        return;
      }
      logger.error('Failed to validate share link', err);
      next(err);
    }
  },
);

export const docsAccessRouter = router;
