import type { Request, Response } from 'express';
import prisma from '../config/db';
import { getFallbackPatientById } from '../services/patient.store';
import {
  deletePatientAttachment,
  InvalidPatientAttachmentError,
  PatientAttachmentNotFoundError,
  createPatientAttachment,
  listPatientAttachments,
  patientAttachmentCategories,
  resolvePatientAttachmentPath,
  serializePatientAttachment,
} from '../services/patient-attachment.store';

const resolveRouteParam = (value: string | string[] | undefined) => (Array.isArray(value) ? value[0] : value);

const normalizeNullableText = (value: unknown) => {
  if (typeof value !== 'string') {
    return null;
  }

  const trimmedValue = value.trim();
  return trimmedValue ? trimmedValue : null;
};

const patientExists = async (patientId: string) => {
  try {
    const patient = await prisma.paciente.findUnique({
      where: { id: patientId },
      select: { id: true },
    });

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

  return Boolean(await getFallbackPatientById(patientId));
};

const getPatientIdFromRequest = (req: Request, res: Response) => {
  const patientId = resolveRouteParam(req.params.id);

  if (!patientId) {
    res.status(400).json({ error: 'Paciente invalido' });
    return null;
  }

  return patientId;
};

const getAttachmentIdFromRequest = (req: Request, res: Response) => {
  const attachmentId = resolveRouteParam(req.params.attachmentId);

  if (!attachmentId) {
    res.status(400).json({ error: 'Adjunto invalido' });
    return null;
  }

  return attachmentId;
};

export const getPatientAttachments = async (req: Request, res: Response) => {
  const patientId = getPatientIdFromRequest(req, res);

  if (!patientId) {
    return;
  }

  if (!(await patientExists(patientId))) {
    return res.status(404).json({ error: 'Paciente no encontrado' });
  }

  try {
    const attachments = await listPatientAttachments(patientId);
    res.setHeader('X-Data-Source', 'local-fallback');

    return res.json({
      attachments: attachments.map(serializePatientAttachment),
    });
  } catch (error) {
    console.error('Error fetching patient attachments:', error);
    return res.status(500).json({ error: 'Error interno del servidor' });
  }
};

export const createPatientAttachmentRecord = async (req: Request, res: Response) => {
  const patientId = getPatientIdFromRequest(req, res);

  if (!patientId) {
    return;
  }

  if (!(await patientExists(patientId))) {
    return res.status(404).json({ error: 'Paciente no encontrado' });
  }

  const category =
    typeof req.body.category === 'string' && patientAttachmentCategories.includes(req.body.category)
      ? req.body.category
      : null;
  const title = typeof req.body.title === 'string' ? req.body.title.trim() : '';
  const fileName = typeof req.body.fileName === 'string' ? req.body.fileName.trim() : '';
  const contentBase64 = typeof req.body.contentBase64 === 'string' ? req.body.contentBase64 : '';
  const mimeType = normalizeNullableText(req.body.mimeType);
  const notes = normalizeNullableText(req.body.notes);
  const uploadedByName = normalizeNullableText(req.body.uploadedByName);

  if (!category) {
    return res.status(400).json({ error: 'Selecciona una categoria valida para el adjunto' });
  }

  if (!title) {
    return res.status(400).json({ error: 'El titulo del adjunto es obligatorio' });
  }

  if (!fileName || !contentBase64) {
    return res.status(400).json({ error: 'Debes seleccionar un archivo para subir' });
  }

  try {
    const attachment = await createPatientAttachment(patientId, {
      category,
      title,
      notes,
      fileName,
      mimeType,
      contentBase64,
      uploadedByName,
    });

    res.setHeader('X-Data-Source', 'local-fallback');
    return res.status(201).json({
      attachment: serializePatientAttachment(attachment),
    });
  } catch (error) {
    if (error instanceof InvalidPatientAttachmentError) {
      return res.status(400).json({ error: error.message });
    }

    console.error('Error creating patient attachment:', error);
    return res.status(500).json({ error: 'Error interno del servidor' });
  }
};

export const getPatientAttachmentContent = async (req: Request, res: Response) => {
  const patientId = getPatientIdFromRequest(req, res);

  if (!patientId) {
    return;
  }

  const attachmentId = getAttachmentIdFromRequest(req, res);

  if (!attachmentId) {
    return;
  }

  try {
    const { attachment, absolutePath } = await resolvePatientAttachmentPath(patientId, attachmentId);
    const shouldDownload = req.query.download === '1' || req.query.download === 'true';

    res.setHeader('X-Data-Source', 'local-fallback');
    res.setHeader('Content-Type', attachment.mimeType);
    res.setHeader('Content-Length', attachment.fileSize.toString());
    res.setHeader(
      'Content-Disposition',
      `${shouldDownload ? 'attachment' : 'inline'}; filename="${encodeURIComponent(attachment.fileName)}"`,
    );

    return res.sendFile(absolutePath);
  } catch (error) {
    if (error instanceof PatientAttachmentNotFoundError) {
      return res.status(404).json({ error: error.message });
    }

    console.error('Error opening patient attachment:', error);
    return res.status(500).json({ error: 'Error interno del servidor' });
  }
};

export const removePatientAttachmentRecord = async (req: Request, res: Response) => {
  const patientId = getPatientIdFromRequest(req, res);

  if (!patientId) {
    return;
  }

  const attachmentId = getAttachmentIdFromRequest(req, res);

  if (!attachmentId) {
    return;
  }

  try {
    const attachment = await deletePatientAttachment(patientId, attachmentId);

    res.setHeader('X-Data-Source', 'local-fallback');
    return res.json({
      attachment: serializePatientAttachment(attachment),
      message: 'Archivo clinico eliminado correctamente',
    });
  } catch (error) {
    if (error instanceof PatientAttachmentNotFoundError) {
      return res.status(404).json({ error: error.message });
    }

    console.error('Error deleting patient attachment:', error);
    return res.status(500).json({ error: 'Error interno del servidor' });
  }
};
