import { Component, OnInit } from '@angular/core';
import { AppService } from 'src/app/shared/services/app.service';
import { IAppConfig } from 'src/app/shared/models/app-config.model';
import { OAuthService } from 'angular-oauth2-oidc';
import { AuthService } from 'src/app/shared/services/auth.service';
import { ChartConfiguration, ChartData, ChartOptions } from 'chart.js';
import { MAT_DIALOG_SCROLL_STRATEGY_PROVIDER_FACTORY } from '@angular/material/dialog';
import ChartDataLabels from 'chartjs-plugin-datalabels';
import { GeneralMetricsApiService } from '../../openApis/openSliceMetrics/services';
import { ResourceMetricsApiService } from '../../openApis/openSliceMetrics/services';
import { ServiceMetricsApiService } from '../../openApis/openSliceMetrics/services';
import { NfvMetricsApiService } from '../../openApis/nfvPortalMetrics/services';
import { ServiceOrderMetricsApiService } from '../../openApis/openSliceMetrics/services';
import {  ResourcesGroupByStateItem, ServiceOrdersGroupByDayItem, ServicesGroupByStateItem } from '../../openApis/openSliceMetrics/models';
import { asyncScheduler, forkJoin, scheduled } from 'rxjs';
import { catchError } from 'rxjs/internal/operators/catchError';
import * as moment from 'moment';

@Component({
  selector: 'app-landing',
  templateUrl: './metrics.component.html',
  styleUrls: ['./metrics.component.scss']
})
export class MetricsComponent implements OnInit {

  constructor(
    private appService: AppService,
    public authService: AuthService,
    private generalMetricsService: GeneralMetricsApiService,
    private serviceMetricsService: ServiceMetricsApiService,
    private resourceMetricsService: ResourceMetricsApiService,
    private serviceOrderMetricsService: ServiceOrderMetricsApiService,
    private nfvMetricsService: NfvMetricsApiService
  ) { }

  isLoading: boolean = true;
  config: IAppConfig
  loggedIn: boolean
  
  registeredUsers = 9999;
  activeServices = 9999;
  registeredNSDs = 9999;
  registeredVNFs = 9999;
  registeredMANO = 9999;
  registeredResourceSpecs = 9999;
  publishedServiceSpecs = 9999;
  availableResources = 9999;
  activeServiceOrders = 9999;
  publishedProductOfferings = 9999;
  totalCreatedServices = 9999;
  totalCompletedOrders = 9999;
  totalResources = 9999;
  serviceOrdersTotal15days = 9999;
  serviceOrdersTotalMonth = 9999;
  servicesTotalMonth = 9999;
  resourcesTotalMonth = 9999;

  serviceOrdersByState = [];
  servicesByState: ServicesGroupByStateItem[];
  serviceOrdersByDay: ServiceOrdersGroupByDayItem[];
  resourcesByState: ResourcesGroupByStateItem[];

  public doughnutChartPlugins = [ChartDataLabels];
  barChartLabels = [];
  barchartData: ChartData<'bar'> = {
    labels: this.barChartLabels,
    datasets: [{
      
      data: [],
      backgroundColor: '#428bca',
      borderColor: '#428bca',
      hoverBackgroundColor: '#235e91',
      hoverBorderColor: '#235e91',
      borderWidth: 1,
      }]
    };

  barChartOptions: ChartOptions = { 
    responsive: true,
    maintainAspectRatio: false,
    plugins: {
      datalabels: {
        display: false
      },
      title: {
        display: false,
        text: 'Service Orders Last 15 Days',
        font: {
          family: 'Roboto',
          weight: 'initial'
        }
      },
      legend: {
        display: false
      }
    },
    scales: {
      x: {
        grid: { display: false }
      },
      y: {
        grid: { display: false },
        ticks: {
          callback: function (value) {
            const num = Number(value);
            return Number.isInteger(num) && num >= 0 ? num : '';
          },
          stepSize: 1,
        },
        beginAtZero: true
      }
    }
  }

