<!--
  @component Main cluster status view component; renders current system-usage information

  Properties:
  - `presetCluster String`: The cluster to show status information for
  - `presetSubCluster String?`: The subCluster to show status information for [Default: null]
  - `useCbColors Bool?`: Use colorblind friendly colors [Default: false]
  - `useAltColors Bool?`: Use alternative color set [Default: false]
-->

<script>
  import {
    Row,
    Col,
    Spinner,
    Card,
    Table,
    Icon,
    Tooltip,
    Input,
    InputGroup,
    InputGroupText,
  } from "@sveltestrap/sveltestrap";
  import { queryStore, gql, getContextClient } from "@urql/svelte";
  import {
    scramble,
    scrambleNames,
    convert2uplot,
  } from "../../generic/utils.js";
  import Pie, { colors } from "../../generic/plots/Pie.svelte";
  import Histogram from "../../generic/plots/Histogram.svelte";
  import Refresher from "../../generic/helper/Refresher.svelte";

  /* Svelte 5 Props */
  let {
    presetCluster,
    presetSubCluster = null,
    useCbColors = false,
    useAltColors = false,
    loadMe = false,
  } = $props();

  /* Const Init */
  const client = getContextClient();
  const durationBinOptions = ["1m", "10m", "1h", "6h", "12h"];

  /* State Init */
  let selectedHistograms = $state([]); // Dummy For Refresh
  let colWidthJobs = $state(0);
  let colWidthNodes = $state(0);
  let colWidthAccs = $state(0);
  let numDurationBins = $state("1h");

  /* Derived */
  const canvasPrefix = $derived(
    `${presetCluster}-${presetSubCluster ? presetSubCluster : ""}`,
  );

  const statusFilter = $derived(
    presetSubCluster
      ? [
          { cluster: { eq: presetCluster } },
          { subCluster: { eq: presetSubCluster } },
          { state: ["running"] },
        ]
      : [{ cluster: { eq: presetCluster } }, { state: ["running"] }],
  );
  const topStatsQuery = $derived(
    loadMe
      ? queryStore({
          client: client,
          query: gql`
            query ($filter: [JobFilter!]!) {
              allUsers: jobsStatistics(
                filter: $filter
                groupBy: USER
              ) {
                id
                name
                totalJobs
                totalNodes
                totalAccs
              }
              allProjects: jobsStatistics(
                filter: $filter
                groupBy: PROJECT
              ) {
                id
                totalJobs
                totalNodes
                totalAccs
              }
            }
          `,
          variables: {
            filter: statusFilter,
          },
          requestPolicy: "network-only",
        })
      : null,
  );

  // Sort + slice top-10 from the full results in the frontend
  const topUserJobs = $derived(
    $topStatsQuery?.data?.allUsers
      ?.toSorted((a, b) => b.totalJobs - a.totalJobs)
      .slice(0, 10) ?? [],
  );
  const topProjectJobs = $derived(
    $topStatsQuery?.data?.allProjects
      ?.toSorted((a, b) => b.totalJobs - a.totalJobs)
      .slice(0, 10) ?? [],
  );
  const topUserNodes = $derived(
    $topStatsQuery?.data?.allUsers
      ?.toSorted((a, b) => b.totalNodes - a.totalNodes)
      .slice(0, 10) ?? [],
  );
  const topProjectNodes = $derived(
    $topStatsQuery?.data?.allProjects
      ?.toSorted((a, b) => b.totalNodes - a.totalNodes)
      .slice(0, 10) ?? [],
  );
  const topUserAccs = $derived(
    $topStatsQuery?.data?.allUsers
      ?.toSorted((a, b) => b.totalAccs - a.totalAccs)
      .slice(0, 10) ?? [],
  );
  const topProjectAccs = $derived(
    $topStatsQuery?.data?.allProjects
      ?.toSorted((a, b) => b.totalAccs - a.totalAccs)
      .slice(0, 10) ?? [],
  );

  // Note: nodeMetrics are requested on configured $timestep resolution
  const nodeStatusQuery = $derived(
    loadMe
      ? queryStore({
          client: client,
          query: gql`
            query (
              $filter: [JobFilter!]!
              $selectedHistograms: [String!]
              $numDurationBins: String
            ) {
              jobsStatistics(
                filter: $filter
                metrics: $selectedHistograms
                numDurationBins: $numDurationBins
              ) {
                histDuration {
                  count
                  value
                }
                histNumNodes {
                  count
                  value
                }
                histNumAccs {
                  count
                  value
                }
              }
            }
          `,
          variables: {
            filter: statusFilter,
            selectedHistograms: selectedHistograms, // No Metrics requested for node hardware stats
            numDurationBins: numDurationBins,
          },
          requestPolicy: "network-only",
        })
      : null,
  );

  /* Functions */
  function legendColors(targetIdx) {
    // Reuses first color if targetIdx overflows
    let c;
    if (useCbColors) {
      c = [...colors["colorblind"]];
    } else if (useAltColors) {
      c = [...colors["alternative"]];
    } else {
      c = [...colors["default"]];
    }
    return c[(c.length + targetIdx) % c.length];
  }
