import type { NextFunction, Request, Response } from 'express';
import { Role } from '@prisma/client';
import prisma from '../config/db';
import { findFallbackUserById } from '../services/settings.store';

type StaffSessionUser = {
  id: string;
  email: string;
  name: string;
  role: Role;
  active: boolean;
  sourceMode: 'database' | 'local-fallback';
};

const normalizeHeader = (value: unknown) => (typeof value === 'string' ? value.trim() : '');

const resolveRole = (value: string) => (Object.values(Role).includes(value as Role) ? (value as Role) : null);

const serializeSessionUser = (
  user: {
    id: string;
    email: string;
    name: string;
    role: Role;
    active: boolean;
  },
  sourceMode: 'database' | 'local-fallback',
): StaffSessionUser => ({
  id: user.id,
  email: user.email,
  name: user.name,
  role: user.role,
  active: user.active,
  sourceMode,
});

const loadStaffSessionUser = async (userId: string) => {
  try {
    const databaseUser = await prisma.user.findUnique({
      where: { id: userId },
    });

    if (databaseUser) {
      return serializeSessionUser(databaseUser, 'database');
    }
  } catch (error) {
    console.warn('Database unavailable for staff session validation, using local fallback store.', error);
  }

  const fallbackUser = await findFallbackUserById(userId);

  if (fallbackUser) {
    return serializeSessionUser(fallbackUser, 'local-fallback');
  }

  return null;
};

const getRequestSessionHeaders = (req: Request) => ({
  userId: normalizeHeader(req.header('x-user-id')),
  role: resolveRole(normalizeHeader(req.header('x-user-role'))),
});

export const requireStaffSession = async (req: Request, res: Response, next: NextFunction) => {
  const { userId, role } = getRequestSessionHeaders(req);

  if (!userId || !role) {
    return res.status(401).json({ error: 'Sesion de staff no valida.' });
  }

  const user = await loadStaffSessionUser(userId);

  if (!user || !user.active) {
    return res.status(401).json({ error: 'Usuario interno no autorizado.' });
  }

  if (user.role !== role) {
    return res.status(403).json({ error: 'La sesion no coincide con el rol informado.' });
  }

  res.locals.staffUser = user;
  return next();
};

export const requireAdminSession = async (req: Request, res: Response, next: NextFunction) => {
  await requireStaffSession(req, res, async () => {
    const user = res.locals.staffUser as StaffSessionUser | undefined;

    if (!user || user.role !== Role.ADMIN) {
      return res.status(403).json({ error: 'Solo administracion puede modificar esta base.' });
    }

    return next();
  });
};

export type { StaffSessionUser };