  resourcesLabels = ['Reserved', 'Available', 'Suspended'];
  resourcesData: ChartData<'doughnut'> = {
    labels: this.resourcesLabels,
    datasets: [
      { 
        data: [],
        backgroundColor: ['#428bca', '#42ca74', '#c5ca42' ],
        hoverBackgroundColor: ['#235e91', '#24a34a', '#8d911f'],
        hoverBorderColor: 'white'
      }
    ],
  };

  serviceOrdersLabels = ['Initial', 'Acknowledged', 'In Progress',  'Completed', 'Failed'];
  serviceOrdersData: ChartData<'doughnut'> = {
    labels: this.serviceOrdersLabels,
    datasets: [
      { 
        data: [],
        backgroundColor: ['#428bca', '#42ca74', '#c5ca42', '#ca42c1', '#d91424'],
        hoverBackgroundColor: ['#235e91', '#24a34a', '#8d911f', '#871880', '#941822'],
        hoverBorderColor: 'white',
      }
    ],
  };

  servicesLabels = ['Reserved', 'Inactive', 'Active', 'Terminated'];
  servicesData: ChartData<'doughnut'> = {
    labels: this.servicesLabels,
    
    datasets: [
      {
        data: [],
        backgroundColor: ['#428bca', '#c5ca42', '#42ca74', '#d91424'],
        hoverBackgroundColor:  ['#235e91', '#8d911f', '#24a34a', '#941822'],
        hoverBorderColor: 'white'
      }
    ]
  };

