import { defineStore } from "pinia"
import { useFetch } from '@/hooks/fetch'
import { useDomainsStore } from "./domains"

import { Status, Result, Loading, Resolution, Analysis } from '@/types/api/system'

interface OperationState<T> {
  status: Status
  results: Result<T>
}

interface SystemState {
  loading: OperationState<Loading>
  resolution: OperationState<Resolution>
  analysis: OperationState<Analysis>
}
  
function createDefault(operation: string) {
  return {
    status: {
      [operation]: false
    },
    results: {
      last_result: null
    }
  }
}

function getStatus(status?: Status): boolean {
  return status ? ( Object.values(status)[0] ?? false ) : false
}

function setStatus(target: Status, value: Status) {
  const key = Object.keys(target)[0]
  target[key] = getStatus(value)
}

async function operationRequest(op: string, action = 'start'): Promise<Status> {
  const { data, error, execute } = useFetch(`/${op}/${action}`).json<Status>()
  await execute()
  if (error.value) {
    console.error(error.value)
    return { fallback: false }
  } else {
    return data.value ?? { fallback: false }
  }
}

async function operationResult<T>(op: string): Promise<Result<T>> {
  const { data, error, execute } = useFetch(`/${op}/result`).json<Result<T>>()
  await execute()
  if (error.value) {
    console.error(error.value)
    return { last_result: null }
  } else {
    return data.value ?? { last_result: null }
  }
}

function updateDomains() {
  console.log('Updating domain list from system store after successful operation')  
  const ds = useDomainsStore()
  ds.fetchDomains()
}

export const useSystemStore = defineStore({
  id: 'system',
  state: (): SystemState => ({
    loading: createDefault('loading'),
    resolution: createDefault('resolution'),
    analysis: createDefault('analysis')
  }),
  getters: {
    operationsStatus(state) {
      const ops = new Map<string, boolean>()
      if (state.loading.status.loading) ops.set('loading', true)
      if (state.resolution.status.resolution) ops.set('resolution', true)
      if (state.analysis.status.analysis) ops.set('analysis', true)
      return ops
    },
    anyRunning(): boolean {
      return Array.from(this.operationsStatus.values()).some(v => v)
    },
    statusText() {
      const ops = this.operationsStatus
      if (!ops) return 'Status unknown'
      if (ops.size > 1) return 'Multiple operations running...'
      if (ops.get('loading')) return 'DRS is pulling data from your SIEM...'
      if (ops.get('resolution')) return 'Reslovers are gathering additional data...'
      if (ops.get('analysis')) return 'DRS is analyzing domains...'
      return 'Idle'
    }
  },
  actions: {
    // START
    async load() {
      setStatus(this.loading.status, await operationRequest('loading'))
    },
    async resolve() {
      setStatus(this.resolution.status, await operationRequest('resolution'))
    },
    async analyze() {
      setStatus(this.analysis.status, await operationRequest('analysis'))
    },
    // STATUS
    async updateLoading() {
      setStatus(this.loading.status, await operationRequest('loading', 'status'))
      if (!getStatus(this.loading.status)) {
        await this.loadingResult()
      }
    },
    async updateResolution() {
      setStatus(this.resolution.status, await operationRequest('resolution', 'status'))
      if (!getStatus(this.resolution.status)) {
        await this.resolutionResult()
      }
    },
    async updateAnalysis() {
      setStatus(this.analysis.status, await operationRequest('analysis', 'status'))
      if (!getStatus(this.analysis.status)) {
        await this.analysisResult()
      }
    },
    async updateRunning() {
      const ops = this.operationsStatus
      if (!ops) return
      const updates = []
      if (ops.get('loading')) updates.push(this.updateLoading())
      if (ops.get('resolution')) updates.push(this.updateResolution())
      if (ops.get('analysis')) updates.push(this.updateAnalysis())
      await Promise.all(updates)
    },
    async updateAll() {
      await Promise.all([
        this.updateLoading(),
        this.updateResolution(),
        this.updateAnalysis()
      ])
    },
    // RESULTS
    async loadingResult() {
      this.loading.results = await operationResult('loading')
      updateDomains()
    },
    async resolutionResult() {
      this.resolution.results = await operationResult('resolution')
      updateDomains()
    },
    async analysisResult() {
      this.analysis.results = await operationResult('analysis')
      updateDomains()
    },
    async allResults() {
      await Promise.all([
        this.loadingResult(),
        this.resolutionResult(),
        this.analysisResult()
      ])
    }
  }
})
