import { AxiosInstance } from "axios";
import { presignedAxios } from "./presignedAxios";
import { withHeaders } from "@/record-management/services/api/withHeaders";
import { XSSUploadValidator, XSSUploadValidationContext } from "./validation";

/**
 * NOTE: File validation happens on the server-side.
 * The browser uses XSSUploadValidator for early feedback to users.
 * The server uses BufferContentValidator with file-type library for actual validation.
 */

export const UUID_RE =
  /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;

const isValidUUID = (id: string): boolean => UUID_RE.test(id);
type UUID = string;

// ---- UploadContext ----
export interface UploadContext<TBody> {
  endpoint: string;
  buildBody: (file: File, fileName: string, parentId: UUID) => TBody;
  allowedMimeTypes?: string[];
  allowedExtensions?: string[];
  maxSizeMB?: number;
  uploadContext?:
    | "record"
    | "external-portal"
    | "incoming"
    | "outgoing"
    | "signature";
  module?: "record-management" | "user-management" | "external-portal";
}

/**
 * Upload file metadata and get presigned URL
 * Runs client-side XSS validation before requesting presigned URL
 * Server-side validation happens after file is uploaded
 */
export async function uploadMeta<TBody>(
  ctx: UploadContext<TBody>,
  file: File,
  fileName: string,
  parentId: UUID,
  axiosInstance: AxiosInstance,
) {
  if (!isValidUUID(parentId)) throw new Error("Invalid UUID provided");

  // Run client-side XSS validation before requesting presigned URL
  const validationContext: XSSUploadValidationContext = {
    allowedMimeTypes: ctx.allowedMimeTypes,
    allowedExtensions: ctx.allowedExtensions,
    maxSizeMB: ctx.maxSizeMB,
    uploadContext: ctx.uploadContext || "record",
  };

  const validationResult = await XSSUploadValidator.validateFileForUpload(
    file,
    validationContext,
  );

  if (!validationResult.isValid) {
    throw new Error(
      validationResult.clientValidation.fileName?.error ||
        validationResult.clientValidation.extension?.error ||
        validationResult.clientValidation.mimeType?.error ||
        validationResult.clientValidation.size?.error ||
        "File validation failed",
    );
  }

  const body = ctx.buildBody(file, fileName, parentId);
  const { data } = await axiosInstance.post(ctx.endpoint, body, {
    headers: withHeaders(),
  });
  return data;
}

export const getFile = async (
  endpoint: string,
  id: string,
  axiosInstance: AxiosInstance,
) => {
  try {
    const response = await axiosInstance.get(`${endpoint}/${id}`, {
      headers: withHeaders(),
    });
    return response.data;
  } catch (error) {
    console.error("Failed to fetch file:", error);
    throw error;
  }
};

/**
 * Upload file to presigned URL
 * Server-side validation happens after upload
 */
export const uploadFile = async (file: File, uploadUrl: string) => {
  if (!uploadUrl) throw new Error("Upload URL is undefined.");

  const uploadRes = await presignedAxios.put(uploadUrl, file, {
    headers: {
      "Content-Type": file.type,
    },
  });

  // Validate response status for presigned uploads
  if (uploadRes.status < 200 || uploadRes.status >= 300) {
    throw new Error(
      `Upload failed with status ${uploadRes.status}: ${uploadRes.statusText}`,
    );
  }

  return uploadRes;
};

export async function updateStatus(
  endpoint: string,
  fileId: UUID,
  parentId: UUID,
  uploadedSuccessfully: boolean,
  axiosInstance: AxiosInstance,
  validationContext?: XSSUploadValidationContext,
) {
  if (!isValidUUID(fileId) || !isValidUUID(parentId))
    throw new Error("Invalid UUID provided");

  // Prepare status update payload
  const statusPayload: any = {
    id: fileId,
    parentId,
    uploadedSuccessfully,
  };

  // Include validation context if provided (for server-side validation)
  if (validationContext) {
    statusPayload.validationContext = validationContext;
  }

  // Temporarily disabled upload-status request.
  // Re-enable when backend upload-status sync is needed again.
  // const { data } = await axiosInstance.patch(
  //   `${endpoint}/${fileId}/upload-status`,
  //   statusPayload,
  //   { headers: withHeaders() }
  // );
  // return data;
  return statusPayload;
}

export async function updateIncomingRecordStatus(
  endpoint: string,
  fileId: UUID,
  uploadedSuccessfully: boolean,
  axiosInstance: AxiosInstance,
) {
  if (!isValidUUID(fileId)) throw new Error("Invalid UUID provided");

  // Temporarily disabled upload-status request.
  // Re-enable when backend upload-status sync is needed again.
  // const { data } = await axiosInstance.patch(
  //   `${endpoint}/upload-status`,
  //   { recordId: fileId, uploadedSuccessfully },
  //   { headers: withHeaders() }
  // );
  // return data;
  return { recordId: fileId, uploadedSuccessfully };
}

export async function deleteFile(
  endpoint: string,
  fileId: UUID,
  axiosInstance: AxiosInstance,
  parentId?: UUID, // optional, in case backend needs it
) {
  if (!isValidUUID(fileId)) throw new Error("Invalid fileId provided");
  if (parentId && !isValidUUID(parentId))
    throw new Error("Invalid parentId provided");

  // Construct URL (add parentId query if needed)
  const url =
    parentId !== undefined
      ? `${endpoint}/${fileId}?parentId=${parentId}`
      : `${endpoint}/${fileId}`;

  const { data } = await axiosInstance.delete(url, {
    headers: withHeaders(),
  });
  return data;
}