  ngOnInit() {
    this.config = this.appService.config
    this.authService.isAuthenticated$.subscribe(
      isAuthenticated => this.loggedIn = isAuthenticated
    )
    
    const endtime = moment().toISOString();
    const starttime = moment().subtract(14, 'days').toISOString();
    const starttimeMonth = moment().subtract(29, 'days').toISOString();
    // const starttime = moment().subtract(13, 'days').startOf('day').toISOString();
    // const starttimeMonth = moment().subtract(29, 'days').startOf('day').toISOString();


    forkJoin({
      registeredMANO: this.nfvMetricsService.getRegisteredManoProviders().pipe(
        catchError(error => {
          console.error('Failed to load registered MANO providers', error);
          return scheduled([{ registeredManoProviders: this.registeredMANO }], asyncScheduler);
        })
      ),
      registeredNSDs: this.nfvMetricsService.getRegisteredNsds().pipe(
        catchError(error => {
          console.error('Failed to load registered NSDs', error);
          return scheduled([{ registeredNSDs: this.registeredNSDs }], asyncScheduler);
        })
      ),
      registeredVNFs: this.nfvMetricsService.getRegisteredVnfs().pipe(
        catchError(error => {
          console.error('Failed to load registered VNFs', error);
          return scheduled([{ registeredVNFs: this.registeredVNFs }], asyncScheduler);
        })
      ),
      registeredUsers: this.generalMetricsService.getRegisteredIndividuals().pipe(
        catchError(error => {
          console.error('Failed to load registered users', error);
          return scheduled([{ registeredIndividuals: this.registeredUsers }], asyncScheduler);
        })
      ),
      publishedServiceSpecs: this.generalMetricsService.getPublishedServiceSpecifications().pipe(
        catchError(error => {
          console.error('Failed to load published specs', error);
          return scheduled([{ publishedServiceSpecifications: this.publishedServiceSpecs }], asyncScheduler);
        })
      ),
      registeredResourceSpecs: this.generalMetricsService.getRegisteredResourceSpecifications().pipe(
        catchError(error => {
          console.error('Failed to load resource specs', error);
          return scheduled([{ registeredResourceSpecifications: this.registeredResourceSpecs }], asyncScheduler);
        })
      ),
      totalCreatedServices: this.serviceMetricsService.getTotalServices().pipe(
        catchError(error => {
          console.error('Failed to load total services', error);
          return scheduled([{ totalServices: this.totalCreatedServices }], asyncScheduler);
        })
      ),
      activeServices: this.serviceMetricsService.getTotalServices({ state: 'ACTIVE' }).pipe(
        catchError(error => {
          console.error('Failed to load active services', error);
          return scheduled([{ totalServices: this.activeServices }], asyncScheduler);
        })
      ),
      totalResources: this.resourceMetricsService.getTotalResources().pipe(
        catchError(error => {
          console.error('Failed to load total resources', error);
          return scheduled([{ totalResources: this.totalResources }], asyncScheduler);
        })
      ),
      availableResources: this.resourceMetricsService.getTotalResources({ state: 'AVAILABLE' }).pipe(
        catchError(error => {
          console.error('Failed to load available resources', error);
          return scheduled([{ totalResources: this.availableResources }], asyncScheduler);
        })
      ),
      activeServiceOrders: this.serviceOrderMetricsService.getTotalActiveServiceOrders().pipe(
        catchError(error => {
          console.error('Failed to load active service orders', error);
          return scheduled([{ activeServiceOrders: this.activeServiceOrders }], asyncScheduler);
        })
      ),
      totalCompletedOrders: this.serviceOrderMetricsService.getTotalServiceOrders({ state: 'COMPLETED' }).pipe(
        catchError(error => {
          console.error('Failed to load completed service orders', error);
          return scheduled([{ totalServiceOrders: this.totalCompletedOrders }], asyncScheduler);
        })
      ),
      serviceOrdersTotal15days: this.serviceOrderMetricsService.getServiceOrdersGroupedByDay({ starttime, endtime }).pipe(
        catchError(error => {
          console.error('Failed to load service orders by day', error);
          return scheduled([{ serviceOrders:{ total: this.serviceOrdersTotal15days } }], asyncScheduler);
        })
      ),
      serviceOrdersGroupedByDay: this.serviceOrderMetricsService.getServiceOrdersGroupedByDay({ starttime, endtime }).pipe(
        catchError(error => {
          console.error('Failed to load service orders by day', error);
          const mockGroupByDay = this.barChartLabels.map(label => {
            const [day, month] = label.split('/').map(Number);
            const isoDate = new Date(Date.UTC(new Date().getFullYear(), month - 1, day)).toISOString();
            return {
              key: isoDate,
              count: 0
            };
          });
          return scheduled([{ 
            serviceOrders: { 
              aggregations: { 
                groupByDay: mockGroupByDay 
              } 
            } 
          }], asyncScheduler);
        })
      ),
      serviceOrdersTotalMonth: this.serviceOrderMetricsService.getServiceOrdersGroupedByState({ starttime: starttimeMonth, endtime }).pipe(
        catchError(error => {
          console.error('Failed to load service orders by state', error);
          return scheduled([{ serviceOrders:{ total: this.serviceOrdersTotalMonth } }], asyncScheduler);
        })
      ),
      serviceOrdersGroupedByState: this.serviceOrderMetricsService.getServiceOrdersGroupedByState({ starttime: starttimeMonth, endtime }).pipe(
        catchError(error => {
          console.error('Failed to load service orders by state', error);
          return scheduled([{ 
            serviceOrders: { 
              aggregations: { 
                groupByState: this.serviceOrdersLabels.map(label => ({
                  key: label.replace(/\s+/g, '').toUpperCase(),
                  count: 0
                }))
              } 
            } 
          }], asyncScheduler);
        })
      ),
      resourcesTotalMonth: this.resourceMetricsService.getResourcesGroupedByState({ starttime: starttimeMonth, endtime }).pipe(
        catchError(error => {
          console.error('Failed to load resources by state', error);
          return scheduled([{ resources:{ total: this.resourcesTotalMonth } }], asyncScheduler);
        })
      ),
      resourcesGroupedByState: this.resourceMetricsService.getResourcesGroupedByState({ starttime: starttimeMonth, endtime }).pipe(
        catchError(error => {
          console.error('Failed to load resources by state', error);
          return scheduled([{ 
            resources: { 
              aggregations: { 
                groupByState: this.resourcesLabels.map(label => ({
                  key: label.toUpperCase(),
                  count: 0
                })) 
              } 
            } 
          }], asyncScheduler);
        })
      ),
      servicesTotalMonth: this.serviceMetricsService.getServicesGroupedByState({ starttime: starttimeMonth, endtime }).pipe(
        catchError(error => {
          console.error('Failed to load services by state', error);
          return scheduled([{ services:{ total: this.servicesTotalMonth } }], asyncScheduler);
        })
      ),
      servicesGroupedByState: this.serviceMetricsService.getServicesGroupedByState({ starttime: starttimeMonth, endtime }).pipe(
        catchError(error => {
          console.error('Failed to load services by state', error);
          return scheduled([{ 
            services: { 
              aggregations: { 
                groupByState: [] 
              } 
            } 
          }], asyncScheduler);
        })
      )
    }).subscribe(results => {
      this.registeredUsers = results.registeredUsers.registeredIndividuals;
      this.publishedServiceSpecs = results.publishedServiceSpecs.publishedServiceSpecifications;
      this.registeredResourceSpecs = results.registeredResourceSpecs.registeredResourceSpecifications;
      this.totalCreatedServices = results.totalCreatedServices.totalServices;
      this.activeServices = results.activeServices.totalServices;
      this.totalResources = results.totalResources.totalResources;
      this.availableResources = results.availableResources.totalResources;
      this.activeServiceOrders = results.activeServiceOrders.activeServiceOrders;
      this.totalCompletedOrders = results.totalCompletedOrders.totalServiceOrders;
      this.registeredMANO = results.registeredMANO.registeredManoProviders;
      this.registeredNSDs = results.registeredNSDs.registeredNSDs;
      this.registeredVNFs = results.registeredVNFs.registeredVNFs;
      
      this.serviceOrdersTotal15days = results.serviceOrdersTotal15days.serviceOrders.total;
      this.serviceOrdersTotalMonth = results.serviceOrdersTotalMonth.serviceOrders.total;
      this.servicesTotalMonth = results.servicesTotalMonth.services.total;
      this.resourcesTotalMonth = results.resourcesTotalMonth.resources.total;
      

      this.serviceOrdersByDay = results.serviceOrdersGroupedByDay.serviceOrders.aggregations.groupByDay;
      this.barchartData.labels = this.serviceOrdersByDay.map(data =>
        new Date(data.key).toLocaleDateString('en-GB', { day: 'numeric', month: 'numeric' })
      );
      this.barchartData.datasets[0].data = this.serviceOrdersByDay.map(data => data.count);

      const chartLabelsOrders = this.serviceOrdersLabels.map(val => val.replace(/\s+/g, '').toUpperCase());
      this.serviceOrdersByState = results.serviceOrdersGroupedByState.serviceOrders.aggregations.groupByState
        .filter(item => chartLabelsOrders.includes(item.key));
      this.serviceOrdersData.datasets[0].data = this.serviceOrdersByState.map(data => data.count);

      const chartLabelsResources = this.resourcesLabels.map(val => val.toUpperCase());
      this.resourcesByState = results.resourcesGroupedByState.resources.aggregations.groupByState
        .filter(item => chartLabelsResources.includes(item.key));
      this.resourcesData.datasets[0].data = this.resourcesByState.map(data => data.count);

      const chartLabelsServices = this.servicesLabels.map(val => val.toUpperCase());
      this.servicesByState = results.servicesGroupedByState.services.aggregations.groupByState
        .filter(item => chartLabelsServices.includes(item.key));
      this.servicesData.datasets[0].data = this.servicesByState.map(data => data.count);
      this.isLoading = false;
    });
  }
  
  
  login() {
    this.authService.login()
  }
  
  
  chartOptions: ChartConfiguration<'doughnut'>['options'] = {
    responsive: true,
    maintainAspectRatio: false,
    cutout: '50%',
    plugins: {
      datalabels: {
        color: 'white',
        anchor: 'center',
        align: 'center',
        formatter: function(value: number, context: any) {
          let dataset = context.chart.data.datasets[0].data;
          let total = dataset.reduce((acc: number, val: number) => acc + val, 0);
          let finalVal = ((value/total)*100).toFixed(0);
          if(Number(finalVal)<=5) return '';
          return finalVal+'%';
        },
        font: (context: any) => {
          let chartHeight = context.chart.height;
          let chartWidth = context.chart.width;
          let minSize = Math.min(chartHeight, chartWidth);
          let size = Math.round(minSize/12);
          return {
            weight: 'bold',
            size: size
          }
        }
      },
      legend: { 
        display: false,
        labels: {
          color: 'white'
        },
        position: 'right'
      },
      title: {
        display: false,
        color: 'white',
        align: 'end',
        position: 'right'
      },
    },
  };
}
