import type { NextFunction, Request, Response } from 'express';
import prisma from '../config/db';
import { findFallbackPatientPortalAccessById } from '../services/patient-account.store';
import { verifyPatientSessionToken } from '../services/patient-session.service';

export type PatientPortalRequest = Request & {
  patientAuth: {
    accountId: string;
    patientId: string;
    email: string;
    sourceMode: 'database' | 'local-fallback';
  };
};

const extractBearerToken = (authorizationHeader: string | undefined) => {
  if (!authorizationHeader) {
    return null;
  }

  const [scheme, token] = authorizationHeader.split(' ');

  if (!scheme || !token || scheme.toLowerCase() !== 'bearer') {
    return null;
  }

  return token.trim();
};

export const getPatientAuthContext = (req: Request) => {
  const request = req as Partial<PatientPortalRequest>;
  return request.patientAuth ?? null;
};

export const requirePatientSession = async (req: Request, res: Response, next: NextFunction) => {
  const token = extractBearerToken(req.headers.authorization);

  if (!token) {
    return res.status(401).json({ error: 'Debes iniciar sesion como paciente.' });
  }

  const verifiedSession = verifyPatientSessionToken(token);

  if (!verifiedSession) {
    return res.status(401).json({ error: 'La sesion del paciente es invalida o vencio.' });
  }

  try {
    const account = await prisma.patientPortalAccess.findUnique({
      where: { id: verifiedSession.accountId },
    });

    if (
      !account ||
      !account.active ||
      account.patientId !== verifiedSession.patientId ||
      account.email !== verifiedSession.email
    ) {
      return res.status(401).json({ error: 'No se pudo validar la sesion del paciente.' });
    }

    (req as PatientPortalRequest).patientAuth = {
      accountId: account.id,
      patientId: account.patientId,
      email: account.email,
      sourceMode: 'database',
    };

    return next();
  } catch (error) {
    console.warn('Database unavailable while validating patient session, using local fallback store.', error);
  }

  const fallbackAccount = await findFallbackPatientPortalAccessById(verifiedSession.accountId);

  if (
    !fallbackAccount ||
    !fallbackAccount.active ||
    fallbackAccount.patientId !== verifiedSession.patientId ||
    fallbackAccount.email !== verifiedSession.email
  ) {
    return res.status(401).json({ error: 'No se pudo validar la sesion del paciente.' });
  }

  (req as PatientPortalRequest).patientAuth = {
    accountId: fallbackAccount.id,
    patientId: fallbackAccount.patientId,
    email: fallbackAccount.email,
    sourceMode: 'local-fallback',
  };

  return next();
};
