// src/server/features/reference_books/external_access.routes.ts
// HTTP handlers for external access routes

import { FastifyPluginAsync } from 'fastify';
import { ZodTypeProvider } from 'fastify-type-provider-zod';
import { z } from 'zod';
import { accessService } from './access.service';
import { logger } from '@serverShared/lib/logger';
import { AppError } from '@serverShared/lib/errors';
import { requireAuth, type UserInfo } from '@serverShared/lib/auth';
import { AppPermission } from '@shared/contracts/permissions';
import { hasPermission, hasAnyPermission } from '@serverShared/plugins/rbac';
import { approveAccessEntry, rejectAccessEntry, getAccessPendingEntryById } from './lib/access.workflow';
import {
  createAccessSchema,
  updateAccessSchema,
  externalAccessSchema,
  externalAccessPendingEntrySchema,
  accessActionResponseSchema,
  accessErrorResponseSchema,
} from '@shared/contracts/access';

const externalAccessRoutes: FastifyPluginAsync = async (fastify, opts) => {
  // Create external access entry
  fastify.withTypeProvider<ZodTypeProvider>().route({
    method: 'POST',
    url: '/external-access',
    preHandler: requireAuth(),
    onRequest: fastify.canAny(AppPermission.ACCESS_EDIT, AppPermission.ACCESS_REQUEST_APPROVAL),
    schema: {
      body: createAccessSchema,
      response: {
        201: externalAccessSchema,
        400: accessErrorResponseSchema,
      },
    },
    handler: async (request, reply) => {
      const result = await accessService.create(request.body, request.user as UserInfo);
      logger.info(`[ExternalAccess] Entry created: ${result.resourceName}`);
      return reply.status(201).send(result);
    },
  });

  // Get all external access entries
  fastify.withTypeProvider<ZodTypeProvider>().route({
    method: 'GET',
    url: '/external-access',
    preHandler: requireAuth(),
    onRequest: fastify.can(AppPermission.ACCESS_READ),
    schema: {
      querystring: z.object({
        status: z.enum(['all', 'pending', 'approved']).optional().default('all'),
      }),
      response: {
        200: z.array(externalAccessSchema),
      },
    },
    handler: async (request, reply) => {
      const { status } = request.query as { status: 'all' | 'pending' | 'approved' };
      const entries = await accessService.findAll(status);
      return reply.status(200).send(entries);
    },
  });

  // Get pending external access entries (for admin approval)
  fastify.withTypeProvider<ZodTypeProvider>().route({
    method: 'GET',
    url: '/external-access/pending',
    preHandler: requireAuth(),
    onRequest: fastify.can(AppPermission.ACCESS_READ),
    schema: {
      response: {
        200: z.array(externalAccessPendingEntrySchema),
      },
    },
    handler: async (request, reply) => {
      const entries = await accessService.getAccessPendingEntries();
      return reply.status(200).send(entries);
    },
  });

  // Get external access entry by ID
  fastify.withTypeProvider<ZodTypeProvider>().route({
    method: 'GET',
    url: '/external-access/:id',
    preHandler: requireAuth(),
    onRequest: fastify.can(AppPermission.ACCESS_READ),
    schema: {
      params: z.object({
        id: z.string(),
      }),
      response: {
        200: externalAccessSchema,
        404: accessErrorResponseSchema,
      },
    },
    handler: async (request, reply) => {
      const { id } = request.params as { id: string };
      const entry = await accessService.findById(id);
      
      if (!entry) {
        throw new AppError('External access entry not found', 404, 'NOT_FOUND');
      }
      
      return reply.status(200).send(entry);
    },
  });

  // Update external access entry
  fastify.withTypeProvider<ZodTypeProvider>().route({
    method: 'PUT',
    url: '/external-access/:id',
    preHandler: requireAuth(),
    onRequest: fastify.canAny(AppPermission.ACCESS_EDIT, AppPermission.ACCESS_REQUEST_APPROVAL),
    schema: {
      params: z.object({
        id: z.string(),
      }),
      body: updateAccessSchema,
      response: {
        200: externalAccessSchema,
        404: accessErrorResponseSchema,
      },
    },
    handler: async (request, reply) => {
      const { id } = request.params as { id: string };
      const updated = await accessService.update(id, request.body, request.user as UserInfo);
      
      if (!updated) {
        throw new AppError('External access entry not found', 404, 'NOT_FOUND');
      }
      
      logger.info(`[ExternalAccess] Entry updated: ${id}`);
      return reply.status(200).send(updated);
    },
  });

  // Archive external access entry
  fastify.withTypeProvider<ZodTypeProvider>().route({
    method: 'DELETE',
    url: '/external-access/:id',
    preHandler: requireAuth(),
    onRequest: fastify.canAny(AppPermission.ACCESS_EDIT, AppPermission.ACCESS_REQUEST_APPROVAL),
    schema: {
      params: z.object({
        id: z.string(),
      }),
      response: {
        200: accessActionResponseSchema,
        404: accessErrorResponseSchema,
      },
    },
    handler: async (request, reply) => {
      const { id } = request.params as { id: string };
      const existing = await accessService.findById(id);
      
      if (!existing) {
        throw new AppError('External access entry not found', 404, 'NOT_FOUND');
      }
      
      await accessService.softDelete(id, request.user as UserInfo);
      logger.info(`[ExternalAccess] Entry archived: ${id}`);
      return reply.status(200).send({
        success: true,
        message: 'External access entry archived',
      });
    },
  });

  // Approve single pending access entry (ADMIN only)
  fastify.withTypeProvider<ZodTypeProvider>().route({
    method: 'POST',
    url: '/external-access/approve/:id',
    preHandler: requireAuth(),
    onRequest: fastify.can(AppPermission.ACCESS_EDIT),
    schema: {
      params: z.object({
        id: z.string().uuid(),
      }),
      response: {
        200: z.object({ success: z.boolean() }),
      },
    },
    handler: async (request, reply) => {
      const { id } = request.params as { id: string };
      await approveAccessEntry(id);
      return reply.status(200).send({ success: true });
    },
  });

  // Reject single pending access entry (ADMIN and MANAGER for own requests)
  fastify.withTypeProvider<ZodTypeProvider>().route({
    method: 'POST',
    url: '/external-access/reject/:id',
    preHandler: requireAuth(),
    onRequest: fastify.canAny(AppPermission.ACCESS_EDIT, AppPermission.ACCESS_REQUEST_APPROVAL),
    schema: {
      params: z.object({
        id: z.string().uuid(),
      }),
      response: {
        200: z.object({ success: z.boolean() }),
      },
    },
    handler: async (request, reply) => {
      const { id } = request.params as { id: string };
      const user = request.user as UserInfo;
      const canEdit = await hasPermission(user, AppPermission.ACCESS_EDIT);
      const canRequestApproval = await hasAnyPermission(user, [AppPermission.ACCESS_EDIT, AppPermission.ACCESS_REQUEST_APPROVAL]);

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

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

      await rejectAccessEntry(id);
      return reply.status(200).send({ success: true });
    },
  });
};

export default externalAccessRoutes;
