// src/server/features/blacklist/blacklist.routes.ts
// REST API routes for blacklist management with RBAC and approval workflow

import { FastifyInstance } from 'fastify';
import { ZodTypeProvider } from 'fastify-type-provider-zod';
import { z } from 'zod';
import { requireAuth, type UserInfo } from '@serverShared/lib/auth';
import { AppError } from '@serverShared/lib/errors';
import { blacklistCreateSchema, blacklistUpdateSchema, blacklistEntrySchema, blacklistPendingEntrySchema } from './blacklist.schema';
import * as blacklistService from './lib/blacklist.service';
import { approveBlacklistEntry, rejectBlacklistEntry, getBlacklistPendingEntryById } from './lib/blacklist.workflow';
import { AppPermission } from '@shared/contracts/permissions';
import { hasPermission, hasAnyPermission } from '@serverShared/plugins/rbac';

/**
 * Register blacklist routes
 */
export const blacklistRoutes = async (fastify: FastifyInstance) => {
  const server = fastify.withTypeProvider<ZodTypeProvider>();

  // POST / - Create new blacklist entry
  server.post('/', {
    preHandler: requireAuth(),
    onRequest: fastify.canAny(AppPermission.BLACKLIST_EDIT, AppPermission.BLACKLIST_REQUEST_APPROVAL),
    schema: {
      body: blacklistCreateSchema,
      response: {
        201: blacklistEntrySchema,
      },
    },
  }, async (request: any) => {
    const data = request.body;
    const entry = await blacklistService.createBlacklistEntry(data, request.user as UserInfo);
    return entry;
  });

  // GET / - Get all blacklist entries
  server.get('/', {
    preHandler: requireAuth(),
    onRequest: fastify.can(AppPermission.BLACKLIST_READ),
    schema: {
      querystring: z.object({
        status: z.enum(['all', 'pending', 'approved']).optional().default('all'),
      }),
      response: {
        200: z.array(blacklistEntrySchema),
      },
    },
  }, async (request: any) => {
    const { status } = request.query;

    let entries;
    if (status === 'pending') {
      entries = await blacklistService.getBlacklistEntriesByStatus('pending');
    } else if (status === 'approved') {
      entries = await blacklistService.getBlacklistEntriesByStatus('approved');
    } else {
      entries = await blacklistService.getAllBlacklistEntries();
    }

    return entries;
  });

  // GET /pending - Get pending blacklist entries (for admin approval)
  server.get('/pending', {
    preHandler: requireAuth(),
    onRequest: fastify.can(AppPermission.BLACKLIST_READ),
    schema: {
      response: {
        200: z.array(blacklistPendingEntrySchema),
      },
    },
  }, async () => {
    const entries = await blacklistService.getBlacklistPendingEntries();
    return entries;
  });

  // GET /:id - Get blacklist entry by ID
  server.get('/:id', {
    preHandler: requireAuth(),
    onRequest: fastify.can(AppPermission.BLACKLIST_READ),
    schema: {
      params: z.object({
        id: z.string().uuid(),
      }),
      response: {
        200: blacklistEntrySchema,
      },
    },
  }, async (request: any) => {
    const { id } = request.params;
    const entry = await blacklistService.getBlacklistEntryById(id);

    if (!entry) {
      throw new AppError('Запись не найдена', 404, 'NOT_FOUND');
    }

    return entry;
  });

  // PUT /:id - Update blacklist entry
  server.put('/:id', {
    preHandler: requireAuth(),
    onRequest: fastify.canAny(AppPermission.BLACKLIST_EDIT, AppPermission.BLACKLIST_REQUEST_APPROVAL),
    schema: {
      params: z.object({
        id: z.string().uuid(),
      }),
      body: blacklistUpdateSchema,
      response: {
        200: blacklistEntrySchema,
      },
    },
  }, async (request: any) => {
    const { id } = request.params;
    const data = request.body;

    const entry = await blacklistService.updateBlacklistEntry({
      id,
      ...data,
    }, request.user as UserInfo);
    return entry;
  });

  // DELETE /:id - Delete blacklist entry (soft delete)
  server.delete('/:id', {
    preHandler: requireAuth(),
    onRequest: fastify.canAny(AppPermission.BLACKLIST_EDIT, AppPermission.BLACKLIST_REQUEST_APPROVAL),
    schema: {
      params: z.object({
        id: z.string().uuid(),
      }),
    },
  }, async (request: any) => {
    const { id } = request.params;
    await blacklistService.deleteBlacklistEntry(id, request.user as UserInfo);
  });

  // POST /approve/:id - Approve single pending blacklist entry (ADMIN only)
  server.post('/approve/:id', {
    preHandler: requireAuth(),
    onRequest: fastify.can(AppPermission.BLACKLIST_EDIT),
    schema: {
      params: z.object({
        id: z.string().uuid(),
      }),
      response: {
        200: z.object({
          success: z.boolean(),
        }),
      },
    },
  }, async (request: any) => {
    const { id } = request.params;
    await approveBlacklistEntry(id);
    return { success: true };
  });

  // POST /reject/:id - Reject single pending blacklist entry (ADMIN and MANAGER)
  server.post('/reject/:id', {
    preHandler: requireAuth(),
    onRequest: fastify.canAny(AppPermission.BLACKLIST_EDIT, AppPermission.BLACKLIST_REQUEST_APPROVAL),
    schema: {
      params: z.object({
        id: z.string().uuid(),
      }),
      response: {
        200: z.object({
          success: z.boolean(),
        }),
      },
    },
  }, async (request: any) => {
    const { id } = request.params;
    const user = request.user as UserInfo;
    const canEdit = await hasPermission(user, AppPermission.BLACKLIST_EDIT);
    const canRequestApproval = await hasAnyPermission(user, [AppPermission.BLACKLIST_EDIT, AppPermission.BLACKLIST_REQUEST_APPROVAL]);

    if (!canRequestApproval) {
      throw new AppError('Недостаточно прав', 403, 'FORBIDDEN');
    }

    // Пользователь без EDIT может отклонять только свои заявки
    if (!canEdit) {
      const entry = await getBlacklistPendingEntryById(id);
      if (!entry) {
        throw new AppError('Запись на согласование не найдена', 404, 'NOT_FOUND');
      }
      if (entry.createdBy !== user.id) {
        throw new AppError('Вы можете отклонить только свои запросы', 403, 'FORBIDDEN');
      }
    }

    await rejectBlacklistEntry(id);
    return { success: true };
  });
};
