import { ILabelValue } from "@/types/shared";
import type { AxiosResponse } from "axios";
import * as moment from "moment";
import { z } from "zod";
export const unixToDate = ({
  hasTime = false,
  unix,
}: {
  unix: number;
  hasTime?: boolean;
}) => {
  return unix === 0 ? "-" : moment.unix(unix).format(`YYYY/MM/DD - ${hasTime ? "HH:MM" : ""}`);
};
export const msToDate = (ms: number) => {
  return moment.default(ms).format("YYYY/MM/DD");
};

export const isoStringToDate = ({
  hasTime = false,
  isoString,
}: {
  isoString: string;
  hasTime: boolean;
}): string => {
  return moment
    .default(isoString)
    .format(`YYYY/MM/DD ${hasTime ? "HH:MM" : ""}`);
};

export const getStatusColor = <T>(status: T) => {
  switch (status) {
    case "active":
      return "bg-green text-white";
    case "expired":
      return "bg-orange-light text-white";
    case "cancelled":
      return "bg-error-dark text-white";
    case "awarded":
      return "bg-blue-light-3";
    case "draft":
      return "bg-gray-3";
  }
};
export const formatPhoneNumber = (input?: string) => {
  if (!input) return "";

  let cleaned = input.replace(/[^\d+]/g, "");

  const hasPlus = cleaned.startsWith("+");
  if (hasPlus) {
    cleaned = cleaned.slice(1);
  }

  const groups = [];
  let i = 0;
  while (i < cleaned.length) {
    const size = i === 0 ? 3 : 4;
    groups.push(cleaned.slice(i, i + size));
    i += size;
  }

  return (hasPlus ? "+" : "") + groups.join(" ");
};
export const capitalize = (text: string) => {
  return text
    .split(" ")
    .map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
    .join(" ")
    .trim();
};
export const getEnumAsArray = <T extends object>(target: T): ILabelValue[] => {
  return Object.entries(target).map(([key, value]) => ({
    label: capitalize(key.split("_").join(" ")),
    value,
  }));
};
export const truncateString = (
  str: string,
  startLength: number = 10,
  endLength: number = 10,
): string => {
  if (str.length <= startLength + endLength) {
    return str;
  }
  return str.slice(0, startLength) + "..." + str.slice(-endLength);
};

export function parseWithSchema<T>(
  schema: z.ZodSchema<T>,
  data: unknown,
  errorContext: string,
): T {
  const parsedData = schema.safeParse(data);

  if (parsedData.success) {
    return parsedData.data;
  } else {
    console.error(`ERROR caught in ${errorContext}`);
    throw new Error(`ERROR caught in ${errorContext}`);
  }
}

export const deleteEmptyKeys = (obj: Record<string, any>) => {
  const newObj = { ...obj };
  for (const key in newObj) {
    if (Object.prototype.hasOwnProperty.call(newObj, key)) {
      if (!newObj[key]) {
        delete newObj[key];
      }
    }
  }
  return newObj;
};

/** Serializes params with repeated keys for arrays (e.g. cpv_codes=1&cpv_codes=2). */
export const buildQueryString = (params: Record<string, any>): string => {
  const searchParams = new URLSearchParams();

  for (const [key, value] of Object.entries(params)) {
    if (value === undefined || value === null || value === "") {
      continue;
    }

    if (Array.isArray(value)) {
      for (const item of value) {
        if (item !== undefined && item !== null && item !== "") {
          searchParams.append(key, String(item));
        }
      }
      continue;
    }

    searchParams.append(key, String(value));
  }

  return searchParams.toString();
};

export const serializeAxiosParams = buildQueryString;
export const isValidAlpha2CountryCode = (code: string) => /^[A-Za-z]{2}$/.test(code);

/** True when a date-range filter value holds at least one usable (positive, finite) unix bound. */
export const isDateRangeActive = (value: unknown): boolean => {
  if (!Array.isArray(value) || value.length === 0) return false;
  return value.some((bound) => {
    if (bound === undefined || bound === null || bound === "") return false;
    const n = typeof bound === "number" ? bound : Number(bound);
    return Number.isFinite(n) && n > 0;
  });
};

