/**
 * In-App Feedback routes.
 * Authenticated: submit feedback. Admin: list, triage, delete.
 */

import type { FastifyInstance } from 'fastify';
import { UnauthorizedError, ForbiddenError, NotFoundError, BadRequestError } from '../../lib/errors.js';
import { getRequestProductId } from '../../lib/request-context.js';
import { CreateFeedbackSchema, UpdateFeedbackSchema, QueryFeedbackSchema } from './types.js';
import { generateSasUrl, BLOB_CONTAINERS } from '../../lib/blob.js';
import {
  createFeedback,
  getFeedback,
  listFeedback,
  updateFeedback,
  deleteFeedback,
  getFeedbackStats,
  generateScreenshotBlobPath,
  hasScreenshot,
} from './repository.js';

function requireAuth(req: { jwtPayload?: { sub: string; role?: string } }): string {
  if (!req.jwtPayload?.sub) throw new UnauthorizedError('Authentication required');
  return req.jwtPayload.sub;
}

function requireAdmin(req: { jwtPayload?: { sub: string; role?: string } }): void {
  requireAuth(req);
  if (req.jwtPayload?.role !== 'admin') throw new ForbiddenError('Admin access required');
}

export async function feedbackRoutes(app: FastifyInstance): Promise<void> {
  // ── User: Submit feedback ─────────────────────────────────
  app.post('/feedback', async (req, reply) => {
    const userId = requireAuth(req);
    const productId = getRequestProductId(req);
    const input = CreateFeedbackSchema.parse(req.body);
    const fb = await createFeedback(productId, userId, input);
    reply.status(201);
    return fb;
  });

  // ── Admin: List feedback ──────────────────────────────────
  app.get('/feedback', async req => {
    requireAdmin(req);
    const productId = getRequestProductId(req);
    const query = QueryFeedbackSchema.parse(req.query);
    return listFeedback(productId, query);
  });

  // ── Admin: Get single feedback ────────────────────────────
  app.get<{ Params: { id: string } }>('/feedback/:id', async req => {
    requireAdmin(req);
    const productId = getRequestProductId(req);
    const fb = await getFeedback(req.params.id, productId);
    if (!fb) throw new NotFoundError('Feedback not found');
    return fb;
  });

  // ── Admin: Update feedback (triage) ───────────────────────
  app.put<{ Params: { id: string } }>('/feedback/:id', async req => {
    requireAdmin(req);
    const productId = getRequestProductId(req);
    const updates = UpdateFeedbackSchema.parse(req.body);
    const fb = await updateFeedback(req.params.id, productId, updates);
    if (!fb) throw new NotFoundError('Feedback not found');
    return fb;
  });

  // ── Admin: Delete feedback ────────────────────────────────
  app.delete<{ Params: { id: string } }>('/feedback/:id', async (req, reply) => {
    requireAdmin(req);
    const productId = getRequestProductId(req);
    const ok = await deleteFeedback(req.params.id, productId);
    if (!ok) throw new NotFoundError('Feedback not found');
    reply.status(204);
  });

  // ── Admin: Feedback stats ─────────────────────────────────
  app.get('/feedback/stats', async req => {
    requireAdmin(req);
    const productId = getRequestProductId(req);
    return getFeedbackStats(productId);
  });

  // ── User: Generate SAS URL for screenshot upload (Phase 1.3) ─────────────
  app.post('/feedback/sas', async req => {
    const userId = requireAuth(req);
    const productId = getRequestProductId(req);
    
    // Validate content type
    const body = req.body as { contentType?: 'image/png' | 'image/jpeg' | 'image/webp' };
    const validTypes = ['image/png', 'image/jpeg', 'image/webp'];
    if (!body.contentType || !validTypes.includes(body.contentType)) {
      throw new BadRequestError('Invalid contentType. Must be image/png, image/jpeg, or image/webp');
    }

    // Generate blob path with user-specific prefix for organization
    // Note: Blob stays at this location - we don't move it after feedback creation
    const blobPath = generateScreenshotBlobPath(productId, userId, body.contentType);

    // Generate SAS URL for upload (5 minutes expiry)
    const uploadUrl = await generateSasUrl(
      BLOB_CONTAINERS.feedbackScreenshots,
      blobPath,
      'w', // write permission
      5 // 5 minutes
    );

    return {
      blobPath,
      uploadUrl,
      expiresIn: 300, // 5 minutes
      maxSizeBytes: 5 * 1024 * 1024, // 5MB limit
    };
  });

  // ── Admin: Get screenshot view URL (Phase 1.3) ────────────────────────────
  app.get<{ Params: { id: string } }>('/feedback/:id/screenshot', async req => {
    requireAdmin(req);
    const productId = getRequestProductId(req);
    const fb = await getFeedback(req.params.id, productId);
    
    if (!fb) throw new NotFoundError('Feedback not found');
    if (!hasScreenshot(fb)) {
      throw new NotFoundError('No screenshot attached to this feedback');
    }

    // Generate fresh SAS URL for viewing (15 minutes expiry)
    const viewUrl = await generateSasUrl(
      BLOB_CONTAINERS.feedbackScreenshots,
      fb.screenshotBlobPath!,
      'r', // read permission
      15 // 15 minutes
    );

    return {
      url: viewUrl,
      contentType: fb.screenshotContentType,
      sizeBytes: fb.screenshotSizeBytes,
      expiresIn: 900, // 15 minutes
    };
  });

  // ── Admin: Delete screenshot (GDPR/privacy compliance) (Phase 1.3) ───────
  app.delete<{ Params: { id: string } }>('/feedback/:id/screenshot', async (req, reply) => {
    requireAdmin(req);
    const productId = getRequestProductId(req);
    const fb = await getFeedback(req.params.id, productId);
    
    if (!fb) throw new NotFoundError('Feedback not found');
    if (!hasScreenshot(fb)) {
      throw new NotFoundError('No screenshot attached to this feedback');
    }

    // Update feedback to remove screenshot reference
    await updateFeedback(req.params.id, productId, {
      screenshotBlobPath: undefined,
      screenshotContentType: undefined,
      screenshotSizeBytes: undefined,
    } as unknown as import('./types.js').UpdateFeedbackInput);

    // Note: Actual blob deletion would be handled by lifecycle policy or batch job
    // to avoid blocking the request

    reply.status(204);
  });
}
