import { ReactComponent as Img } from '@/assets/not-integrated.svg';
import {
  CreditCardOutlined,
  DownOutlined,
  AppstoreOutlined,
  CloudOutlined,
  ProfileOutlined,
  FolderOpenOutlined,
  InfoCircleOutlined,
  CheckCircleFilled,
} from '@ant-design/icons';
import { PageContainer, ProCard } from '@ant-design/pro-components';
import {
  Tree,
  Tabs,
  Descriptions,
  Table,
  notification,
  Alert,
  Typography,
  Tooltip,
  Card,
  Tag,
  Badge,
} from 'antd';
import type { DirectoryTreeProps } from 'antd/es/tree';
import { useEffect, useState } from 'react';
import './services.less';
const yaml = require('js-yaml');

function traverse(data, treeData, expandedKeys) {
  if (data.kube_deployment) {
    treeData.title = data.kube_deployment.name;
  } else {
    treeData.title = data.name;
  }
  treeData.kind = data.kind;
  treeData.key = data.kind + '.' + data.name;
  if (treeData.kind === 'application') {
    treeData.icon = <AppstoreOutlined />;
    treeData.title += ` (${data.children.length})`;
    // treeData.selectable = false;
  } else if (treeData.kind === 'service') {
    treeData.icon = <CloudOutlined />;
    if (data.kube_deployment) {
      let status =
        data.kube_deployment.available_replicas <
        data.kube_deployment.desired_replicas
          ? 'error'
          : 'success';
      treeData.title = <Badge status={status} text={treeData.title} />;
    }
  } else if (treeData.kind === 'business') {
    treeData.icon = <ProfileOutlined />;
    treeData.selectable = false;
    expandedKeys.push(treeData.key);
  } else {
    treeData.icon = <FolderOpenOutlined />;
    treeData.selectable = false;
    expandedKeys.push(treeData.key);
  }

  treeData.children = [];
  if (data.children === null) {
    return treeData;
  }
  data.children.sort((a, b) => a.name.localeCompare(b.name));
  for (let child of data.children) {
    let node = {};
    treeData.children.push(traverse(child, node, expandedKeys));
  }
  return treeData;
}

function openRequestErrorNotification(url, statusText) {
  notification.error({
    message: 'Error',
    description: 'fetch ' + url + ' got ' + statusText,
  });
}

function openBackendNotification(error) {
  notification.error({
    message: 'Error',
    description: error,
  });
}

function getWeekday(numDay: number): string {
  switch (numDay) {
    case 1:
      return "Monday";
    case 2:
      return "Tuesday";
    case 3:
      return "Wednesday"
    case 4:
      return "Thursday";
    case 5:
      return "Friday";
    case 6:
      return "Saturday";
    case 7:
      return "Sunday";
    default:
      return "Unknown";
  }
}


function getReleaseWindow(result) {
  if (result.windowStartAt == null || result.windowEndAt == null) {
	return "NA"
  } 
  let startWeekday = getWeekday(result.windowStartAt.weekday)
  let endWeekday = getWeekday(result.windowEndAt.weekday)
  let releaseWindow = `${startWeekday} ${result.windowStartAt.hour}:${result.windowStartAt.minute} ~ ${endWeekday} ${result.windowEndAt.hour}:${result.windowEndAt.minute}`
  return releaseWindow
}

function renderApplicationTab(result, items) {
  let releaseWindow = getReleaseWindow(result)
  let owners = yaml.dump(result.owners);
  let slis = yaml.dump(result.slis);
  items.push({
    label: 'Application',
    key: 'applicaion',
    children: (
      <Descriptions column={1} bordered>
        <Descriptions.Item label='Owners' style={{whiteSpace: 'pre-wrap'}}>
          {owners}
        </Descriptions.Item>
        <Descriptions.Item label='CD Type'>
          {result.cdType}
        </Descriptions.Item>
        <Descriptions.Item label='Release Window'>
          {releaseWindow}
        </Descriptions.Item>
        <Descriptions.Item label='SLIs' style={{whiteSpace: 'pre-wrap'}}>
          <p>{slis}</p>
        </Descriptions.Item>
      </Descriptions>
    )
  })
}

