// src/server/features/personnel/personnel.routes.ts
// HTTP handlers for personnel management routes

import { FastifyInstance } from 'fastify';
import { ZodTypeProvider } from 'fastify-type-provider-zod';
import { z } from 'zod';
import * as personnelService from './personnel.service';
import {
  createEmployeeSchema,
  updateEmployeeSchema,
  fireEmployeeSchema,
  rehireEmployeeSchema,
  restoreEmployeeSchema,
  employeeResponseSchema,
  employeesListResponseSchema,
  employeeResponseWrapperSchema,
  errorResponseSchema,
  checkUsernameSchema,
  checkUsernameResponseSchema,
  getEmployeesQuerySchema,
} from './personnel.schema';
import { requireAuth } from '@serverShared/lib/auth';
import { AppPermission } from '@shared/contracts/permissions';
import { hasPermission } from '@serverShared/plugins/rbac';

const ROLE_ASSIGN_PERMISSIONS: Record<string, AppPermission> = {
  ADMIN: AppPermission.PERSONNEL_ASSIGN_ADMIN,
  MANAGER: AppPermission.PERSONNEL_ASSIGN_MANAGER,
  MAID: AppPermission.PERSONNEL_ASSIGN_MAID,
};

export const personnelRoutes = async (fastify: FastifyInstance) => {
  const server = fastify.withTypeProvider<ZodTypeProvider>();

  // Get all employees (supports optional isFired query parameter)
  server.get('/', {
    preHandler: [requireAuth(), fastify.can(AppPermission.PERSONNEL_READ)],
    schema: {
      querystring: getEmployeesQuerySchema,
      response: {
        200: employeesListResponseSchema,
      },
    },
  }, async (request: any) => {
    const { isFired } = request.query;
    const currentUserId = request.user.id;
    const employees = isFired !== undefined
      ? await personnelService.getEmployeesByStatus(isFired, currentUserId)
      : await personnelService.getEmployees(currentUserId);
    return {
      success: true,
      data: employees,
    };
  });

  // Get employees by status (active or fired)
  server.get('/status/:status', {
    preHandler: [requireAuth(), fastify.can(AppPermission.PERSONNEL_READ)],
    schema: {
      params: z.object({
        status: z.enum(['active', 'fired']),
      }),
      response: {
        200: employeesListResponseSchema,
      },
    },
  }, async (request: any) => {
    const isFired = request.params.status === 'fired';
    const currentUserId = request.user.id;
    const employees = await personnelService.getEmployeesByStatus(isFired, currentUserId);
    return {
      success: true,
      data: employees,
    };
  });

  // Get employee by ID
  server.get('/:id', {
    preHandler: [requireAuth(), fastify.can(AppPermission.PERSONNEL_READ)],
    schema: {
      params: z.object({
        id: z.string(),
      }),
      response: {
        200: employeeResponseWrapperSchema,
        404: errorResponseSchema,
      },
    },
  }, async (request: any) => {
    const employee = await personnelService.getEmployeeById(request.params.id);
    if (!employee) {
      throw new Error('Employee not found');
    }
    return {
      success: true,
      data: employee,
    };
  });

  // Create employee
  server.post('/', {
    preHandler: [requireAuth(), fastify.can(AppPermission.PERSONNEL_WRITE)],
    schema: {
      body: createEmployeeSchema,
      response: {
        201: employeeResponseWrapperSchema,
      },
    },
  }, async (request: any) => {
    const { role } = request.body;
    if (role && ROLE_ASSIGN_PERMISSIONS[role]) {
      const canAssign = await hasPermission(request.user, ROLE_ASSIGN_PERMISSIONS[role]);
      if (!canAssign) {
        throw new Error('Недостаточно прав для назначения этой роли');
      }
    }

    const employeeId = await personnelService.createEmployee(request.body);
    const employee = await personnelService.getEmployeeById(employeeId);
    return {
      success: true,
      data: employee!,
    };
  });

  // Update employee
  server.put('/:id', {
    preHandler: [requireAuth(), fastify.can(AppPermission.PERSONNEL_WRITE)],
    schema: {
      params: z.object({
        id: z.string(),
      }),
      body: updateEmployeeSchema,
      response: {
        200: employeeResponseWrapperSchema,
        404: errorResponseSchema,
      },
    },
  }, async (request: any) => {
    const { id } = request.params;
    const { role } = request.body;
    const currentUserId = request.user.id;

    // Security check: Only GOD user can set GOD role, and only for themselves
    if (role === 'GOD' && id !== currentUserId) {
      throw new Error('Only GOD user can set GOD role for themselves');
    }

    if (role && role !== 'GOD' && ROLE_ASSIGN_PERMISSIONS[role]) {
      const canAssign = await hasPermission(request.user, ROLE_ASSIGN_PERMISSIONS[role]);
      if (!canAssign) {
        throw new Error('Недостаточно прав для назначения этой роли');
      }
    }

    await personnelService.updateEmployee(id, request.body);
    const employee = await personnelService.getEmployeeById(id);
    if (!employee) {
      throw new Error('Employee not found');
    }
    return {
      success: true,
      data: employee,
    };
  });

  // Fire employee
  server.post('/:id/fire', {
    preHandler: [requireAuth(), fastify.can(AppPermission.PERSONNEL_MANAGE)],
    schema: {
      params: z.object({
        id: z.string(),
      }),
      body: fireEmployeeSchema,
      response: {
        200: z.object({
          success: z.boolean(),
          message: z.string(),
        }),
      },
    },
  }, async (request: any) => {
    const { firedAt } = request.body;
    const firedAtDate = firedAt ? new Date(firedAt) : undefined;
    await personnelService.fireEmployee(
      request.params.id,
      request.body.firedReason,
      personnelService.getEmployeeByIdIncludingArchived,
      firedAtDate
    );
    return {
      success: true,
      message: 'Employee fired successfully',
    };
  });

  // Rehire employee (with return reason and system note)
  server.post('/:id/rehire', {
    preHandler: [requireAuth(), fastify.can(AppPermission.PERSONNEL_MANAGE)],
    schema: {
      params: z.object({
        id: z.string(),
      }),
      body: rehireEmployeeSchema,
      response: {
        200: z.object({
          success: z.boolean(),
          message: z.string(),
        }),
      },
    },
  }, async (request: any) => {
    // Get adminId from authenticated user
    const adminId = request.user.id;
    const { rehireDate, maidPeriods } = request.body;
    await personnelService.rehireEmployee(
      request.params.id,
      request.body.returnReason,
      adminId,
      personnelService.getEmployeeByIdIncludingArchived,
      rehireDate,
      maidPeriods
    );
    return {
      success: true,
      message: 'Employee rehired successfully',
    };
  });

  // Restore employee (rehire)
  server.post('/:id/restore', {
    preHandler: [requireAuth(), fastify.can(AppPermission.PERSONNEL_MANAGE)],
    schema: {
      params: z.object({
        id: z.string(),
      }),
      body: restoreEmployeeSchema,
      response: {
        200: z.object({
          success: z.boolean(),
          message: z.string(),
        }),
      },
    },
  }, async (request: any) => {
    await personnelService.restoreEmployee(request.params.id);
    return {
      success: true,
      message: 'Employee restored successfully',
    };
  });

  // Delete employee (soft delete)
  server.delete('/:id', {
    preHandler: [requireAuth(), fastify.can(AppPermission.PERSONNEL_DELETE)],
    schema: {
      params: z.object({
        id: z.string(),
      }),
      response: {
        200: z.object({
          success: z.boolean(),
          message: z.string(),
        }),
      },
    },
  }, async (request: any) => {
    await personnelService.deleteEmployee(request.params.id);
    return {
      success: true,
      message: 'Employee deleted successfully',
    };
  });

  // Export employees to CSV with BOM
  server.get('/export', {
    preHandler: [requireAuth(), fastify.can(AppPermission.PERSONNEL_READ)],
    schema: {
      response: {
        200: z.string(),
      },
    },
  }, async (request: any, reply: any) => {
    const currentUserId = request.user.id;
    const employees = await personnelService.getEmployees(currentUserId);
    const csv = personnelService.exportEmployeesToCSV(employees);

    reply.raw.setHeader('Content-Type', 'text/csv; charset=utf-8');
    reply.raw.setHeader('Content-Disposition', 'attachment; filename="employees.csv"');

    return csv;
  });

  // Check username availability
  server.get('/check-username', {
    preHandler: [requireAuth(), fastify.can(AppPermission.PERSONNEL_READ)],
    schema: {
      querystring: checkUsernameSchema,
      response: {
        200: checkUsernameResponseSchema,
      },
    },
  }, async (request: any) => {
    const { username } = request.query;
    const available = await personnelService.checkUsernameAvailability(username);
    return {
      success: true,
      data: {
        available,
      },
    };
  });
};
