import { Router, Request, Response, NextFunction } from 'express';
import { strawPollsService } from './polls.service';
import {
  listStrawPollsSchema,
  submitStrawPollVoteSchema,
  submitStrawPollCommentSchema,
  challengeVoteSchema,
} from './polls.schemas';
import { validate } from '../../middleware/validate';
import { authenticate, optionalAuth } from '../../middleware/auth.middleware';
import { strawPollVoteRateLimit, strawPollCommentRateLimit } from './polls.rate-limits';
import { pollSseService } from './polls-sse.service';

const publicRouter = Router();

// List active public polls
publicRouter.get('/public', validate(listStrawPollsSchema, 'query'), async (req: Request, res: Response, next: NextFunction) => {
  try {
    const result = await strawPollsService.findAllPublic(req.query as any);
    res.json(result);
  } catch (err) { next(err); }
});

// Get poll by slug (public)
publicRouter.get('/public/:slug', optionalAuth, async (req: Request, res: Response, next: NextFunction) => {
  try {
    const slug = req.params.slug as string;
    const voterToken = req.query.voterToken as string | undefined;
    const clientIp = req.ip || req.socket.remoteAddress || '';
    const poll = await strawPollsService.findBySlugPublic(slug, req.user?.id, voterToken, clientIp);
    if (!poll) return res.status(404).json({ error: 'Poll not found' });

    // For AFTER_VOTE visibility, strip results if not voted
    if ('resultVisibility' in poll && poll.resultVisibility === 'AFTER_VOTE' && !poll.hasVoted) {
      const stripped = {
        ...poll,
        options: (poll as any).options?.map((o: any) => ({ ...o, voteCount: undefined })),
        totalVotes: undefined,
        showResults: false,
      };
      return res.json(stripped);
    }

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

// Submit vote
publicRouter.post('/public/:slug/vote', optionalAuth, strawPollVoteRateLimit, validate(submitStrawPollVoteSchema), async (req: Request, res: Response, next: NextFunction) => {
  try {
    const slug = req.params.slug as string;
    const clientIp = req.ip || req.socket.remoteAddress || '';
    const result = await strawPollsService.submitVote(slug, req.body, req.user?.id, clientIp);
    res.json(result);
  } catch (err) {
    if (err instanceof Error && (err.message.includes('not found') || err.message.includes('not active') || err.message.includes('Invalid option'))) {
      return res.status(400).json({ error: err.message });
    }
    if (err instanceof Error && err.message.includes('required')) {
      return res.status(401).json({ error: err.message });
    }
    next(err);
  }
});

// Submit comment
publicRouter.post('/public/:slug/comment', optionalAuth, strawPollCommentRateLimit, validate(submitStrawPollCommentSchema), async (req: Request, res: Response, next: NextFunction) => {
  try {
    const slug = req.params.slug as string;
    const comment = await strawPollsService.addComment(slug, req.body, req.user?.id);
    res.status(201).json(comment);
  } catch (err) {
    if (err instanceof Error && err.message.includes('disabled')) {
      return res.status(400).json({ error: err.message });
    }
    next(err);
  }
});

// SSE stream for live results
publicRouter.get('/public/:slug/live', (req: Request, res: Response) => {
  const slug = req.params.slug as string;

  res.writeHead(200, {
    'Content-Type': 'text/event-stream',
    'Cache-Control': 'no-cache',
    'Connection': 'keep-alive',
    'X-Accel-Buffering': 'no', // Disable nginx buffering
  });
  res.write(': connected\n\n');

  const connectionId = pollSseService.addClient(slug, res);
  if (!connectionId) {
    res.write('event: error\ndata: {"message":"Too many connections"}\n\n');
    res.end();
    return;
  }

  req.on('close', () => {
    pollSseService.removeClient(connectionId);
  });
});

// Challenge a friend (requires auth)
publicRouter.post('/public/:slug/challenge', authenticate, validate(challengeVoteSchema), async (req: Request, res: Response, next: NextFunction) => {
  try {
    const slug = req.params.slug as string;
    // Look up poll ID from slug
    const { prisma } = await import('../../config/database');
    const poll = await prisma.strawPoll.findUnique({ where: { slug }, select: { id: true } });
    if (!poll) return res.status(404).json({ error: 'Poll not found' });

    const challenge = await strawPollsService.challengeFriend(poll.id, req.user!.id, req.body.challengedUserId);
    res.status(201).json(challenge);
  } catch (err) {
    if (err instanceof Error && err.message.includes('not found')) {
      return res.status(404).json({ error: err.message });
    }
    next(err);
  }
});

export { publicRouter as strawPollPublicRouter };