function renderMetaTab(result, items) {
  let hasRPC = result.repo_rpc != null && result.repo_rpc.length > 0;

  let notIntegrated = (
    <Typography style={{ color: 'grey' }}>Not integrated yet</Typography>
  );

  items.push({
    label: 'Meta',
    key: 'meta',
    children: (
      <Descriptions column={1} bordered>
        <Descriptions.Item
          label={
            <Typography>
              Service{' '}
              <Tooltip
                title="The Kubenetes Deployment name in the Pulumi script"
                placement="right"
              >
                <InfoCircleOutlined style={{ fontSize: 11, color: 'grey' }} />
              </Tooltip>
            </Typography>
          }
        >
          {result.kube_deployment
            ? `${result.kube_deployment.name}`
            : `${result.service_name}`}
        </Descriptions.Item>
        <Descriptions.Item label="Business">
          {result.business}
        </Descriptions.Item>
        <Descriptions.Item label="Application">
          {result.application}
        </Descriptions.Item>
        <Descriptions.Item label="Ready Replicas">
          {result.kube_deployment ? (
            <a
              target={'_blank'}
              href={`https://clinic.pingcap.com/grafana/d/SzQVcRiVz/pod-overview?var-kube_deployment=${result.service_name}`}
            >
              <Badge
                status={
                  result.kube_deployment.available_replicas <
                  result.kube_deployment.desired_replicas
                    ? 'error'
                    : 'success'
                }
                text={
                  <a>{`[${result.kube_deployment.available_replicas}/${result.kube_deployment.desired_replicas}]`}</a>
                }
              />
            </a>
          ) : (
            <Typography></Typography>
          )}
        </Descriptions.Item>
        <Descriptions.Item
          label={
            <Typography>
              Kubernetes Service Name{' '}
              <Tooltip
                title="The Kubenetes Service exposed to API Gateway"
                placement="right"
              >
                <InfoCircleOutlined style={{ fontSize: 11, color: 'grey' }} />
              </Tooltip>
            </Typography>
          }
        >
          {result.kube_service == '' ? notIntegrated : result.kube_service}
        </Descriptions.Item>
        <Descriptions.Item label="Github Repository">
          <a target={'_blank'} href={result.repo}>
            {result.repo}
          </a>
        </Descriptions.Item>
        <Descriptions.Item label="Pulumi">
          <a
            target={'_blank'}
            href={`${result.repo}/tree/${result.branch}${result.pulumi_path}`}
          >
            {result.pulumi_path}
          </a>
        </Descriptions.Item>
        <Descriptions.Item
          label={
            <Typography>
              Trace{' '}
              <Tooltip
                title="Only for Service Integrated with Jaeger"
                placement="right"
              >
                <InfoCircleOutlined style={{ fontSize: 11, color: 'grey' }} />
              </Tooltip>
            </Typography>
          }
        >
          {result.jaeger_service == '' ? (
            notIntegrated
          ) : (
            <a
              target={'_blank'}
              href={`https://jaeger.oauth.prod.aws.tidbcloud.com/search?&service=${result.jaeger_service}`}
            >
              Jaeger
            </a>
          )}
        </Descriptions.Item>
        <Descriptions.Item label="Resource Monitoring">
          <a
            target={'_blank'}
            href={`https://clinic.pingcap.com/grafana/d/SzQVcRiVz/pod-overview?var-kube_deployment=${result.service_name}`}
          >
            Pod Overview
          </a>
        </Descriptions.Item>
        <Descriptions.Item
          label={
            <Typography>
              RPC Monitoring{' '}
              <Tooltip
                title="Only for Service associated with proto"
                placement="right"
              >
                <InfoCircleOutlined style={{ fontSize: 11, color: 'grey' }} />
              </Tooltip>
            </Typography>
          }
        >
          {!hasRPC ? (
            notIntegrated
          ) : (
            <a
              target={'_blank'}
              href={`https://clinic.pingcap.com/grafana/d/QdHZ0jMVk/tiinf-grpc?var-kube_deployment=${result.service_name}`}
            >
              TiInf gRPC
            </a>
          )}
        </Descriptions.Item>
        <Descriptions.Item
          label={
            <Typography>
              HTTP Monitoring{' '}
              <Tooltip
                title="Only for Service exposed to API Gateway"
                placement="right"
              >
                <InfoCircleOutlined style={{ fontSize: 11, color: 'grey' }} />
              </Tooltip>
            </Typography>
          }
        >
          {result.kube_service == '' ? (
            notIntegrated
          ) : (
            <a
              target={'_blank'}
              href={`https://clinic.pingcap.com/grafana/d/6abxfwgVk/api-gateway-kong?&var-kube_service=${result.kube_service}`}
            >
              API Gateway
            </a>
          )}
        </Descriptions.Item>
        <Descriptions.Item label="Service Logs">
          <a
            target={'_blank'}
            href={`https://clinic.pingcap.com/grafana/explore?orgId=1&left=%7B%22datasource%22:%22Loki-aws-us-west-2%22,%22queries%22:%5B%7B%22refId%22:%22A%22,%22editorMode%22:%22code%22,%22expr%22:%22%7Bcluster%3D%5C%22base%2Feks%2Fus-west-2%5C%22,%20namespace%3D%5C%22prod-ms%5C%22,%20app%3D%5C%22${result.service_name}%5C%22%7D%22,%22queryType%22:%22range%22%7D%5D,%22range%22:%7B%22from%22:%22now-1h%22,%22to%22:%22now%22%7D%7D`}
          >
            Loki
          </a>
        </Descriptions.Item>
        <Descriptions.Item
          label={
            <Typography>
              API Gateway Logs{' '}
              <Tooltip
                title="Only for Service exposed to API Gateway"
                placement="right"
              >
                <InfoCircleOutlined style={{ fontSize: 11, color: 'grey' }} />
              </Tooltip>
            </Typography>
          }
        >
          {result.kube_service == '' ? (
            notIntegrated
          ) : (
            <a
              target={'_black'}
              href={
                'https://clinic.pingcap.com/grafana/explore?orgId=1&left=%7B%22datasource%22:%22Loki-aws-us-west-2%22,%22queries%22:%5B%7B%22refId%22:%22A%22,%22editorMode%22:%22code%22,%22expr%22:%22%7Bcluster%3D%5C%22base%2Feks%2Fus-west-2%5C%22,%20namespace%3D%5C%22kong%5C%22,%20container%3D%5C%22proxy%5C%22%7D%20%7C%20json%20%7C%20__error__%3D%20%5C%22%5C%22%22,%22queryType%22:%22range%22%7D%5D,%22range%22:%7B%22from%22:%22now-1h%22,%22to%22:%22now%22%7D%7D'
              }
            >
              Loki(API Gateway)
            </a>
          )}
        </Descriptions.Item>
      </Descriptions>
    ),
  });
}

