import React, { useEffect, useRef, useState } from "react";
import { connect } from "react-redux";
import { RouteComponentProps } from "react-router-dom";
import {
	GlobalTime,
	serviceMapStore,
	getServiceMapItems,
	getDetailedServiceMapItems,
} from "Src/store/actions";
import { Spin } from "antd";
import styled from "styled-components";
import { StoreState } from "../../store/reducers";
import { getGraphData } from "./utils";
import SelectService from "./SelectService";
import { ForceGraph2D } from "react-force-graph";

const Container = styled.div`
	.force-graph-container .graph-tooltip {
		background: black;
		padding: 1px;
		.keyval {
			display: flex;
			.key {
				margin-right: 4px;
			}
			.val {
				margin-left: auto;
			}
		}
	}
`;

interface ServiceMapProps extends RouteComponentProps<any> {
	serviceMap: serviceMapStore;
	globalTime: GlobalTime;
	getServiceMapItems: Function;
	getDetailedServiceMapItems: Function;
}
interface graphNode {
	id: string;
	group: number;
}
interface graphLink {
	source: string;
	target: string;
	value: number;
}
export interface graphDataType {
	nodes: graphNode[];
	links: graphLink[];
}

const ServiceMap = (props: ServiceMapProps) => {
	const fgRef = useRef();
	const {
		getDetailedServiceMapItems,
		getServiceMapItems,
		globalTime,
		serviceMap,
	} = props;

	useEffect(() => {
		getServiceMapItems(globalTime);
		getDetailedServiceMapItems(globalTime);
	}, [globalTime]);

	useEffect(() => {
		fgRef.current && fgRef.current.d3Force("charge").strength(-400);
	});
	if (!serviceMap.items.length || !serviceMap.services.length) {
		return <Spin />;
	}

	const zoomToService = (value: string) => {
		fgRef && fgRef.current.zoomToFit(700, 380, (e) => e.id === value);
	};

	const { nodes, links } = getGraphData(serviceMap);
	const graphData = { nodes, links };
	return (
		<Container>
			<SelectService
				services={serviceMap.services}
				zoomToService={zoomToService}
			/>
			<ForceGraph2D
				ref={fgRef}
				cooldownTicks={100}
				onEngineStop={() => {
					fgRef.current.zoomToFit(100, 120);
				}}
				graphData={graphData}
				nodeLabel="id"
				linkAutoColorBy={(d) => d.target}
				linkDirectionalParticles="value"
				linkDirectionalParticleSpeed={(d) => d.value}
				nodeCanvasObject={(node, ctx, globalScale) => {
					const label = node.id;
					const fontSize = node.fontSize;
					ctx.font = `${fontSize}px Roboto`;
					const width = node.width;

					ctx.fillStyle = node.color;
					ctx.beginPath();
					ctx.arc(node.x, node.y, width, 0, 2 * Math.PI, false);
					ctx.fill();
					ctx.textAlign = "center";
					ctx.textBaseline = "middle";
					ctx.fillStyle = "#333333";
					ctx.fillText(label, node.x, node.y);
				}}
				onNodeClick={(node) => {
					const tooltip = document.querySelector(".graph-tooltip");
					if (tooltip && node) {
						tooltip.innerHTML = `<div style="color:#333333;padding:12px;background: white;border-radius: 2px;">
								<div style="font-weight:bold; margin-bottom:16px;">${node.id}</div>
								<div class="keyval">
									<div class="key">P99 latency:</div>
									<div class="val">${node.p99 / 1000000}ms</div>
								</div>
								<div class="keyval">
									<div class="key">Request:</div>
									<div class="val">${node.callRate}/sec</div>
								</div>
								<div class="keyval">
									<div class="key">Error Rate:</div>
									<div class="val">${node.errorRate}%</div>
								</div>
							</div>`;
					}
				}}
				nodePointerAreaPaint={(node, color, ctx) => {
					ctx.fillStyle = color;
					ctx.beginPath();
					ctx.arc(node.x, node.y, 5, 0, 2 * Math.PI, false);
					ctx.fill();
				}}
			/>
		</Container>
	);
};

const mapStateToProps = (
	state: StoreState,
): {
	serviceMap: serviceMapStore;
	globalTime: GlobalTime;
} => {
	return {
		serviceMap: state.serviceMap,
		globalTime: state.globalTime,
	};
};

export default connect(mapStateToProps, {
	getServiceMapItems: getServiceMapItems,
	getDetailedServiceMapItems: getDetailedServiceMapItems,
})(ServiceMap);
