// src/server/features/reference_books/contractors.routes.ts
// HTTP handlers for contractors routes with RBAC and approval workflow

import { FastifyPluginAsync } 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 { contractorCreateSchema, contractorUpdateSchema, contractorEntrySchema, contractorPendingEntrySchema } from './contractors.schema';
import * as contractorsService from './contractors.service';
import { approveContractorEntry, rejectContractorEntry, getContractorPendingEntryById } from './lib/contractors.workflow';
import { AppPermission } from '@shared/contracts/permissions';
import { hasPermission, hasAnyPermission } from '@serverShared/plugins/rbac';

/**
 * Register contractors routes
 */
const contractorsRoutes: FastifyPluginAsync = async (fastify, opts) => {
  const server = fastify.withTypeProvider<ZodTypeProvider>();

  // POST /contractors - Create new contractor entry
  server.post('/contractors', {
    preHandler: requireAuth(),
    onRequest: fastify.canAny(AppPermission.CONTRACTORS_EDIT, AppPermission.CONTRACTORS_REQUEST_APPROVAL),
    schema: {
      body: contractorCreateSchema,
      response: {
        201: contractorEntrySchema,
      },
    },
  }, async (request: any) => {
    const data = request.body;
    const entry = await contractorsService.createContractorEntry(data, request.user as UserInfo);
    return entry;
  });

  // GET /contractors - Get all contractor entries
  server.get('/contractors', {
    preHandler: requireAuth(),
    onRequest: fastify.can(AppPermission.CONTRACTORS_READ),
    schema: {
      querystring: z.object({
        archived: z.coerce.boolean().optional(),
        status: z.enum(['all', 'pending', 'approved']).optional().default('all'),
      }),
      response: {
        200: z.array(contractorEntrySchema),
      },
    },
  }, async (request: any) => {
    const { archived, status } = request.query;
    const entries = await contractorsService.getAllContractorsEntries(archived, status);
    return entries;
  });

  // GET /contractors/pending - Get pending contractor entries (for admin approval)
  server.get('/contractors/pending', {
    preHandler: requireAuth(),
    onRequest: fastify.can(AppPermission.CONTRACTORS_READ),
    schema: {
      response: {
        200: z.array(contractorPendingEntrySchema),
      },
    },
  }, async () => {
    const entries = await contractorsService.getContractorsPendingEntries();
    return entries;
  });

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

  // POST /contractors/reject/:id - Reject single pending contractor entry (ADMIN and MANAGER)
  server.post('/contractors/reject/:id', {
    preHandler: requireAuth(),
    onRequest: fastify.canAny(AppPermission.CONTRACTORS_EDIT, AppPermission.CONTRACTORS_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.CONTRACTORS_EDIT);
    const canRequestApproval = await hasAnyPermission(user, [AppPermission.CONTRACTORS_EDIT, AppPermission.CONTRACTORS_REQUEST_APPROVAL]);

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

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

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

  // GET /contractors/:id - Get contractor entry by ID (must be AFTER static routes)
  server.get('/contractors/:id', {
    preHandler: requireAuth(),
    onRequest: fastify.can(AppPermission.CONTRACTORS_READ),
    schema: {
      params: z.object({
        id: z.string().uuid(),
      }),
      response: {
        200: contractorEntrySchema,
      },
    },
  }, async (request: any) => {
    const { id } = request.params;
    const entry = await contractorsService.getContractorEntryById(id);

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

    return entry;
  });

  // PUT /contractors/:id - Update contractor entry (must be AFTER static routes)
  server.put('/contractors/:id', {
    preHandler: requireAuth(),
    onRequest: fastify.canAny(AppPermission.CONTRACTORS_EDIT, AppPermission.CONTRACTORS_REQUEST_APPROVAL),
    schema: {
      params: z.object({
        id: z.string().uuid(),
      }),
      body: contractorUpdateSchema,
      response: {
        200: contractorEntrySchema,
      },
    },
  }, async (request: any) => {
    const { id } = request.params;
    const data = request.body;

    const entry = await contractorsService.updateContractorEntry(
      id,
      data,
      request.user as UserInfo,
    );
    return entry;
  });

  // DELETE /contractors/:id - Delete contractor entry (soft delete) (must be AFTER static routes)
  server.delete('/contractors/:id', {
    preHandler: requireAuth(),
    onRequest: fastify.canAny(AppPermission.CONTRACTORS_EDIT, AppPermission.CONTRACTORS_REQUEST_APPROVAL),
    schema: {
      params: z.object({
        id: z.string().uuid(),
      }),
    },
  }, async (request: any) => {
    const { id } = request.params;
    await contractorsService.deleteContractorEntry(id, request.user as UserInfo);
  });
};

export default contractorsRoutes;