function renderRpcTab(result, items) {
  let hasRPC = result.repo_rpc != null && result.repo_rpc.length > 0;
  if (hasRPC) {
    let dataSource = [];
    let grpcMethods = [];
    let grpcServices = [];
    for (let repo of result.repo_rpc) {
      for (let rpc of repo.rpcs) {
        for (let rm of rpc.request_methods) {
          dataSource.push({
            method: rm.method,
            service: rpc.service,
            key: rpc.service + '.' + rm.method,
            max_qps: rm.max_qps,
            requests_count: rm.requests_count,
            state:
              rm.requests_count > 0 ? <Tag color="geekblue">online</Tag> : '-',
          });
          if (!grpcMethods.includes(rm.method)) {
            grpcMethods.push({
              text: rm.method,
              value: rm.method,
            });
          }
        }
        if (!grpcServices.includes(rpc.service)) {
          grpcServices.push({
            text: rpc.service,
            value: rpc.service,
          });
        }
      }
    }
    let columns = [
      {
        title: 'gRPC Method',
        dataIndex: 'method',
        filters: grpcMethods,
        filterSearch: true,
        onFilter: (value: string, record) => record.method == value,
      },
      {
        title: 'gRPC Service',
        dataIndex: 'service',
        filters: grpcServices,
        filterSearch: true,
        onFilter: (value: string, record) => record.service == value,
      },
      {
        title: 'State',
        dataIndex: 'state',
      },
      {
        title: (
          <Typography>
            Max QPS{' '}
            <Tooltip
              title="Last 24 hours, flush every 10 minutes"
              placement="right"
            >
              <InfoCircleOutlined style={{ fontSize: 11, color: 'grey' }} />
            </Tooltip>
          </Typography>
        ),
        dataIndex: 'max_qps',
        sorter: (a, b) => a.max_qps - b.max_qps,
      },
      {
        title: (
          <Typography>
            Requests Count{' '}
            <Tooltip
              title="Last 24 hours, flush every 10 minutes"
              placement="right"
            >
              <InfoCircleOutlined style={{ fontSize: 11, color: 'grey' }} />
            </Tooltip>
          </Typography>
        ),
        dataIndex: 'requests_count',
        defaultSortOrder: 'descend',
        sorter: (a, b) => a.requests_count - b.requests_count,
      },
    ];
    items.push({
      label: 'RPC',
      key: 'rpc',
      children: (
        <Table
          pagination={{ defaultPageSize: 20 }}
          columns={columns}
          dataSource={dataSource}
        />
      ),
    });
  } else {
    items.push({
      label: 'RPC',
      key: 'rpc',
      children: (
        <Card style={{ textAlign: 'center' }}>
          <Img />
          <Typography style={{ color: 'grey' }}>Not integrated yet</Typography>
        </Card>
      ),
    });
  }
}

