// src/server/shared/plugins/session-guard.ts
// Проверка сессий: sessionDuration + inactivityTimeout

import fp from 'fastify-plugin';
import { db } from '../db/client';
import { employees } from '@features/personnel/db/employees.table';
import { eq } from 'drizzle-orm';
import { dayjs } from '../lib/dayjs';
import { loggerService } from '../lib/logger';
import {
  getCachedSessionPolicy,
  getSessionPolicyForRole,
  setCachedSessionPolicy,
} from '@features/system/system.service';
import { getClearCookieOptions } from '../lib/env';

const sessionLogger = loggerService.get('AUTH');
const DB_DEBOUNCE_MS = 60_000;
const activityWriteCache = new Map<string, number>();
const clearOpts = getClearCookieOptions();

export default fp(async (fastify) => {
  fastify.addHook('onRequest', async (request: any, reply: any) => {
    try {
      if (!request.user?.id || !request.user?.role) return;

      const userId = request.user.id as string;
      const role = request.user.role as string;

      let policy = getCachedSessionPolicy(role);
      if (!policy) {
        policy = await getSessionPolicyForRole(role);
        if (policy) setCachedSessionPolicy(role, policy);
      }
      if (!policy) return;

      const now = dayjs().tz('Europe/Moscow');

      if (policy.sessionDuration) {
        const iat = (request.user as any).iat;
        if (iat) {
          const loginTime = dayjs.unix(iat).tz('Europe/Moscow');
          const maxEnd = loginTime.add(policy.sessionDuration, 'minute');
          if (now.isAfter(maxEnd)) {
            sessionLogger.info(`[SessionGuard] Session duration exceeded for ${userId} (role=${role}, limit=${policy.sessionDuration}min)`);
            reply.clearCookie('auth_token', clearOpts);
            reply.code(401).send({
              success: false,
              error: { message: 'Сессия истекла по времени', code: 'SESSION_EXPIRED' },
            });
            return;
          }
        }
      }

      if (policy.inactivityTimeout) {
        const lastWrite = activityWriteCache.get(userId);
        if (lastWrite && Date.now() - lastWrite < DB_DEBOUNCE_MS) return;

        const [row] = await db
          .select({ lastActivityAt: employees.lastActivityAt })
          .from(employees)
          .where(eq(employees.id, userId))
          .limit(1);

        const lastActivity = row?.lastActivityAt
          ? dayjs(row.lastActivityAt).tz('Europe/Moscow')
          : null;

        if (lastActivity) {
          const iat = (request.user as any).iat;
          if (iat && lastActivity.isBefore(dayjs.unix(iat))) {
            activityWriteCache.set(userId, Date.now());
            db.update(employees)
              .set({ lastActivityAt: new Date() })
              .where(eq(employees.id, userId))
              .catch(() => {});
            return;
          }

          const maxInactivity = lastActivity.add(policy.inactivityTimeout, 'minute');
          if (now.isAfter(maxInactivity)) {
            sessionLogger.info(`[SessionGuard] Inactivity timeout for ${userId} (role=${role}, limit=${policy.inactivityTimeout}min)`);
            reply.clearCookie('auth_token', clearOpts);
            reply.code(401).send({
              success: false,
              error: { message: 'Сессия завершена из-за бездействия', code: 'INACTIVITY' },
            });
            return;
          }
        }

        activityWriteCache.set(userId, Date.now());

        db.update(employees)
          .set({ lastActivityAt: new Date() })
          .where(eq(employees.id, userId))
          .catch(() => {});
      }
    } catch (err) {
      sessionLogger.error('[SessionGuard] Error in session check', { err });
    }
  });
}, { name: 'session-guard' });
