// dtp-site-app.js
// Copyright (C) 2022 DTP Technologies, LLC
// License: Apache-2.0

'use strict';

const DTP_COMPONENT = { name: 'Site Admin', slug: 'site-admin-app' };
const dtp = window.dtp = window.dtp || { };

const GRID_COLOR = '#a0a0a0';
const GRID_TICK_COLOR = '#707070';

const AXIS_TICK_COLOR = '#c0c0c0';

const CHART_LINE_USER = 'rgb(0, 192, 0)';
const CHART_LINE_NICE = 'rgb(160, 160, 160)';
const CHART_LINE_SYSTEM = 'rgb(192, 192, 0)';
const CHART_LINE_IRQ = 'rgb(0, 0, 192)';

const CHART_LINE_TX_SEC = 'rgb(240, 185, 6)';
const CHART_LINE_RX_SEC = 'rgb(6, 154, 240)';

import DtpApp from 'dtp/dtp-app.js';
import numeral from 'numeral';
import UIkit from 'uikit';
// import UIkit from 'uikit';

export default class DtpSiteAdminHostStatsApp extends DtpApp {

  constructor (user) {
    super(DTP_COMPONENT, user);
    this.log.debug('constructor', 'app instance created');
  }

  prepareGraphData ( ) {
    this.charts = { cpus: [ ], interfaces: [ ] };

    this.coreGraphs = document.querySelectorAll('.dtp-cpu-graph');
    this.coreCount = dtp.hostStats[0].cpus.length;
    this.cores = [ ];
    for (let idx = 0; idx < this.coreCount; ++idx) {
      this.cores.push([ ]);
    }

    this.ifaceGraphs = document.querySelectorAll('.dtp-iface-graph');
    this.ifaceCount = this.ifaceGraphs.length;
    this.ifaces = { };
    dtp.hostStats[0].network.forEach((iface) => {
      this.ifaces[iface.iface] = [ ];
    });

    dtp.hostStats.forEach((stats) => {
      for (let idx = 0; idx < this.coreCount; ++idx) {
        const stat = stats.cpus[idx];
        stat.totalTime = stat.user + stat.nice + stat.sys + stat.idle + stat.irq;
        this.cores[idx].push({
          created: stats.created,
          ...stats.cpus[idx],
        });
      }

      stats.network.forEach((iface) => {
        iface.created = stats.created;
        this.ifaces[iface.iface].push(iface);
      });
    });
  }

  renderCpuGraphs ( ) {
    for (let idx = 0; idx < this.coreCount; ++idx) {
      const ctx = this.coreGraphs[idx].getContext('2d');
      const datasets = [
        {
          label: 'user',
          data: this.cores[idx].map((sample) => ((sample.user / sample.totalTime) * 100.0)),
          borderColor: CHART_LINE_USER,
          tension: 0.5,
        },
        {
          label: 'nice',
          data: this.cores[idx].map((sample) => ((sample.nice / sample.totalTime) * 100.0)),
          borderColor: CHART_LINE_NICE,
          tension: 0.5,
        },
        {
          label: 'sys',
          data: this.cores[idx].map((sample) => ((sample.sys / sample.totalTime) * 100.0)),
          borderColor: CHART_LINE_SYSTEM,
          tension: 0.5,
        },
        {
          label: 'irq',
          data: this.cores[idx].map((sample) => ((sample.irq / sample.totalTime) * 100.0)),
          borderColor: CHART_LINE_IRQ,
          tension: 0.5,
        },
      ];

      const chart = new Chart(ctx, {
        type: 'line',
        data: {
          labels: this.cores[idx].map((sample) => sample.created),
          datasets,
        },
        options: {
          scales: {
            yAxis: {
              display: true,
              max: 100.0,
              ticks: {
                color: AXIS_TICK_COLOR,
              },
              grid: {
                color: GRID_COLOR,
                tickColor: GRID_TICK_COLOR,
              },
            },
            xAxis: {
              display: false,
            },
          },
          plugins: {
            title: { display: false },
            subtitle: { display: false },
            legend: {
              display: true,
              position: 'bottom',
            },
          },
        },
      });
      this.charts.cpus.push(chart);
    }
  }

  renderNetworkGraphs ( ) {
    const ifNames = Object.keys(this.ifaces);

    ifNames.forEach((ifName) => {
      const iface = this.ifaces[ifName];
      const canvas = document.querySelector(`.dtp-iface-graph[data-iface="${ifName}"]`);
      if (!canvas) {
        return;
      }
      const ctx = canvas.getContext('2d');

      const datasets = [
        {
          label: 'TX/sec',
          data: iface.map((sample) => sample.txPerSecond),
          borderColor: CHART_LINE_TX_SEC,
          tension: 0.5,
        },
        {
          label: 'RX/sec',
          data: iface.map((sample) => sample.rxPerSecond),
          borderColor: CHART_LINE_RX_SEC,
          tension: 0.5,
        },
      ];

      const chart = new Chart(ctx, {
        type: 'line',
        data: {
          labels: iface.map((sample) => sample.created),
          datasets,
        },
        options: {
          scales: {
            yAxis: {
              display: true,
              ticks: {
                color: AXIS_TICK_COLOR,
                callback: (value) => {
                  let label = 'Mbps';
                  let megabits = value * (8 / 1024.0 / 1000.0);
                  if (megabits > 1000) {
                    label = 'Gbps';
                    megabits /= 1000.0;
                  }
                  return `${numeral(megabits).format('0,0.00')} ${label}`;
                },
              },
              grid: {
                color: GRID_COLOR,
                tickColor: GRID_TICK_COLOR,
              },
            },
            xAxis: { display: false },
          },
          plugins: {
            title: { display: false },
            subtitle: { display: false },
            legend: {
              display: true,
              position: 'bottom',
            },
          },
        },
      });
      this.charts.interfaces.push(chart);
    });
  }

