import create, { State } from 'zustand'
import produce from 'immer'
import { PublicKey } from '@solana/web3.js'
import { notify } from '../utils/notifications'

export type AlertProvider = 'mail' | 'sms' | 'tg'

export interface Alert {
  acc: PublicKey
  alertProvider: AlertProvider
  collateralRatioThresh: number
  open: boolean
  timestamp: number
  triggeredTimestamp: number | undefined
}

interface AlertRequest {
  alertProvider: AlertProvider
  collateralRatioThresh: number
  mangoGroupPk: string
  marginAccountPk: string
  phoneNumber: any
  email: string | undefined
}

interface AlertsStore extends State {
  activeAlerts: Array<Alert>
  triggeredAlerts: Array<Alert>
  loading: boolean
  error: string
  submitting: boolean
  success: string
  tgCode: string | null
  set: (s: any) => void
  actions: { [key: string]: (any) => void }
}

const useAlertsStore = create<AlertsStore>((set, get) => ({
  activeAlerts: [],
  triggeredAlerts: [],
  loading: false,
  error: '',
  submitting: false,
  success: '',
  tgCode: null,
  set: (fn) => set(produce(fn)),
  actions: {
    async createAlert(req: AlertRequest) {
      const set = get().set
      const alert = {
        acc: new PublicKey(req.marginAccountPk),
        alertProvider: req.alertProvider,
        collateralRatioThresh: req.collateralRatioThresh,
        open: true,
        timestamp: Date.now(),
      }

      set((state) => {
        state.submitting = true
        state.error = ''
        state.success = ''
      })

      const fetchUrl = `https://mango-margin-call.herokuapp.com/alerts`
      const headers = { 'Content-Type': 'application/json' }

      fetch(fetchUrl, {
        method: 'POST',
        headers: headers,
        body: JSON.stringify(req),
      })
        .then((response: any) => {
          if (!response.ok) {
            throw response
          }
          return response.json()
        })
        .then((json: any) => {
          const alerts = get().activeAlerts

          set((state) => {
            state.activeAlerts = [alert as Alert].concat(alerts)
            state.success = json.code ? '' : 'Alert saved successfully'
            state.tgCode = json.code
          })
        })
        .catch((err) => {
          if (typeof err.text === 'function') {
            err.text().then((errorMessage: string) => {
              set((state) => {
                state.error = errorMessage
              })
              notify({
                message: errorMessage,
                type: 'error',
              })
            })
          } else {
            set((state) => {
              state.error = 'Something went wrong'
            })
            notify({
              message: 'Something went wrong',
              type: 'error',
            })
          }
        })
        .finally(() => {
          set((state) => {
            state.submitting = false
          })
        })
    },
    async loadAlerts(marginAccounts: PublicKey[]) {
      const set = get().set

      set((state) => {
        state.loading = true
      })

      const headers = { 'Content-Type': 'application/json' }
      const responses = await Promise.all(
        marginAccounts.map((pubkey) => {
          return fetch(
            `https://mango-margin-call.herokuapp.com/alerts/${pubkey}`,
            {
              method: 'GET',
              headers: headers,
            }
          )
            .then((response: any) => {
              if (!response.ok) {
                throw response
              }

              return response.json()
            })
            .catch((err) => {
              if (typeof err.text === 'function') {
                err.text().then((errorMessage: string) => {
                  notify({
                    message: errorMessage,
                    type: 'error',
                  })
                })
              } else {
                notify({
                  message: 'Something went wrong',
                  type: 'error',
                })
              }
            })
        })
      )

      // Add margin account address to alerts
      // Assign triggeredTimestamp for old alerts in the DB that don't have this property yet
      responses.forEach((accounts, index) =>
        accounts.alerts.forEach((alert) => {
          alert.acc = marginAccounts[index]
          if (!alert.open) {
            alert.triggeredTimestamp ||= alert.timestamp
          }
        })
      )

      const flattenAccountAlerts = responses.map((acc) => acc.alerts).flat()

      // sort active by latest creation time first
      const activeAlerts = flattenAccountAlerts
        .filter((alert) => alert.open)
        .sort((a, b) => {
          return b.timestamp - a.timestamp
        })

      // sort triggered by latest trigger time first
      const triggeredAlerts = flattenAccountAlerts
        .filter((alert) => !alert.open)
        .sort((a, b) => {
          return b.triggeredTimestamp - a.triggeredTimestamp
        })

      set((state) => {
        state.activeAlerts = activeAlerts
        state.triggeredAlerts = triggeredAlerts
        state.loading = false
      })
    },
  },
}))

export default useAlertsStore
