// src/server/shared/plugins/jwt.ts
// JWT authentication plugin for Fastify with HttpOnly Cookie support

import fp from 'fastify-plugin';
import jwt from '@fastify/jwt';
import { db } from '../db/client';
import { employees } from '../../features/personnel/db/employees.table';
import { eq } from 'drizzle-orm';
import { AuthorizationError, ACCOUNT_DISABLED_ERROR_CODE, isAccountAccessAllowed } from '../lib/auth';
import { dayjs } from '../lib/dayjs';

export default fp(async (fastify) => {
  fastify.register(jwt, {
    secret: process.env.JWT_SECRET || 'super-secret-key',
    cookie: {
      cookieName: 'auth_token',
      signed: false,
    },
    // Disable token from headers - only accept from HttpOnly cookie
    sign: {
      expiresIn: '7d',
    },
  });

  // Add onRequest hook to check if user is fired before processing request
  fastify.addHook('onRequest', async (request: any, reply: any) => {
    try {
      // Try to verify JWT token from cookie
      const user = await request.jwtVerify({ onlyCookie: true }).catch(() => null);
      
      // If no valid token, skip check
      if (!user) {
        return;
      }

      // Get fresh user data from database to check current isFired, createdAt and archivedAt status
      const freshUserData = await db
        .select({
          id: employees.id,
          isFired: employees.isFired,
          createdAt: employees.createdAt,
          archivedAt: employees.archivedAt,
        })
        .from(employees)
        .where(eq(employees.id, user.id))
        .limit(1);

      // If user not found in DB, deny access
      if (!freshUserData || freshUserData.length === 0) {
        reply.clearCookie('auth_token', {
          path: '/',
          httpOnly: true,
          secure: process.env.NODE_ENV === 'production',
          sameSite: 'strict',
        });

        throw new AuthorizationError('Пользователь не найден', 403, ACCOUNT_DISABLED_ERROR_CODE);
      }

      const { isFired, createdAt, archivedAt } = freshUserData[0];

      // Use of shared helper to check if account access is allowed
      const accessAllowed = isAccountAccessAllowed({
        isFired: Boolean(isFired),
        createdAt,
        archivedAt,
      });

      if (!accessAllowed) {
        // Determine appropriate error message based on reason
        const mskNow = dayjs().tz('Europe/Moscow');
        const isFutureHire = mskNow.isBefore(dayjs.utc(createdAt).tz('Europe/Moscow').startOf('day'));
        const errorMessage = isFutureHire 
          ? 'Срок действия учетной записи еще не начался'
          : 'Срок действия учетной записи истек';

        reply.clearCookie('auth_token', {
          path: '/',
          httpOnly: true,
          secure: process.env.NODE_ENV === 'production',
          sameSite: 'strict',
        });

        throw new AuthorizationError(errorMessage, 403, ACCOUNT_DISABLED_ERROR_CODE);
      }
    } catch (err) {
      // Re-throw AuthorizationError
      if (err instanceof AuthorizationError) {
        throw err;
      }
      // Log unexpected errors but don't block requests on JWT/DB failures
      fastify.log.error({ err }, 'Failed to validate user status in onRequest hook');
    }
  });

  fastify.decorate('authenticate', async (request: any, reply: any): Promise<void> => {
    try {
      // jwtVerify() automatically checks for token in configured cookie
      await request.jwtVerify();
    } catch (err) {
      reply.send(err);
    }
    // Явный возврат из async функции разрешает запрос
    return;
  });
}, { name: 'jwt' });
