/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import React from 'react';
import moment from 'moment';
import { Table, Tabs, Tooltip } from 'antd';
import { TablePaginationConfig } from 'antd/es/table';
import { InfoCircleOutlined } from '@ant-design/icons';
import prettyMilliseconds from 'pretty-ms';

import { IAxiosResponse } from '@/types/axios.types';
import AutoReloadPanel from '@/components/autoReloadPanel/autoReloadPanel';
import { showDataFetchError } from '@/utils/common';
import { ColumnSearch } from '@/utils/columnSearch';
import { ReplicationIcon } from '@/utils/themeIcons';
import { AutoReloadHelper } from '@/utils/autoReloadHelper';
import { AxiosGetHelper, cancelRequests } from '@/utils/axiosRequestHelper';

import './pipelines.less';

const PipelineStatusList = ['OPEN', 'CLOSING', 'QUASI_CLOSED', 'CLOSED', 'UNHEALTHY', 'INVALID', 'DELETED', 'DORMANT'] as const;
type PipelineStatusTuple = typeof PipelineStatusList;
export type PipelineStatus = PipelineStatusTuple[number]; // 'OPEN' | 'CLOSING' | 'QUASI_CLOSED' | 'CLOSED' | 'UNHEALTHY' | 'INVALID' | 'DELETED';

interface IPipelineResponse {
  pipelineId: string;
  status: PipelineStatus;
  replicationType: string;
  leaderNode: string;
  datanodes: string[];
  lastLeaderElection: number;
  duration: number;
  leaderElections: number;
  replicationFactor: string;
  containers: number;
}

interface IPipelinesResponse {
  totalCount: number;
  pipelines: IPipelineResponse[];
}

interface IPipelinesState {
  activeLoading: boolean;
  activeDataSource: IPipelineResponse[];
  activeTotalCount: number;
  lastUpdated: number;
}

const COLUMNS = [
  {
    title: 'Pipeline ID',
    dataIndex: 'pipelineId',
    key: 'pipelineId',
    isSearchable: true,
    sorter: (a: IPipelineResponse, b: IPipelineResponse) => a.pipelineId.localeCompare(b.pipelineId),
    fixed: 'left'
  },
  {
    title: 'Replication Type & Factor',
    dataIndex: 'replicationType',
    key: 'replicationType',
    render: (replicationType: string, record: IPipelineResponse) => {
      const replicationFactor = record.replicationFactor;
      return (
        <span>
          <ReplicationIcon
            replicationFactor={replicationFactor}
            replicationType={replicationType}
            leaderNode={record.leaderNode}
            isLeader={false} />
          {replicationType} ({replicationFactor})
        </span>
      );
    },
    sorter: (a: IPipelineResponse, b: IPipelineResponse) =>
      (a.replicationType + a.replicationFactor.toString()).localeCompare(b.replicationType + b.replicationFactor.toString()),
    defaultSortOrder: 'descend' as const
  },
  {
    title: 'Status',
    dataIndex: 'status',
    key: 'status',
    filterMultiple: true,
    filters: PipelineStatusList.map(status => ({ text: status, value: status })),
    onFilter: (value: PipelineStatus, record: IPipelineResponse) => record.status === value,
    sorter: (a: IPipelineResponse, b: IPipelineResponse) => a.status.localeCompare(b.status)
  },
  {
    title: 'Containers',
    dataIndex: 'containers',
    key: 'containers',
    isSearchable: true,
    sorter: (a: IPipelineResponse, b: IPipelineResponse) => a.containers - b.containers
  },
  {
    title: 'Datanodes',
    dataIndex: 'datanodes',
    key: 'datanodes',
    isSearchable: true,
    render: (datanodes: string[]) => <div> {datanodes && datanodes.map(datanode =>
      <div key={datanode.hostName}>
        <div className='uuidtooltip'>
          <Tooltip placement='top' title={`UUID: ${datanode?.uuid ?? 'NA'}`} getPopupContainer={(triggerNode) => triggerNode}>
            {datanode?.hostName ?? 'N/A'}
          </Tooltip>
        </div>
      </div>)}
    </div>
  },
  {
    title: 'Leader',
    dataIndex: 'leaderNode',
    key: 'leaderNode',
    isSearchable: true,
    sorter: (a: IPipelineResponse, b: IPipelineResponse) => a.leaderNode.localeCompare(b.leaderNode)
  },
  {
    title:
      <span>
        Last Leader Election&nbsp;
        <Tooltip title='Elapsed time since the current leader got elected. Only available if any metrics service providers like Prometheus is configured.'>
          <InfoCircleOutlined />
        </Tooltip>
      </span>,
    dataIndex: 'lastLeaderElection',
    key: 'lastLeaderElection',
    render: (lastLeaderElection: number) => lastLeaderElection > 0 ?
      prettyMilliseconds(lastLeaderElection, { compact: true }) + ' ago' : 'NA',
    sorter: (a: IPipelineResponse, b: IPipelineResponse) => a.lastLeaderElection - b.lastLeaderElection
  },
  {
    title: 'Lifetime',
    dataIndex: 'duration',
    key: 'duration',
    render: (duration: number) => prettyMilliseconds(duration, { compact: true }),
    sorter: (a: IPipelineResponse, b: IPipelineResponse) => a.duration - b.duration
  },
  {
    title:
      <span>
        No. of Elections&nbsp;
        <Tooltip title='Number of elections in this pipeline. Only available if any metrics service providers like Prometheus is configured.'>
          <InfoCircleOutlined />
        </Tooltip>
      </span>,
    dataIndex: 'leaderElections',
    key: 'leaderElections',
    isSearchable: true,
    render: (leaderElections: number) => leaderElections > 0 ?
      leaderElections : 'NA',
    sorter: (a: IPipelineResponse, b: IPipelineResponse) => a.leaderElections - b.leaderElections
  }
];