function renderHttpTab(result, items) {
  let hasHTTP = result.repo_http != null && result.repo_http.length > 0;
  if (hasHTTP) {
    let dataSource = [];
    for (let repo of result.repo_http) {
      for (let http of repo.apis) {
        dataSource.push({
          path: http.path,
          method: http.method,
          key: http.method + '.' + http.path,
          max_qps: http.max_qps,
          requests_count: http.requests_count,
          state:
            http.requests_count > 0 ? <Tag color="geekblue">online</Tag> : '-',
        });
      }
    }

    dataSource.sort((a, b) => b.requests_count - a.requests_count);
    let deduplicated = [];
    let paths = [];
    // use two pointer to handle row span.
    let l = null;
    let r = null;
    let t = 1;
    for (let item of dataSource) {
      let rowSpan = {};
      if (
        r &&
        r.requests_count === item.requests_count &&
        item.requests_count > 0
      ) {
        rowSpan = { rowSpan: 0 };
        t += 1;
      } else {
        if (l) {
          l.rowSpan = { rowSpan: t };
        }
        l = item;
        t = 1;
      }
      item.rowSpan = rowSpan;
      r = item;
      if (!deduplicated.includes(item.path)) {
        deduplicated.push(item.path);
        paths.push({
          text: item.path,
          value: item.requests_count > 0 ? item.requests_count : item.path,
        });
      }
    }
    // corner case, all items have the same requests count.
    if (dataSource[0].requests_count == r.requests_count) {
      dataSource[0].rowSpan = { rowSpan: t };
    }

    let columns = [
      {
        title: (
          <Typography>
            Path{' '}
            <Tooltip
              title="The paths are grouped by regular expression of API Gateway"
              placement="right"
            >
              <InfoCircleOutlined style={{ fontSize: 11, color: 'grey' }} />
            </Tooltip>
          </Typography>
        ),
        dataIndex: 'path',
        filters: paths,
        filterSearch: true,
        onFilter: (value: string, record) => {
          if (value.startsWith('/')) {
            return record.path == value;
          } else {
            return record.requests_count == value;
          }
        },
      },
      {
        title: 'HTTP Method',
        dataIndex: 'method',
      },
      {
        title: 'State',
        dataIndex: 'state',
        onCell: (record, index) => {
          return record.rowSpan;
        },
      },
      {
        title: (
          <Typography>
            Max QPS{' '}
            <Tooltip
              title="Last 24 hours, flush every 10 minutes"
              placement="right"
            >
              <InfoCircleOutlined style={{ fontSize: 11, color: 'grey' }} />
            </Tooltip>
          </Typography>
        ),
        dataIndex: 'max_qps',
        onCell: (record, index) => {
          return record.rowSpan;
        },
      },
      {
        title: (
          <Typography>
            Requests Count{' '}
            <Tooltip
              title="Last 24 hours, flush every 10 minutes"
              placement="right"
            >
              <InfoCircleOutlined style={{ fontSize: 11, color: 'grey' }} />
            </Tooltip>
          </Typography>
        ),
        dataIndex: 'requests_count',
        onCell: (record, index) => {
          return record.rowSpan;
        },
      },
    ];
    items.push({
      label: 'HTTP',
      key: 'http',
      children: (
        <Table
          pagination={{ defaultPageSize: 20 }}
          columns={columns}
          dataSource={dataSource}
        />
      ),
    });
  } else {
    items.push({
      label: 'HTTP',
      key: 'http',
      children: (
        <Card style={{ textAlign: 'center' }}>
          <Img />
          <Typography style={{ color: 'grey' }}>Not integrated yet</Typography>
        </Card>
      ),
    });
  }
}