  async jobQueueAction (event) {
    event.preventDefault();
    event.stopPropagation();

    const target = event.currentTarget || event.target;
    const queueName = target.getAttribute('data-job-queue');
    const jobId = target.getAttribute('data-job-id');
    const action = target.getAttribute('data-job-action');

    try {
      this.log.info('queueJobRemove', 'removing job from queue', { queueName, jobId });

      const response = await fetch(`/admin/job-queue/${queueName}/${jobId}/action`, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
        },
        body: JSON.stringify({ action }),
      });
      if (!response.ok) {
        throw new Error('Server error');
      }

      const json = await response.json();
      if (!json.success) {
        throw new Error(json.message);
      }

      if (['remove','discard'].includes(action)) {
        window.location = `/admin/job-queue/${queueName}`;
      }
    } catch (error) {
      UIkit.modal.alert(`Failed to remove job: ${error.message}`);
    }
  }

  async sendNewsletter (event) {
    const newsletterId = event.currentTarget.getAttribute('data-newsletter-id');
    const newsletterTitle = event.currentTarget.getAttribute('data-newsletter-title');
    console.log(newsletterId, newsletterTitle);
    try {
      await UIkit.modal.confirm(`Are you sure you want to transmit "${newsletterTitle}"`);
    } catch (error) {
      this.log.info('sendNewsletter', 'aborted');
      return;
    }
    try {
      const response = await fetch(`/admin/newsletter/${newsletterId}/transmit`, {
        method: 'POST',
      });
      if (!response.ok) {
        throw new Error('Server error');
      }
      await this.processResponse(response);      
    } catch (error) {
      this.log.error('sendNewsletter', 'failed to send newsletter', { newsletterId, newsletterTitle, error });
      UIkit.modal.alert(`Failed to send newsletter: ${error.message}`);
    }
  }

  async deleteNewsletter (event) {
    const newsletterId = event.currentTarget.getAttribute('data-newsletter-id');
    const newsletterTitle = event.currentTarget.getAttribute('data-newsletter-title');
    console.log(newsletterId, newsletterTitle);
    try {
      await UIkit.modal.confirm(`Are you sure you want to delete "${newsletterTitle}"`);
    } catch (error) {
      this.log.info('deleteNewsletter', 'aborted');
      return;
    }
    try {
      const response = await fetch(`/admin/newsletter/${newsletterId}`, {
        method: 'DELETE',
      });
      if (!response.ok) {
        throw new Error('Failed to delete newsletter');
      }
      await this.processResponse(response);      
    } catch (error) {
      this.log.error('deleteNewsletter', 'failed to delete newsletter', { newsletterId, newsletterTitle, error });
      UIkit.modal.alert(`Failed to delete newsletter: ${error.message}`);
    }
  }

  async deleteAnnouncement (event) {
    const target = event.currentTarget || event.target;
    const announcementId = target.getAttribute('data-announcement-id');
    try {
      await UIkit.modal.confirm('Are you sure you want to delete the announcement?');
    } catch (error) {
      return;
    }
    try {
      const actionUrl = `/admin/announcement/${announcementId}`;
      const response = await fetch(actionUrl, { method: 'DELETE' });
      if (!response.ok) {
        throw new Error('Server error');
      }
      await this.processResponse(response);
    } catch (error) {
      UIkit.modal.alert(`Failed to delete announcement: ${error.message}`);
    }
  }

  async postCoreConnectResponse (event, action) {
    const target = event.currentTarget || event.target;
    const requestId = target.getAttribute('data-request-id');
    try {
      this.log.info('postCoreConnectResponse', 'posting Core Connect response', { requestId, action });
      const requestUrl = `/admin/service-node/connect-queue/${requestId}`;
      const response = await fetch(requestUrl, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
        },
        body: JSON.stringify({ action }),
      });
      await this.processResponse(response);
    } catch (error) {
      UIkit.modal.alert(`Failed to send Core Connect response: ${error.message}`);
    }
  }

  async deleteServiceNode (event) {
    const target = event.currentTarget || event.target;
    const serviceNodeId = target.getAttribute('data-service-node-id');
    try {
      await UIkit.modal.confirm('Are you sure you want to delete the Service Node?');
    } catch (error) {
      return; // user cancel
    }
    try {
      const actionUrl = `/admin/service-node/${serviceNodeId}`;
      const response = await fetch(actionUrl, { method: 'DELETE' });
      if (!response.ok) {
        throw new Error('Server error');
      }
      await this.processResponse(response);
    } catch (error) {
      UIkit.modal.alert(`Failed to delete Service Node: ${error.message}`);
    }
  }

  async resolveNewsroomFeed (event) {
    event.preventDefault();
    event.stopPropagation();

    const form = document.querySelector('form#add-feed-form');
    const input = form.querySelector('input#url');
    const feedUrl = input.value;
    try {
      const response = await fetch(`/admin/newsroom/resolve`, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
        },
        body: JSON.stringify({ feedUrl }),
      });
      await this.processResponse(response);
    } catch (error) {
      UIkit.modal.alert(`Failed to resolve feed: ${error.message}`);
    }

    return false;
  }

  async removeNewsroomFeed (event) {
    event.preventDefault();
    event.stopPropagation();

    const target = event.currentTarget || event.target;
    const feedId = target.getAttribute('data-feed-id');
    const feedTitle = target.getAttribute('data-feed-title');

    try {
      await UIkit.modal.confirm(`Are you sure you want to remove feed "${feedTitle}"?`);
    } catch (error) {
      // canceled
      return false;
    }

    try {
      const response = await fetch(`/admin/newsroom/${feedId}`, { method: 'DELETE' });
      await this.processResponse(response);
    } catch (error) {
      UIkit.modal.alert(`Failed to remove feed: ${error.message}`);
    }

    return false;
  }

  async deletePost (event) {
    event.preventDefault();
    event.stopPropagation();

    const target = event.currentTarget || event.target;
    const postId = target.getAttribute('data-post-id');
    const postTitle = target.getAttribute('data-post-title');

    try {
      await UIkit.modal.confirm(`Are you sure you want to remove post "${postTitle}"?`);
    } catch (error) {
      // canceled
      return false;
    }

    try {
      const response = await fetch(`/admin/post/${postId}`, { method: 'DELETE' });
      await this.processResponse(response);
    } catch (error) {
      UIkit.modal.alert(`Failed to remove post: ${error.message}`);
    }

    return false;
  }

  async deletePage (event) {
    event.preventDefault();
    event.stopPropagation();

    const target = event.currentTarget || event.target;
    const pageId = target.getAttribute('data-page-id');
    const pageTitle = target.getAttribute('data-page-title');

    try {
      await UIkit.modal.confirm(`Are you sure you want to remove page "${pageTitle}"?`);
    } catch (error) {
      // canceled
      return false;
    }

    try {
      const response = await fetch(`/admin/page/${pageId}`, { method: 'DELETE' });
      await this.processResponse(response);
    } catch (error) {
      UIkit.modal.alert(`Failed to remove page: ${error.message}`);
    }

    return false;
  }

  async deleteSiteLink (event) {
    event.preventDefault();
    event.stopPropagation();

    const target = event.currentTarget || event.target;
    const link = JSON.parse(target.getAttribute('data-link'));

    try {
      await UIkit.modal.confirm(`Are you sure you want to remove site link "${link.label}"?`);
    } catch (error) {
      // canceled
      return false;
    }

    try {
      const response = await fetch(`/admin/site-link/${link._id}`, { method: 'DELETE' });
      await this.processResponse(response);
    } catch (error) {
      UIkit.modal.alert(`Failed to remove site link: ${error.message}`);
    }

    return false;
  }

  async deleteVenueChannel (event) {
    event.preventDefault();
    event.stopPropagation();

    const target = event.currentTarget || event.target;
    const channel = JSON.parse(target.getAttribute('data-channel'));

    try {
      await UIkit.modal.confirm(`Are you sure you want to remove channel "${channel.name}"?`);
    } catch (error) {
      // canceled
      return false;
    }

    try {
      const response = await fetch(`/admin/venue/channel/${channel._id}`, { method: 'DELETE' });
      await this.processResponse(response);
    } catch (error) {
      UIkit.modal.alert(`Failed to remove site link: ${error.message}`);
    }

    return false;
  }

  async submitImageForm (event) {
    event.preventDefault();
    event.stopPropagation();

    const formElement = event.currentTarget || event.target;
    const form = new FormData(formElement);

    this.cropper.getCroppedCanvas().toBlob(async (imageData) => {
      try {
        form.append('imageFile', imageData, 'icon.png');
  
        this.log.info('submitImageForm', 'updating site image', { event, action: formElement.action });
        const response = await fetch(formElement.action, {
          method: formElement.method,
          body: form,
        });
  
        if (!response.ok) {
          let json;
          try {
            json = await response.json();
          } catch (error) {
            throw new Error('Server error');
          }
          throw new Error(json.message || 'Server error');
        }
  
        await this.processResponse(response);
        window.location.reload();
      } catch (error) {
        UIkit.modal.alert(`Failed to update site image: ${error.message}`);
      }
    });

    return;
  }
}

dtp.DtpSiteAdminHostStatsApp = DtpSiteAdminHostStatsApp;