import { Router, Request, Response } from 'express';
import jwt from 'jsonwebtoken';
import { prisma } from '../db/prisma';
import { requireAuth, requires } from '../middleware/auth';
import { authService, type ResolvedAccess } from '../services/auth';
import { looksLikeApiKey, resolveApiKey } from '../services/apiKeys';
import { nostrService } from '../services/nostr';
import { importPostFromNostr } from '../services/postImport';

const JWT_SECRET = process.env.JWT_SECRET || 'change-me-in-production';

const router = Router();

// Resolves access for an optional Bearer token (JWT or API key) without
// rejecting unauthenticated callers, so listing endpoints can stay public while
// still recognizing staff who pass `?all=true`.
async function resolveOptionalAccess(req: Request): Promise<ResolvedAccess | null> {
  const header = req.headers.authorization;
  if (!header || !header.startsWith('Bearer ')) return null;
  const token = header.slice(7);
  try {
    if (looksLikeApiKey(token)) {
      return await resolveApiKey(token);
    }
    const payload = jwt.verify(token, JWT_SECRET) as { pubkey: string };
    return await authService.resolveAccess(payload.pubkey);
  } catch {
    return null;
  }
}

// Returns true when the requester is authenticated and may see hidden posts.
async function canViewHiddenPosts(req: Request): Promise<boolean> {
  const access = await resolveOptionalAccess(req);
  return !!access && (access.isSuperAdmin || access.permissions.has('blog.draft'));
}

router.get('/', async (req: Request, res: Response) => {
  try {
    const page = parseInt(req.query.page as string) || 1;
    const limit = parseInt(req.query.limit as string) || 20;
    const category = req.query.category as string | undefined;
    const skip = (page - 1) * limit;

    const includeHidden = req.query.all === 'true' && (await canViewHiddenPosts(req));

    const where: any = {};
    if (!includeHidden) where.visible = true;
    if (category) {
      where.categories = {
        some: { category: { slug: category } },
      };
    }

    const [posts, total] = await Promise.all([
      prisma.post.findMany({
        where,
        include: {
          categories: { include: { category: true } },
        },
        orderBy: { publishedAt: 'desc' },
        skip,
        take: limit,
      }),
      prisma.post.count({ where }),
    ]);

    res.json({
      posts,
      total,
      pagination: { page, limit, total, pages: Math.ceil(total / limit) },
    });
  } catch (err) {
    console.error('List posts error:', err);
    res.status(500).json({ error: 'Internal server error' });
  }
});

router.get('/:slug', async (req: Request, res: Response) => {
  try {
    const post = await prisma.post.findUnique({
      where: { slug: req.params.slug as string },
      include: {
        categories: { include: { category: true } },
      },
    });

    if (!post) {
      res.status(404).json({ error: 'Post not found' });
      return;
    }

    res.json(post);
  } catch (err) {
    console.error('Get post error:', err);
    res.status(500).json({ error: 'Internal server error' });
  }
});

router.post(
  '/import',
  requireAuth,
  requires('blog.draft'),
  async (req: Request, res: Response) => {
    try {
      const { nostrEventId, naddr, title, excerpt, authorPubkey, publishedAt, tags } = req.body;

      if (!nostrEventId || !title || !authorPubkey) {
        res.status(400).json({ error: 'nostrEventId, title, and authorPubkey are required' });
        return;
      }

      const result = await importPostFromNostr({
        nostrEventId,
        naddr,
        title,
        excerpt,
        authorPubkey,
        publishedAt,
        tags,
      });

      res.json(result);
    } catch (err) {
      console.error('Import post error:', err);
      res.status(500).json({ error: 'Internal server error' });
    }
  }
);

router.patch(
  '/:id',
  requireAuth,
  requires('blog.draft'),
  async (req: Request, res: Response) => {
    try {
      const { title, slug, excerpt, featured, visible, categories } = req.body;

      const post = await prisma.post.findUnique({ where: { id: req.params.id as string } });
      if (!post) {
        res.status(404).json({ error: 'Post not found' });
        return;
      }

      const updateData: any = {};
      if (title !== undefined) updateData.title = title;
      if (slug !== undefined) updateData.slug = slug;
      if (excerpt !== undefined) updateData.excerpt = excerpt;
      if (featured !== undefined) updateData.featured = featured;
      if (visible !== undefined) updateData.visible = visible;

      const updated = await prisma.post.update({
        where: { id: req.params.id as string },
        data: updateData,
      });

      if (categories && Array.isArray(categories)) {
        await prisma.postCategory.deleteMany({
          where: { postId: post.id },
        });
        await prisma.postCategory.createMany({
          data: categories.map((categoryId: string) => ({
            postId: post.id,
            categoryId,
          })),
        });
      }

      const result = await prisma.post.findUnique({
        where: { id: updated.id },
        include: { categories: { include: { category: true } } },
      });

      res.json(result);
    } catch (err) {
      console.error('Update post error:', err);
      res.status(500).json({ error: 'Internal server error' });
    }
  }
);

router.get('/:slug/reactions', async (req: Request, res: Response) => {
  try {
    const post = await prisma.post.findUnique({ where: { slug: req.params.slug as string } });
    if (!post) {
      res.status(404).json({ error: 'Post not found' });
      return;
    }

    const reactions = await nostrService.fetchReactions(post.nostrEventId);
    res.json({ count: reactions.length, reactions });
  } catch (err) {
    console.error('Get reactions error:', err);
    res.status(500).json({ error: 'Internal server error' });
  }
});

router.get('/:slug/replies', async (req: Request, res: Response) => {
  try {
    const post = await prisma.post.findUnique({ where: { slug: req.params.slug as string } });
    if (!post) {
      res.status(404).json({ error: 'Post not found' });
      return;
    }

    const [replies, hiddenContent, blockedPubkeys] = await Promise.all([
      nostrService.fetchReplies(post.nostrEventId),
      prisma.hiddenContent.findMany({ select: { nostrEventId: true } }),
      prisma.blockedPubkey.findMany({ select: { pubkey: true } }),
    ]);

    const hiddenIds = new Set(hiddenContent.map((h) => h.nostrEventId));
    const blockedKeys = new Set(blockedPubkeys.map((b) => b.pubkey));

    const filtered = replies.filter(
      (r) => !hiddenIds.has(r.id) && !blockedKeys.has(r.pubkey)
    );

    res.json({ count: filtered.length, replies: filtered });
  } catch (err) {
    console.error('Get replies error:', err);
    res.status(500).json({ error: 'Internal server error' });
  }
});

router.delete(
  '/:id',
  requireAuth,
  requires('blog.delete'),
  async (req: Request, res: Response) => {
    try {
      const post = await prisma.post.findUnique({ where: { id: req.params.id as string } });
      if (!post) {
        res.status(404).json({ error: 'Post not found' });
        return;
      }

      await prisma.post.delete({ where: { id: req.params.id as string } });
      res.json({ success: true });
    } catch (err) {
      console.error('Delete post error:', err);
      res.status(500).json({ error: 'Internal server error' });
    }
  }
);

export default router;