</script>

<!-- Refresher and space for other options -->
<Row class="justify-content-between">
  <Col class="mb-2 mb-md-0" xs="12" md="5" lg="4" xl="3">
    <InputGroup>
      <InputGroupText>
        <Icon name="bar-chart-line-fill" />
      </InputGroupText>
      <InputGroupText>Duration Bin Size</InputGroupText>
      <Input type="select" bind:value={numDurationBins}>
        {#each durationBinOptions as dbin}
          <option value={dbin}>{dbin}</option>
        {/each}
      </Input>
    </InputGroup>
  </Col>
  <Col xs="12" md="5" lg="4" xl="3">
    <Refresher
      initially={120}
      onRefresh={() => {
        selectedHistograms = [...$state.snapshot(selectedHistograms)];
      }}
    />
  </Col>
</Row>

<hr />

<!-- Job Duration, Top Users and Projects-->
{#if $topStatsQuery?.fetching || $nodeStatusQuery?.fetching}
  <Spinner />
{:else if $topStatsQuery?.data && $nodeStatusQuery?.data}
  <Row>
    <Col xs="12" lg="4" class="p-2">
      {#key $nodeStatusQuery?.data?.jobsStatistics[0]?.histDuration}
        <Histogram
          data={convert2uplot(
            $nodeStatusQuery?.data?.jobsStatistics[0]?.histDuration,
          )}
          title="Duration Distribution"
          xlabel="Current Job Runtimes"
          xunit="Runtime"
          ylabel="Number of Jobs"
          yunit="Jobs"
          height="275"
          usesBins
          xtime
          enableFlip
        />
      {/key}
    </Col>
    <Col xs="6" md="3" lg="2" class="p-2">
      <div bind:clientWidth={colWidthJobs}>
        <h4 class="text-center">Top Users: Jobs</h4>
        <Pie
          {useAltColors}
          canvasId="{canvasPrefix}-hpcpie-jobs-users"
          size={colWidthJobs * 0.75}
          sliceLabel="Jobs"
          quantities={topUserJobs.map(
            (tu) => tu["totalJobs"],
          )}
          entities={topUserJobs.map((tu) =>
            scrambleNames ? scramble(tu.id) : tu.id,
          )}
        />
      </div>
    </Col>
    <Col xs="6" md="3" lg="2" class="p-2">
      <Table>
        <tr class="mb-2">
          <th></th>
          <th style="padding-left: 0.5rem;">User</th>
          <th>Jobs</th>
        </tr>
        {#each topUserJobs as tu, i}
          <tr>
            <td
              ><Icon name="circle-fill" style="color: {legendColors(i)};" /></td
            >
            <td id="{canvasPrefix}-topName-jobs-{tu.id}">
              <a
                target="_blank"
                href="/monitoring/user/{tu.id}?cluster={presetCluster}{presetSubCluster
                  ? '&partition=' + presetSubCluster
                  : ''}&state=running"
                >{scrambleNames ? scramble(tu.id) : tu.id}
              </a>
            </td>
            {#if tu?.name}
              <Tooltip
                target={`${canvasPrefix}-topName-jobs-${tu.id}`}
                placement="left"
                >{scrambleNames ? scramble(tu.name) : tu.name}</Tooltip
              >
            {/if}
            <td>{tu["totalJobs"]}</td>
          </tr>
        {/each}
      </Table>
    </Col>

    <Col xs="6" md="3" lg="2" class="p-2">
      <h4 class="text-center">Top Projects: Jobs</h4>
      <Pie
        {useAltColors}
        canvasId="{canvasPrefix}-hpcpie-jobs-projects"
        size={colWidthJobs * 0.75}
        sliceLabel={"Jobs"}
        quantities={topProjectJobs.map(
          (tp) => tp["totalJobs"],
        )}
        entities={topProjectJobs.map((tp) =>
          scrambleNames ? scramble(tp.id) : tp.id,
        )}
      />
    </Col>
    <Col xs="6" md="3" lg="2" class="p-2">
      <Table>
        <tr class="mb-2">
          <th></th>
          <th style="padding-left: 0.5rem;">Project</th>
          <th>Jobs</th>
        </tr>
        {#each topProjectJobs as tp, i}
          <tr>
            <td
              ><Icon name="circle-fill" style="color: {legendColors(i)};" /></td
            >
            <td>
              <a
                target="_blank"
                href="/monitoring/jobs/?cluster={presetCluster}{presetSubCluster
                  ? '&partition=' + presetSubCluster
                  : ''}&state=running&project={tp.id}&projectMatch=eq"
                >{scrambleNames ? scramble(tp.id) : tp.id}
              </a>
            </td>
            <td>{tp["totalJobs"]}</td>
          </tr>
        {/each}
      </Table>
    </Col>
  </Row>
{:else}
  <Card class="mx-4" body color="warning"
    >Cannot render job status charts: No data!</Card
  >
{/if}

<hr />

<!-- Node Distribution, Top Users and Projects-->
{#if $topStatsQuery?.fetching || $nodeStatusQuery?.fetching}
  <Spinner />
{:else if $topStatsQuery?.data && $nodeStatusQuery?.data}
  <Row>
    <Col xs="12" lg="4" class="p-2">
      <Histogram
        data={convert2uplot(
          $nodeStatusQuery?.data?.jobsStatistics[0]?.histNumNodes,
        )}
        title="Number of Nodes Distribution"
        xlabel="Allocated Nodes"
        xunit="Nodes"
        ylabel="Number of Jobs"
        yunit="Jobs"
        height="275"
        enableFlip
      />
    </Col>
    <Col xs="6" md="3" lg="2" class="p-2">
      <div bind:clientWidth={colWidthNodes}>
        <h4 class="text-center">Top Users: Nodes</h4>
        <Pie
          {useAltColors}
          canvasId="{canvasPrefix}-hpcpie-nodes-users"
          size={colWidthNodes * 0.75}
          sliceLabel="Nodes"
          quantities={topUserNodes.map(
            (tu) => tu["totalNodes"],
          )}
          entities={topUserNodes.map((tu) =>
            scrambleNames ? scramble(tu.id) : tu.id,
          )}
        />
      </div>
    </Col>
    <Col xs="6" md="3" lg="2" class="p-2">
      <Table>
        <tr class="mb-2">
          <th></th>
          <th style="padding-left: 0.5rem;">User</th>
          <th>Nodes</th>
        </tr>
        {#each topUserNodes as tu, i}
          <tr>
            <td
              ><Icon name="circle-fill" style="color: {legendColors(i)};" /></td
            >
            <td id="{canvasPrefix}-topName-nodes-{tu.id}">
              <a
                target="_blank"
                href="/monitoring/user/{tu.id}?cluster={presetCluster}{presetSubCluster
                  ? '&partition=' + presetSubCluster
                  : ''}&state=running"
                >{scrambleNames ? scramble(tu.id) : tu.id}
              </a>
            </td>
            {#if tu?.name}
              <Tooltip
                target={`${canvasPrefix}-topName-nodes-${tu.id}`}
                placement="left"
                >{scrambleNames ? scramble(tu.name) : tu.name}</Tooltip
              >
            {/if}
            <td>{tu["totalNodes"]}</td>
          </tr>
        {/each}
      </Table>
    </Col>

    <Col xs="6" md="3" lg="2" class="p-2">
      <h4 class="text-center">Top Projects: Nodes</h4>
      <Pie
        {useAltColors}
        canvasId="{canvasPrefix}-hpcpie-nodes-projects"
        size={colWidthNodes * 0.75}
        sliceLabel={"Nodes"}
        quantities={topProjectNodes.map(
          (tp) => tp["totalNodes"],
        )}
        entities={topProjectNodes.map((tp) =>
          scrambleNames ? scramble(tp.id) : tp.id,
        )}
      />
    </Col>
    <Col xs="6" md="3" lg="2" class="p-2">
      <Table>
        <tr class="mb-2">
          <th></th>
          <th style="padding-left: 0.5rem;">Project</th>
          <th>Nodes</th>
        </tr>
        {#each topProjectNodes as tp, i}
          <tr>
            <td
              ><Icon name="circle-fill" style="color: {legendColors(i)};" /></td
            >
            <td>
              <a
                target="_blank"
                href="/monitoring/jobs/?cluster={presetCluster}{presetSubCluster
                  ? '&partition=' + presetSubCluster
                  : ''}&state=running&project={tp.id}&projectMatch=eq"
                >{scrambleNames ? scramble(tp.id) : tp.id}
              </a>
            </td>
            <td>{tp["totalNodes"]}</td>
          </tr>
        {/each}
      </Table>
    </Col>
  </Row>
{:else}
  <Card class="mx-4" body color="warning"
    >Cannot render node status charts: No data!</Card
  >
{/if}

<hr />

<!-- Acc Distribution, Top Users and Projects-->
{#if $topStatsQuery?.fetching || $nodeStatusQuery?.fetching}
  <Spinner />
{:else if $topStatsQuery?.data && $nodeStatusQuery?.data}
  <Row>
    <Col xs="12" lg="4" class="p-2">
      <Histogram
        data={convert2uplot(
          $nodeStatusQuery?.data?.jobsStatistics[0]?.histNumAccs,
        )}
        title="Number of Accelerators Distribution"
        xlabel="Allocated Accs"
        xunit="Accs"
        ylabel="Number of Jobs"
        yunit="Jobs"
        height="275"
        enableFlip
      />
    </Col>
    <Col xs="6" md="3" lg="2" class="p-2">
      <div bind:clientWidth={colWidthAccs}>
        <h4 class="text-center">Top Users: GPUs</h4>
        <Pie
          {useAltColors}
          canvasId="{canvasPrefix}-hpcpie-accs-users"
          size={colWidthAccs * 0.75}
          sliceLabel="GPUs"
          quantities={topUserAccs.map(
            (tu) => tu["totalAccs"],
          )}
          entities={topUserAccs.map((tu) =>
            scrambleNames ? scramble(tu.id) : tu.id,
          )}
        />
      </div>
    </Col>
    <Col xs="6" md="3" lg="2" class="p-2">
      <Table>
        <tr class="mb-2">
          <th></th>
          <th style="padding-left: 0.5rem;">User</th>
          <th>GPUs</th>
        </tr>
        {#each topUserAccs as tu, i}
          <tr>
            <td
              ><Icon name="circle-fill" style="color: {legendColors(i)};" /></td
            >
            <td id="{canvasPrefix}-topName-accs-{tu.id}">
              <a
                target="_blank"
                href="/monitoring/user/{tu.id}?cluster={presetCluster}{presetSubCluster
                  ? '&partition=' + presetSubCluster
                  : ''}&state=running"
                >{scrambleNames ? scramble(tu.id) : tu.id}
              </a>
            </td>
            {#if tu?.name}
              <Tooltip
                target={`${canvasPrefix}-topName-accs-${tu.id}`}
                placement="left"
                >{scrambleNames ? scramble(tu.name) : tu.name}</Tooltip
              >
            {/if}
            <td>{tu["totalAccs"]}</td>
          </tr>
        {/each}
      </Table>
    </Col>

    <Col xs="6" md="3" lg="2" class="p-2">
      <h4 class="text-center">Top Projects: GPUs</h4>
      <Pie
        {useAltColors}
        canvasId="{canvasPrefix}-hpcpie-accs-projects"
        size={colWidthAccs * 0.75}
        sliceLabel={"GPUs"}
        quantities={topProjectAccs.map(
          (tp) => tp["totalAccs"],
        )}
        entities={topProjectAccs.map((tp) =>
          scrambleNames ? scramble(tp.id) : tp.id,
        )}
      />
    </Col>
    <Col xs="6" md="3" lg="2" class="p-2">
      <Table>
        <tr class="mb-2">
          <th></th>
          <th style="padding-left: 0.5rem;">Project</th>
          <th>GPUs</th>
        </tr>
        {#each topProjectAccs as tp, i}
          <tr>
            <td
              ><Icon name="circle-fill" style="color: {legendColors(i)};" /></td
            >
            <td>
              <a
                target="_blank"
                href="/monitoring/jobs/?cluster={presetCluster}{presetSubCluster
                  ? '&partition=' + presetSubCluster
                  : ''}&state=running&project={tp.id}&projectMatch=eq"
                >{scrambleNames ? scramble(tp.id) : tp.id}
              </a>
            </td>
            <td>{tp["totalAccs"]}</td>
          </tr>
        {/each}
      </Table>
    </Col>
  </Row>
{:else}
  <Card class="mx-4" body color="warning"
    >Cannot render accelerator status charts: No data!</Card
  >
{/if}
