import axios from 'axios'
import {
  decryptArrayBuffer,
  encryptArrayBuffer,
  getHash,
  isOnline,
  uploadToFileStorage
} from '../../../utils'
import { MarkStrategy } from '../MarkStrategy'
import { MarkInputSignature } from './Input'
import { MarkRenderSignature } from './Render'

export const SignatureStrategy: MarkStrategy = {
  input: MarkInputSignature,
  render: MarkRenderSignature,
  /**
   * Encrypts a stringified signature object, creates an encrypted JSON file,
   * and uploads it to a file storage if the user is online.
   * @param value
   * @param encryptionKey
   * @returns the original value string
   */
  encryptAndUpload: async (value, encryptionKey) => {
    // Value is the stringified signature object
    // Encode it to the arrayBuffer
    const encoder = new TextEncoder()
    const uint8Array = encoder.encode(value)

    if (!encryptionKey) {
      throw new Error('Signature requires an encryption key')
    }

    // Encrypt the file contents with the same encryption key from the create signature
    const encryptedArrayBuffer = await encryptArrayBuffer(
      uint8Array,
      encryptionKey
    )

    const hash = await getHash(encryptedArrayBuffer)
    if (!hash) {
      throw new Error("Can't get encrypted file hash.")
    }

    // Create the encrypted json file from array buffer and hash
    const file = new File([encryptedArrayBuffer], `${hash}.json`)

    if (await isOnline()) {
      try {
        const urls = await uploadToFileStorage(file)
        console.info(
          `${file.name} uploaded to following file storages: ${urls.join(', ')}`
        )
        return value
      } catch (error) {
        if (error instanceof Error) {
          console.error(
            `Error occurred in uploading file ${file.name}`,
            error.message
          )
        }
      }
    } else {
      // TODO: offline
    }

    return value
  },
  fetchAndDecrypt: async (value, encryptionKey) => {
    if (!encryptionKey) {
      throw new Error('Signature requires an encryption key')
    }

    const encryptedArrayBuffer = await axios.get(value, {
      responseType: 'arraybuffer'
    })

    // Verify hash
    const parts = value.split('/')
    const urlHash = parts[parts.length - 1]
    const hash = await getHash(encryptedArrayBuffer.data)
    if (hash !== urlHash) {
      // TODO: handle hash verification failing
      throw new Error('Unable to verify signature')
    }

    const arrayBuffer = await decryptArrayBuffer(
      encryptedArrayBuffer.data,
      encryptionKey
    ).catch((err) => {
      console.log('err in decryption:>> ', err)
      return null
    })

    if (arrayBuffer) {
      // decode json
      const decoder = new TextDecoder()
      const json = decoder.decode(arrayBuffer)
      return json
    }

    // TOOD: offline
    return value
  }
}
