import {
  UnsignedEvent,
  EventTemplate,
  nip19,
  nip44,
  finalizeEvent,
  nip04
} from 'nostr-tools'
import { SignedEvent } from '../../types'
import store from '../../store/store'
import { LoginMethod } from '../../store/auth/types'
import { LoginMethodStrategy } from './loginMethodStrategy'
import { verifySignedEvent } from '../../utils/nostr'

/**
 * Login Method Strategy when using dev private key login.
 *
 * This class extends {@link LoginMethodStrategy base strategy} and implements all login method operations
 * @see {@link LoginMethodStrategy}
 */
export class PrivateKeyStrategy extends LoginMethodStrategy {
  async nip04Encrypt(receiver: string, content: string): Promise<string> {
    const keys = store.getState().auth.keyPair

    if (!keys) {
      throw new Error(
        `Login method is ${LoginMethod.privateKey} but private & public key pair is not found.`
      )
    }

    const { private: nsec } = keys
    const privateKey = nip19.decode(nsec).data as Uint8Array

    const encrypted = await nip04.encrypt(privateKey, receiver, content)
    return encrypted
  }

  async nip04Decrypt(sender: string, content: string): Promise<string> {
    const keys = store.getState().auth.keyPair

    if (!keys) {
      throw new Error(
        `Login method is ${LoginMethod.privateKey} but private & public key pair is not found.`
      )
    }

    const { private: nsec } = keys
    const privateKey = nip19.decode(nsec).data as Uint8Array

    const decrypted = await nip04.decrypt(privateKey, sender, content)
    return decrypted
  }

  async nip44Encrypt(receiver: string, content: string): Promise<string> {
    const keys = store.getState().auth.keyPair

    // Check if the private and public key pair is available.
    if (!keys) {
      throw new Error(
        `Login method is ${LoginMethod.privateKey} but private & public key pair is not found.`
      )
    }

    // Decode the private key.
    const { private: nsec } = keys
    const privateKey = nip19.decode(nsec).data as Uint8Array

    // Generate the conversation key using NIP-44 utilities.
    const nip44ConversationKey = nip44.v2.utils.getConversationKey(
      privateKey,
      receiver
    )

    // Encrypt the content using the generated conversation key.
    const encrypted = nip44.v2.encrypt(content, nip44ConversationKey)

    return encrypted
  }

  async nip44Decrypt(sender: string, content: string): Promise<string> {
    const keys = store.getState().auth.keyPair

    // Check if the private and public key pair is available.
    if (!keys) {
      throw new Error(
        `Login method is ${LoginMethod.privateKey} but private & public key pair is not found.`
      )
    }

    // Decode the private key.
    const { private: nsec } = keys
    const privateKey = nip19.decode(nsec).data as Uint8Array

    // Generate the conversation key using NIP-44 utilities.
    const nip44ConversationKey = nip44.v2.utils.getConversationKey(
      privateKey,
      sender
    )

    // Decrypt the content using the generated conversation key.
    const decrypted = nip44.v2.decrypt(content, nip44ConversationKey)

    return decrypted
  }

  async signEvent(event: UnsignedEvent | EventTemplate): Promise<SignedEvent> {
    const keys = store.getState().auth.keyPair

    if (!keys) {
      return Promise.reject(
        `Login method is ${LoginMethod.privateKey}, but keys are not found`
      )
    }

    const { private: nsec } = keys
    const privateKey = nip19.decode(nsec).data as Uint8Array

    const signedEvent = finalizeEvent(event, privateKey)

    verifySignedEvent(signedEvent)

    return Promise.resolve(signedEvent)
  }
}