export default () => {
  const [treeData, setTreeData] = useState([]);
  const [tabItems, setTabItems] = useState([]);
  const [expandedKeys, setExpandedKeys] = useState([]);
  const [selectedKeys, setSelectedKeys] = useState([]);

  const renderDetails = (keys) => {
    if (!keys[0]) {
      return;
    }
    fetch('/api/services/' + keys[0].replace('service.', ''))
      .then((res) => {
        if (res.status != 200) {
          res.json().then(
            (result) => {
              openBackendNotification(result.error);
            },
            (error) => {
              openRequestErrorNotification(res.url, res.statusText);
            },
          );
          throw new Error('Request Error');
        }
        return res.json();
      })
      .then(
        (result) => {
          let items = [];
          renderMetaTab(result, items);
          renderRpcTab(result, items);
          renderHttpTab(result, items);
          setTabItems(items);
          setSelectedKeys(keys);
          localStorage.setItem('lastSelectedKeys', JSON.stringify(keys));
        },
        (error) => {
          console.log(error);
        },
      );
  };

  const renderApplication = (keys) => {
    if (!keys[0]) {
      return;
    }
    fetch('/api/applications/' + keys[0].replace('application.', ''))
      .then((res) => {
        if (res.status != 200) {
          res.json().then(
            (result) => {
              openBackendNotification(result.error);
            },
            (error) => {
              openRequestErrorNotification(res.url, res.statusText);
            },
          );
          throw new Error('Request Error');
        }
        return res.json();
      })
      .then(
        (result) => {
          let items = [];
          renderApplicationTab(result, items);
          setTabItems(items);
        },
        (error) => {
          console.log(error);
        },
      );
  };

  const onSelect: DirectoryTreeProps['onSelect'] = (keys, info) => {
    if (info.node.kind === 'service') {
      renderDetails(keys);
    } else if (info.node.kind === 'application') {
      renderApplication(keys);
    }
  };

  const onExpand: DirectoryTreeProps['onExpand'] = (keys, info) => {
    setExpandedKeys(keys);
    localStorage.setItem('lastExpandedKeys', JSON.stringify(keys));
  };

  useEffect(() => {
    fetch('/api/services')
      .then((res) => {
        if (res.status != 200) {
          res.json().then(
            (result) => {
              openBackendNotification(result.error);
            },
            (error) => {
              openRequestErrorNotification(res.url, res.statusText);
            },
          );
          throw new Error('Request Error');
        }
        return res.json();
      })
      .then(
        (result) => {
          let treeData = {};
          let defaultExpandedKeys = [];
          treeData = traverse(result, treeData, defaultExpandedKeys);
          // Expand first application to show meta data of first Service
          defaultExpandedKeys.push(treeData.children[0].children[0].key);
          // Use first service as the default Service
          let firstService = treeData.children[0].children[0].children[0].key;

          let expandedKeys = defaultExpandedKeys;
          let selectedKeys = [firstService];
          let eKeys = localStorage.getItem('lastExpandedKeys');
          let sKeys = localStorage.getItem('lastSelectedKeys');
          if (eKeys && sKeys) {
            expandedKeys = JSON.parse(eKeys);
            selectedKeys = JSON.parse(sKeys);
          }

          setTreeData([treeData]);
          setExpandedKeys(expandedKeys);
          setSelectedKeys(selectedKeys);
          renderDetails(selectedKeys);
        },
        (error) => {
          console.log(error);
        },
      );
  }, []);

  return (
    <PageContainer>
      <ProCard ghost gutter={8}>
        <ProCard
          headerBordered
          style={{ minHeight: '90vh', height: '100px', overflowY: 'scroll' }}
          colSpan={8}
          bordered
          split="horizontal"
        >
          <ProCard>
            <Typography style={{ fontWeight: 600 }}>
              Service Tree{' '}
              <Tooltip
                title='Service Tree organize services under "Business-Application-Service" hierarchy.'
                placement="right"
              >
                <InfoCircleOutlined style={{ fontSize: 11, color: 'grey' }} />
              </Tooltip>
            </Typography>
          </ProCard>
          <ProCard>
            <Tree
              showIcon
              onSelect={onSelect}
              onExpand={onExpand}
              treeData={treeData}
              expandedKeys={expandedKeys}
              selectedKeys={selectedKeys}
            />
          </ProCard>
        </ProCard>
        <ProCard
          style={{ minHeight: '90vh', height: '100px', overflowY: 'scroll' }}
          colSpan={16}
          bordered
        >
          <Tabs defaultActiveKey="meta" items={tabItems} />
        </ProCard>
      </ProCard>
    </PageContainer>
  );
};