let cancelPipelineSignal: AbortController;

export class Pipelines extends React.Component<Record<string, object>, IPipelinesState> {
  autoReload: AutoReloadHelper;

  constructor(props = {}) {
    super(props);
    this.state = {
      activeLoading: false,
      activeDataSource: [],
      activeTotalCount: 0,
      lastUpdated: 0
    };
    this.autoReload = new AutoReloadHelper(this._loadData);
  }

  _loadData = () => {
    this.setState({
      activeLoading: true
    });
    const { request, controller } = AxiosGetHelper('/api/v1/pipelines', cancelPipelineSignal);
    cancelPipelineSignal = controller;

    request.then((response: IAxiosResponse<IPipelinesResponse>) => {
      const pipelinesResponse: IPipelinesResponse = response.data;
      const totalCount = pipelinesResponse.totalCount;
      const pipelines: IPipelineResponse[] = pipelinesResponse.pipelines;
      this.setState({
        activeLoading: false,
        activeDataSource: pipelines,
        activeTotalCount: totalCount,
        lastUpdated: Number(moment())
      });
    }).catch(error => {
      this.setState({
        activeLoading: false
      });
      showDataFetchError(error);
    });
  };

  componentDidMount(): void {
    // Fetch pipelines on component mount
    this._loadData();
    this.autoReload.startPolling();
  }

  componentWillUnmount(): void {
    this.autoReload.stopPolling();
    cancelRequests([
      cancelPipelineSignal
    ])
  }

  onShowSizeChange = (current: number, pageSize: number) => {
    console.log(current, pageSize);
  };

  onTabChange = (activeKey: string) => {
    // Fetch inactive pipelines if tab is switched to "Inactive"
    if (activeKey === '2') {
      // Fetch inactive pipelines in the future
    }
  };

  render() {
    const { activeDataSource, activeLoading, activeTotalCount, lastUpdated } = this.state;
    const paginationConfig: TablePaginationConfig = {
      showTotal: (total: number, range) => `${range[0]}-${range[1]} of ${total} pipelines`,
      showSizeChanger: true,
      onShowSizeChange: this.onShowSizeChange
    };
    return (
      <div className='pipelines-container'>
        <div className='page-header'>
          Pipelines ({activeTotalCount})
          <AutoReloadPanel isLoading={activeLoading} lastRefreshed={lastUpdated} togglePolling={this.autoReload.handleAutoReloadToggle} onReload={this._loadData} />
        </div>
        <div className='content-div'>
          <Tabs defaultActiveKey='1' onChange={this.onTabChange}>
            <Tabs.TabPane key='1'>
              <Table
                dataSource={activeDataSource}
                columns={COLUMNS.reduce<any[]>((filtered, column) => {
                  if (column.isSearchable) {
                    const newColumn = {
                      ...column,
                      ...new ColumnSearch(column).getColumnSearchProps(column.dataIndex)
                    };
                    filtered.push(newColumn);
                  } else {
                    filtered.push(column);
                  }

                  return filtered;
                }, [])}
                loading={activeLoading} pagination={paginationConfig} rowKey='pipelineId'
                scroll={{ x: 'max-content', scrollToFirstRowOnChange: true }}
                locale={{ filterTitle: '' }}
              />
            </Tabs.TabPane>
          </Tabs>
        </div>
      </div>
    );
  }
}
