import { PatientDocument, CreatePatientDocument } from "@repo/db/types";
import { prisma as db } from "@repo/db/client";
import path from "path";
import fs from "fs/promises";
import { randomBytes } from "crypto";

const UPLOAD_DIR = path.join(process.cwd(), "uploads", "patient-documents");

// Get local file path from URL
const getLocalFilePath = (fileUrl: string): string => {
  const filename = fileUrl.split('/').pop() || '';
  return path.join(UPLOAD_DIR, filename);
};

// Get the base URL for serving files
const getBaseUrl = (): string => {
  // For development, use localhost instead of 0.0.0.0
  const host = process.env.HOST === '0.0.0.0' ? 'localhost' : (process.env.HOST || 'localhost');
  const port = process.env.PORT || '5000';
  const protocol = process.env.NODE_ENV === 'production' ? 'https' : 'http';
  return `${protocol}://${host}:${port}`;
};

// Ensure upload directory exists
const ensureUploadDir = async () => {
  try {
    await fs.access(UPLOAD_DIR);
  } catch {
    await fs.mkdir(UPLOAD_DIR, { recursive: true });
  }
};

// Generate unique filename
const generateUniqueFilename = (originalName: string): string => {
  const ext = path.extname(originalName);
  const name = path.basename(originalName, ext);
  const timestamp = Date.now();
  const random = randomBytes(4).toString("hex");
  return `${name}-${timestamp}-${random}${ext}`;
};

export const patientDocumentsStorage = {
  // Create a new patient document
  createPatientDocument: async (
    patientId: number,
    filename: string,
    originalName: string,
    mimeType: string,
    fileSize: number,
    buffer: Buffer
  ): Promise<PatientDocument> => {
    await ensureUploadDir();
    
    const uniqueFilename = generateUniqueFilename(filename);
    const localFilePath = path.join(UPLOAD_DIR, uniqueFilename);
    
    // Save file to disk
    await fs.writeFile(localFilePath, buffer);
    
    // Create the full URL for accessing the file using the uploads directory structure
    const fileUrl = `${getBaseUrl()}/uploads/patient-documents/${uniqueFilename}`;
    
    // Create database record with full URL
    const document = await db.patientDocument.create({
      data: {
        patientId,
        filename: uniqueFilename,
        originalName,
        mimeType,
        fileSize: BigInt(fileSize),
        filePath: fileUrl, // Store the full URL instead of local path
      },
    });
    
    return document;
  },

  // Get all documents for a patient
  getDocumentsByPatientId: async (patientId: number): Promise<PatientDocument[]> => {
    return await db.patientDocument.findMany({
      where: { patientId },
      orderBy: { uploadedAt: "desc" },
    });
  },

  // Get a specific document by ID
  getDocumentById: async (id: number): Promise<PatientDocument | null> => {
    return await db.patientDocument.findUnique({
      where: { id },
      include: {
        patient: {
          select: {
            firstName: true,
            lastName: true,
          },
        },
      },
    });
  },

  // Get document file content
  getDocumentFile: async (id: number): Promise<{ buffer: Buffer; document: PatientDocument } | null> => {
    const document = await db.patientDocument.findUnique({
      where: { id },
    });
    
    if (!document) {
      return null;
    }
    
    try {
      // Get local file path from the stored URL
      const localFilePath = getLocalFilePath(document.filePath);
      const buffer = await fs.readFile(localFilePath);
      return { buffer, document };
    } catch (error) {
      console.error("Error reading file:", error);
      return null;
    }
  },

  // Delete a document
  deleteDocument: async (id: number): Promise<boolean> => {
    const document = await db.patientDocument.findUnique({
      where: { id },
    });
    
    if (!document) {
      return false;
    }
    
    try {
      // Get local file path from the stored URL
      const localFilePath = getLocalFilePath(document.filePath);
      
      // Delete file from disk
      await fs.unlink(localFilePath);
      
      // Delete database record
      await db.patientDocument.delete({
        where: { id },
      });
      
      return true;
    } catch (error) {
      console.error("Error deleting document:", error);
      return false;
    }
  },

  // Update document metadata
  updateDocument: async (id: number, data: Partial<CreatePatientDocument>): Promise<PatientDocument | null> => {
    return await db.patientDocument.update({
      where: { id },
      data,
    });
  },

  // Get documents with pagination
  getDocumentsByPatientIdPaginated: async (
    patientId: number,
    limit: number,
    offset: number
  ): Promise<{ documents: PatientDocument[]; total: number }> => {
    const [documents, total] = await Promise.all([
      db.patientDocument.findMany({
        where: { patientId },
        orderBy: { uploadedAt: "desc" },
        skip: offset,
        take: limit,
      }),
      db.patientDocument.count({
        where: { patientId },
      }),
    ]);
    
    return { documents, total };
  },
};