export const getPaginatedRowNumber = ({
  index,
  page = 1,
  limit = 10,
}: {
  index: number;
  page?: number;
  limit?: number;
}) => {
  const safePage = page > 0 ? page : 1;
  const safeLimit = limit > 0 ? limit : 10;

  return (safePage - 1) * safeLimit + index + 1;
};

export const ALLOWED_IMAGE_FILE_TYPES = [
  "image/jpeg",
  "image/png",
  "image/webp",
  "image/jpg",
];

export const MAX_IMAGE_FILE_SIZE = 5 * 1024 * 1024;

export const validateImageFile = ({
  file,
  allowedTypes = ALLOWED_IMAGE_FILE_TYPES,
  maxSize = MAX_IMAGE_FILE_SIZE,
}: {
  file: File;
  allowedTypes?: string[];
  maxSize?: number;
}) => {
  if (!allowedTypes.includes(file.type)) {
    return "Only JPG, PNG, or WEBP images are allowed";
  }

  if (file.size > maxSize) {
    return "Image size must be 5MB or less";
  }

  return null;
};

/** Company document upload limits (see company-documents API guide). */
export const ALLOWED_DOCUMENT_FILE_TYPES = [
  // Images
  "image/jpeg",
  "image/png",
  "image/gif",
  "image/webp",
  "image/svg+xml",
  // Documents
  "application/pdf",
  "application/msword",
  "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
  "application/vnd.ms-excel",
  "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
  // Text / data
  "text/plain",
  "text/csv",
  "application/json",
  // Archives
  "application/zip",
  "application/x-rar-compressed",
  "application/x-7z-compressed",
];

export const MAX_DOCUMENT_FILE_SIZE = 100 * 1024 * 1024; // 100 MB
export const MAX_DOCUMENT_FILES = 20;

/** `accept` attribute mirroring the allowed document MIME types. */
export const DOCUMENT_FILE_ACCEPT =
  ".pdf,.doc,.docx,.xls,.xlsx,.png,.jpg,.jpeg,.gif,.webp,.svg,.txt,.csv,.json,.zip,.rar,.7z";

export const validateDocumentFile = ({
  file,
  allowedTypes = ALLOWED_DOCUMENT_FILE_TYPES,
  maxSize = MAX_DOCUMENT_FILE_SIZE,
}: {
  file: File;
  allowedTypes?: string[];
  maxSize?: number;
}): string | null => {
  // Browsers occasionally send an empty type; the server then infers it from
  // the file extension, so only reject explicitly disallowed types.
  if (file.type && !allowedTypes.includes(file.type)) {
    return `Unsupported file type: ${file.type}`;
  }

  if (file.size > maxSize) {
    return "File size must be 100MB or less";
  }

  return null;
};

export const formatFileSize = (bytes: number) => {
  if (bytes < 1024) return `${bytes} B`;
  if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
  return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
};

/** Saves a blob from an axios GET with `responseType: "blob"` (uses Content-Disposition when present). */
export const saveBlobFromAxiosResponse = (
  response: AxiosResponse<Blob>,
  fallbackFilename: string,
) => {
  const disposition = response.headers["content-disposition"] as
    | string
    | undefined;
  let filename = fallbackFilename;
  if (disposition) {
    const utf8Match = /filename\*=UTF-8''([^;\n]+)/i.exec(disposition);
    const asciiMatch = /filename="([^"]+)"/i.exec(disposition);
    const raw = utf8Match?.[1] ?? asciiMatch?.[1];
    if (raw) {
      try {
        filename = decodeURIComponent(raw.replace(/['"]/g, ""));
      } catch {
        filename = raw;
      }
    }
  }
  const url = URL.createObjectURL(response.data);
  const anchor = document.createElement("a");
  anchor.href = url;
  anchor.download = filename;
  document.body.appendChild(anchor);
  anchor.click();
  anchor.remove();
  URL.revokeObjectURL(url);
};