<script setup>

import { useI18n } from 'vue-i18n';
const i18n = useI18n();
const t = i18n.t;

import { ref, onMounted, useTemplateRef, nextTick, onUnmounted } from 'vue';
import AppsModel from '../../models/AppsModel.js';
import { prettyDecimalSize } from '@cloudron/pankow/utils';
import SystemModel from '../../models/SystemModel.js';
import { SingleSelect } from '@cloudron/pankow';
import GraphItem from '../GraphItem.vue';

const { app } = defineProps([ 'app' ]);

const appsModel = AppsModel.create();
const systemModel = SystemModel.create();

const periods = [
  { hours: 0,                        label: t('app.graphs.period.live'), tickFormat: 'hh:mm A', tooltipFormat: 'hh:mm:ss A' },
  { hours: 1,     intervalSecs:  20, label: t('app.graphs.period.1h'),   tickFormat: 'hh:mm A', tooltipFormat: 'hh:mm:ss A' }, // 180 points
  { hours: 6,     intervalSecs:  60, label: t('app.graphs.period.6h'),   tickFormat: 'hh:mm A', tooltipFormat: 'hh:mm A' },
  { hours: 12,    intervalSecs: 240, label: t('app.graphs.period.12h'),  tickFormat: 'hh:mm A', tooltipFormat: 'hh:mm A' },
  { hours: 24,    intervalSecs: 480, label: t('app.graphs.period.24h'),  tickFormat: 'hh:mm A', tooltipFormat: 'hh:mm A' },
  { hours: 24*7,  intervalSecs: 960, label: t('app.graphs.period.7d'),   tickFormat: 'DD MMM',  tooltipFormat: 'DD MMM hh:mm A' },
  { hours: 24*30, intervalSecs: 960, label: t('app.graphs.period.30d'),  tickFormat: 'DD MMM',  tooltipFormat: 'DD MMM hh:mm A' }, // 2700 points
];

const busy = ref(true);
const period = ref(periods[0]);
const cpuGraphItem = useTemplateRef('cpuGraphItem');
const memoryGraphItem = useTemplateRef('memoryGraphItem');
const diskGraphItem = useTemplateRef('diskGraphItem');
const networkGraphItem = useTemplateRef('networkGraphItem');

const networkReadTotal = ref(0);
const networkWriteTotal = ref(0);

const blockReadTotal = ref(0);
const blockWriteTotal = ref(0);

const appMemory = app.memoryLimit || app.manifest.memoryLimit || 256*1024*1024;
let systemCpus = {};
let metricStream = null;

function createDatasets() {
  const datasets = {
      cpu: [{ label: 'CPU', color: '#9ad0f5', stack: 'cpu', data: [] }],
      memory: [{ label: 'Memory', color: '#9ad0f5', stack: 'memory', data: [] }],
      disk: [
        { label: 'Read', color: '#9ad0f5', stack: 'read', data: [] },
        { label: 'Write', color: '#ffb1c1', stack: 'write', data: [] }
      ],
      network: [
        { label: 'RX', color: '#9ad0f5', stack: 'rx', data: [] },
        { label: 'TX', color: '#ffb1c1', stack: 'tx', data: [] }
      ],
  };
  return datasets;
}

async function liveRefresh() {
  metricStream = await appsModel.getMetricStream(app.id);
  metricStream.onerror = (error) => console.log('event stream error:', error);
  metricStream.onmessage = (message) => {
    const data = JSON.parse(message.data);

    // stop here if the DOM elements are already gone due to view change
    if (!cpuGraphItem.value) return;

    const metrics = data[app.id]; // metric can be undefined if the container stopped midway

    cpuGraphItem.value.pushData(0, metrics?.cpu || 0);
    memoryGraphItem.value.pushData(0, metrics?.memory || 0);
    diskGraphItem.value.pushData(0, metrics?.blockReadRate || 0, metrics?.blockWriteRate || 0);
    networkGraphItem.value.pushData(0, metrics?.networkReadRate || 0, metrics?.networkWriteRate || 0);

    blockReadTotal.value = prettyDecimalSize(metrics?.blockReadTotal || 0);
    blockWriteTotal.value = prettyDecimalSize(metrics?.blockWriteTotal || 0);
    networkReadTotal.value = prettyDecimalSize(metrics?.networkReadTotal || 0);
    networkWriteTotal.value = prettyDecimalSize(metrics?.networkWriteTotal || 0);
  };
}

async function rebuild() {
  if (metricStream) {
    metricStream.close();
    metricStream = null;
  }

  const datasets = createDatasets();

  if (period.value.hours !== 0) {
    const [error, metrics] = await appsModel.getMetrics(app.id, { fromSecs: period.value.hours * 60 * 60, intervalSecs: period.value.intervalSecs });
    if (error) return console.error(error);

      datasets.cpu[0].data = metrics[app.id].cpu;
      datasets.memory[0].data = metrics[app.id].memory;
      datasets.disk[0].data = metrics[app.id].blockReadRate;
      datasets.disk[1].data = metrics[app.id].blockWriteRate;
      datasets.network[0].data = metrics[app.id].networkReadRate;
      datasets.network[1].data = metrics[app.id].networkWriteRate;

      networkReadTotal.value = prettyDecimalSize(metrics.networkReadTotal);
      networkWriteTotal.value = prettyDecimalSize(metrics.networkWriteTotal);
      blockReadTotal.value = prettyDecimalSize(metrics.blockReadTotal);
      blockWriteTotal.value = prettyDecimalSize(metrics.blockWriteTotal);
  }

  // stop here if the DOM elements are already gone due to view change
  if (!cpuGraphItem.value) return;

  cpuGraphItem.value.setDatasets(datasets.cpu);
  memoryGraphItem.value.setDatasets(datasets.memory);
  diskGraphItem.value.setDatasets(datasets.disk);
  networkGraphItem.value.setDatasets(datasets.network);

  if (period.value.hours === 0) return await liveRefresh();
}

onMounted(async () => {
  const [error, result] = await systemModel.cpus();
  if (error) return console.error(error);

  systemCpus = result;

  busy.value = false;
  await nextTick();

  await rebuild();
});

onUnmounted(async () => {
  if (metricStream) metricStream.close();
});

</script>

<template>
    <div class="graphs" v-if="!busy">
      <div style="text-align: right"><SingleSelect @select="rebuild()" v-model="period" :options="periods" option-label="label"/></div>

      <GraphItem ref="cpuGraphItem"
        :title="$t('system.cpuUsage.title')"
        :period="period"
        yscale="cpu"
        :cpu-count="systemCpus.length"
        :high-mark="systemCpus.length * app.cpuQuota/100"
        >
      </GraphItem>

      <GraphItem ref="memoryGraphItem"
        title="Memory"
        :period="period"
        yscale="memory"
        :memory="appMemory"
        :high-mark="appMemory"
        >
      </GraphItem>

      <GraphItem ref="diskGraphItem"
        title="Disk I/O"
        :footer="$t('app.graphs.diskIOTotal', { read: blockReadTotal, write: blockWriteTotal })"
        :period="period"
        yscale="disk"
        >
      </GraphItem>

      <GraphItem ref="networkGraphItem"
        title="Network I/O"
        :footer="$t('app.graphs.networkIOTotal', { inbound: networkReadTotal, outbound: networkWriteTotal })"
        :period="period"
        yscale="network"
        >
      </GraphItem>
    </div>
</template>
