import { Meta } from '../types'
import { PdfPage } from '../types/drawing.ts'
import { MOST_COMMON_MEDIA_TYPES } from './const.ts'
import { extractMarksFromSignedMeta } from './mark.ts'
import {
  addMarks,
  groupMarksByFileNamePage,
  isPdf,
  pdfToImages
} from './pdf.ts'
import JSZip from 'jszip'

export const getZipWithFiles = async (
  meta: Meta,
  files: { [filename: string]: SigitFile }
): Promise<JSZip> => {
  const zip = new JSZip()
  const marks = extractMarksFromSignedMeta(meta)
  const marksByFileNamePage = groupMarksByFileNamePage(marks)

  for (const [fileName, file] of Object.entries(files)) {
    if (file.isPdf) {
      // Handle PDF Files
      const blob = await addMarks(file, marksByFileNamePage[fileName])
      zip.file(`files/${fileName}`, blob)
    } else {
      // Handle other files
      zip.file(`files/${fileName}`, file)
    }
  }

  return zip
}

/**
 * Converts a PDF ArrayBuffer to a generic PDF File
 * @param arrayBuffer of a PDF
 * @param fileName identifier of the pdf file
 * @param type optional file type (defaults to pdf)
 */
export const toFile = (
  arrayBuffer: ArrayBuffer,
  fileName: string,
  type: string = 'application/pdf'
): File => {
  const blob = new Blob([arrayBuffer], { type })
  return new File([blob], fileName, { type })
}

export class SigitFile extends File {
  extension: string
  isPdf: boolean
  isImage: boolean

  pages?: PdfPage[]
  objectUrl?: string

  constructor(file: File) {
    super([file], file.name, { type: file.type })
    this.isPdf = isPdf(this)
    this.isImage = isImage(this)
    this.extension = extractFileExtension(this.name)
  }

  async process() {
    if (this.isPdf) this.pages = await pdfToImages(await this.arrayBuffer())
    if (this.isImage) this.objectUrl = URL.createObjectURL(this)
  }
}

export const getSigitFile = async (file: File) => {
  const sigitFile = new SigitFile(file)
  // Process sigit file
  // - generate pages for PDF files
  // - generate ObjectRL for image files
  await sigitFile.process()
  return sigitFile
}

/**
 * Takes an ArrayBuffer and converts to Sigit's Internal File type
 * @param arrayBuffer
 * @param fileName
 */
export const convertToSigitFile = async (
  arrayBuffer: ArrayBuffer,
  fileName: string
): Promise<SigitFile> => {
  const type = getMediaType(extractFileExtension(fileName))
  const file = toFile(arrayBuffer, fileName, type)
  const sigitFile = await getSigitFile(file)
  return sigitFile
}

/**
 * @param fileNames - List of filenames to check
 * @returns List of extensions and if all are same
 */
export const extractFileExtensions = (fileNames: string[]) => {
  const extensions = fileNames.reduce((result: string[], file: string) => {
    const extension = file.split('.').pop()
    if (extension) {
      result.push(extension)
    }
    return result
  }, [])

  const isSame = extensions.every((ext) => ext === extensions[0])

  return { extensions, isSame }
}

/**
 * @param fileName - Filename to check
 * @returns Extension string
 */
export const extractFileExtension = (fileName: string) => {
  const parts = fileName.split('.')
  return parts[parts.length - 1]
}

export const getMediaType = (extension: string) => {
  return MOST_COMMON_MEDIA_TYPES.get(extension)
}

export const isImage = (file: File) => {
  const validImageMediaTypes = [
    'image/png',
    'image/jpeg',
    'image/jpg',
    'image/gif',
    'image/svg+xml',
    'image/bmp',
    'image/x-icon'
  ]

  return validImageMediaTypes.includes(file.type.toLowerCase())
}
