import { CreateSignatureEventContent, Meta } from '../types'
import { fromUnixTimestamp, parseJson } from '.'
import { Event, verifyEvent } from 'nostr-tools'
import { toast } from 'react-toastify'
import { extractFileExtensions } from './file'
import { handleError } from '../types/errors'
import {
  MetaParseError,
  MetaParseErrorType
} from '../types/errors/MetaParseError'

export enum SignStatus {
  Signed = 'Signed',
  Awaiting = 'Awaiting',
  Pending = 'Pending',
  Invalid = 'Invalid',
  Viewer = 'Viewer'
}

export enum SigitStatus {
  Partial = 'In-Progress',
  Complete = 'Completed'
}

export interface SigitCardDisplayInfo {
  createdAt?: number
  title?: string
  submittedBy?: `npub1${string}`
  signers: `npub1${string}`[]
  fileExtensions: string[]
  signedStatus: SigitStatus
  isValid: boolean
}

/**
 * Wrapper for event parser that throws custom SigitMetaParseError with cause and context
 * @param raw Raw string for parsing
 * @returns parsed Event
 */
export const parseNostrEvent = async (raw: string): Promise<Event> => {
  try {
    const event = await parseJson<Event>(raw)
    return event
  } catch (error) {
    throw new MetaParseError(MetaParseErrorType.PARSE_ERROR_EVENT, {
      cause: handleError(error),
      context: raw
    })
  }
}

/**
 * Wrapper for event content parser that throws custom SigitMetaParseError with cause and context
 * @param raw Raw string for parsing
 * @returns parsed CreateSignatureEventContent
 */
export const parseCreateSignatureEventContent = async (
  raw: string
): Promise<CreateSignatureEventContent> => {
  try {
    const createSignatureEventContent =
      await parseJson<CreateSignatureEventContent>(raw)
    return createSignatureEventContent
  } catch (error) {
    throw new MetaParseError(
      MetaParseErrorType.PARSE_ERROR_SIGNATURE_EVENT_CONTENT,
      {
        cause: handleError(error),
        context: raw
      }
    )
  }
}

/**
 * Extracts only necessary metadata for the card display
 * @param meta Sigit metadata
 * @returns SigitCardDisplayInfo
 */
export const extractSigitCardDisplayInfo = async (meta: Meta) => {
  if (!meta?.createSignature) return

  const sigitInfo: SigitCardDisplayInfo = {
    signers: [],
    fileExtensions: [],
    signedStatus: SigitStatus.Partial,
    isValid: false
  }

  try {
    const createSignatureEvent = await parseNostrEvent(meta.createSignature)

    sigitInfo.isValid = verifyEvent(createSignatureEvent)

    // created_at in nostr events are stored in seconds
    sigitInfo.createdAt = fromUnixTimestamp(createSignatureEvent.created_at)

    const createSignatureContent = await parseCreateSignatureEventContent(
      createSignatureEvent.content
    )

    const files = Object.keys(createSignatureContent.fileHashes)
    const { extensions } = extractFileExtensions(files)

    const signedBy = Object.keys(meta.docSignatures) as `npub1${string}`[]
    const isCompletelySigned = createSignatureContent.signers.every((signer) =>
      signedBy.includes(signer)
    )

    sigitInfo.title = createSignatureContent.title
    sigitInfo.submittedBy = createSignatureEvent.pubkey as `npub1${string}`
    sigitInfo.signers = createSignatureContent.signers
    sigitInfo.fileExtensions = extensions

    if (isCompletelySigned) {
      sigitInfo.signedStatus = SigitStatus.Complete
    }

    return sigitInfo
  } catch (error) {
    if (error instanceof MetaParseError) {
      toast.error(error.message)
      console.error(error.name, error.message, error.cause, error.context)
    } else {
      console.error('Unexpected error', error)
    }
  }
}
