import { $updatedSystem, $systems, pb, $chartTime } from '@/lib/stores'
import { ContainerStatsRecord, SystemRecord, SystemStatsRecord } from '@/types'
import { Suspense, lazy, useEffect, useMemo, useState } from 'react'
import { Card, CardHeader, CardTitle, CardDescription, CardContent } from '../ui/card'
import { useStore } from '@nanostores/react'
import Spinner from '../spinner'
import { ClockArrowUp, CpuIcon, GlobeIcon } from 'lucide-react'
import ChartTimeSelect from '../charts/chart-time-select'
import { chartTimeData, cn, getPbTimestamp } from '@/lib/utils'
import { Separator } from '../ui/separator'
import { scaleTime } from 'd3-scale'
import DiskIoChart from '../charts/disk-io-chart'
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '../ui/tooltip'

const CpuChart = lazy(() => import('../charts/cpu-chart'))
const ContainerCpuChart = lazy(() => import('../charts/container-cpu-chart'))
const MemChart = lazy(() => import('../charts/mem-chart'))
const ContainerMemChart = lazy(() => import('../charts/container-mem-chart'))
const DiskChart = lazy(() => import('../charts/disk-chart'))

function timestampToBrowserTime(timestamp: string) {
	const date = new Date(timestamp)
	return date.toLocaleString()
}

export default function ServerDetail({ name }: { name: string }) {
	const servers = useStore($systems)
	const updatedSystem = useStore($updatedSystem)
	const chartTime = useStore($chartTime)
	const [ticks, setTicks] = useState([] as number[])
	const [server, setServer] = useState({} as SystemRecord)
	const [containers, setContainers] = useState([] as ContainerStatsRecord[])

	const [serverStats, setServerStats] = useState([] as SystemStatsRecord[])
	const [cpuChartData, setCpuChartData] = useState([] as { time: number; cpu: number }[])
	const [memChartData, setMemChartData] = useState(
		[] as { time: number; mem: number; memUsed: number; memCache: number }[]
	)
	const [diskChartData, setDiskChartData] = useState(
		[] as { time: number; disk: number; diskUsed: number }[]
	)
	const [diskIoChartData, setDiskIoChartData] = useState(
		[] as { time: number; read: number; write: number }[]
	)
	const [dockerCpuChartData, setDockerCpuChartData] = useState(
		[] as Record<string, number | string>[]
	)
	const [dockerMemChartData, setDockerMemChartData] = useState(
		[] as Record<string, number | string>[]
	)

	useEffect(() => {
		document.title = `${name} / Beszel`
		return () => {
			setServer({} as SystemRecord)
			setCpuChartData([])
			setMemChartData([])
			setDiskChartData([])
			setDockerCpuChartData([])
			setDockerMemChartData([])
		}
	}, [name])

	useEffect(() => {
		if (server.id && server.name === name) {
			return
		}
		const matchingServer = servers.find((s) => s.name === name) as SystemRecord
		if (matchingServer) {
			setServer(matchingServer)
		}
	}, [name, server, servers])

	// if visiting directly, make sure server gets set when servers are loaded
	// useEffect(() => {
	// 	if (!('id' in server)) {
	// 		const matchingServer = servers.find((s) => s.name === name) as SystemRecord
	// 		if (matchingServer) {
	// 			console.log('setting server')
	// 			setServer(matchingServer)
	// 		}
	// 	}
	// }, [servers])

	// get stats
	useEffect(() => {
		if (!server.id) {
			return
		}
		pb.collection<SystemStatsRecord>('system_stats')
			.getFullList({
				filter: `system="${server.id}" && created > "${getPbTimestamp('1h')}"`,
				fields: 'created,stats',
				sort: 'created',
			})
			.then((records) => {
				// console.log('sctats', records)
				setServerStats(records)
			})
	}, [server])

	useEffect(() => {
		if (updatedSystem.id === server.id) {
			setServer(updatedSystem)
		}
	}, [updatedSystem])

	// create cpu / mem / disk data for charts
	useEffect(() => {
		if (!serverStats.length) {
			return
		}
		const cpuData = [] as typeof cpuChartData
		const memData = [] as typeof memChartData
		const diskData = [] as typeof diskChartData
		const diskIoData = [] as typeof diskIoChartData
		for (let { created, stats } of serverStats) {
			const time = new Date(created).getTime()
			cpuData.push({ time, cpu: stats.cpu })
			memData.push({
				time,
				mem: stats.m,
				memUsed: stats.mu,
				memCache: stats.mb,
			})
			diskData.push({ time, disk: stats.d, diskUsed: stats.du })
			diskIoData.push({ time, read: stats.dr, write: stats.dw })
		}
		setCpuChartData(cpuData)
		setMemChartData(memData)
		setDiskChartData(diskData)
		setDiskIoChartData(diskIoData)
	}, [serverStats])

	useEffect(() => {
		if (!serverStats.length) {
			return
		}
		const now = new Date()
		const startTime = chartTimeData[chartTime].getOffset(now)
		const scale = scaleTime([startTime.getTime(), now], [0, cpuChartData.length])
		setTicks(scale.ticks().map((d) => d.getTime()))
	}, [chartTime, serverStats])

	useEffect(() => {
		if (!server.id) {
			return
		}
		pb.collection<ContainerStatsRecord>('container_stats')
			.getFullList({
				filter: `system="${server.id}" && created > "${getPbTimestamp('1h')}"`,
				fields: 'created,stats',
				sort: 'created',
			})
			.then((records) => {
				setContainers(records)
			})
	}, [server])

	// container stats for charts
	useEffect(() => {
		// console.log('containers', containers)
		const dockerCpuData = [] as Record<string, number | string>[]
		const dockerMemData = [] as Record<string, number | string>[]

		for (let { created, stats } of containers) {
			const time = new Date(created).getTime()
			let cpuData = { time } as (typeof dockerCpuChartData)[0]
			let memData = { time } as (typeof dockerMemChartData)[0]
			for (let container of stats) {
				cpuData[container.n] = container.c
				memData[container.n] = container.m
			}
			dockerCpuData.push(cpuData)
			dockerMemData.push(memData)
		}
		// console.log('containerMemData', containerMemData)
		setDockerCpuChartData(dockerCpuData)
		setDockerMemChartData(dockerMemData)
	}, [containers])
	const uptime = useMemo(() => {
		let uptime = server.info?.u || 0
		if (uptime < 172800) {
			return `${Math.trunc(uptime / 3600)} hours`
		}
		return `${Math.trunc(server.info?.u / 86400)} days`
	}, [server.info?.u])

	if (!server.id) {
		return null
	}

	return (
		<div className="grid gap-5 mb-10">
			<Card>
				<div className="grid gap-1.5 px-6 pt-4 pb-5">
					<h1 className="text-[1.6rem] font-semibold">{server.name}</h1>
					<div className="flex flex-wrap items-center gap-3 text-sm opacity-90">
						<div className="capitalize flex gap-2 items-center">
							<span className={cn('relative flex h-3 w-3')}>
								{server.status === 'up' && (
									<span
										className="animate-ping absolute inline-flex h-full w-full rounded-full bg-green-400 opacity-75"
										style={{ animationDuration: '1.5s' }}
									></span>
								)}
								<span
									className={cn('relative inline-flex rounded-full h-3 w-3', {
										'bg-green-500': server.status === 'up',
										'bg-red-500': server.status === 'down',
										'bg-primary/40': server.status === 'paused',
										'bg-yellow-500': server.status === 'pending',
									})}
								></span>
							</span>
							{server.status}
						</div>
						<Separator orientation="vertical" className="h-4 bg-primary/30" />
						<div className="flex gap-1.5 items-center">
							<GlobeIcon className="h-4 w-4" /> {server.host}
						</div>
						{server.info?.u && (
							<TooltipProvider>
								<Tooltip>
									<Separator orientation="vertical" className="h-4 bg-primary/30" />
									<TooltipTrigger asChild>
										<div className="flex gap-1.5 items-center">
											<ClockArrowUp className="h-4 w-4" /> {uptime}
										</div>
									</TooltipTrigger>
									<TooltipContent>Uptime</TooltipContent>
								</Tooltip>
							</TooltipProvider>
						)}
						{server.info?.m && (
							<>
								<Separator orientation="vertical" className="h-4 bg-primary/30" />
								<div className="flex gap-1.5 items-center">
									<CpuIcon className="h-4 w-4" />
									{server.info.m} ({server.info.c}c / {server.info.t}t)
								</div>
							</>
						)}
					</div>
				</div>
			</Card>

			<ChartCard
				title="Total CPU Usage"
				description="Average system-wide CPU utilization as a percentage"
			>
				<CpuChart chartData={cpuChartData} ticks={ticks} />
			</ChartCard>

			{dockerCpuChartData.length > 0 && (
				<ChartCard title="Docker CPU Usage" description="CPU utilization of docker containers">
					<ContainerCpuChart chartData={dockerCpuChartData} ticks={ticks} />
				</ChartCard>
			)}
			<ChartCard title="Total Memory Usage" description="Precise utilization at the recorded time">
				<MemChart chartData={memChartData} ticks={ticks} />
			</ChartCard>

			{dockerMemChartData.length > 0 && (
				<ChartCard title="Docker Memory Usage" description="Memory usage of docker containers">
					<ContainerMemChart chartData={dockerMemChartData} ticks={ticks} />
				</ChartCard>
			)}
			<ChartCard
				title="Disk Usage"
				description="Usage of partition where the root filesystem is mounted"
			>
				<DiskChart chartData={diskChartData} ticks={ticks} />
			</ChartCard>
			<ChartCard
				title="Disk I/O"
				description="Throughput of disk where the root filesystem is mounted"
			>
				<DiskIoChart chartData={diskIoChartData} ticks={ticks} />
			</ChartCard>
			<Card>
				<CardHeader>
					<CardTitle className={'mb-3'}>{server.name}</CardTitle>
					<CardDescription>
						{server.ip} - last updated: {timestampToBrowserTime(server.updated)}
					</CardDescription>
				</CardHeader>
				<CardContent>
					<pre>{JSON.stringify(server, null, 2)}</pre>
				</CardContent>
			</Card>
		</div>
	)
}

function ChartCard({
	title,
	description,
	children,
}: {
	title: string
	description: string
	children: React.ReactNode
}) {
	return (
		<Card className="pb-4 col-span-full">
			<CardHeader className="pb-5 pt-4">
				<CardTitle className="flex gap-2 items-center justify-between -mb-1.5">
					{title}
					<ChartTimeSelect className="translate-y-1" />
				</CardTitle>
				<CardDescription>{description}</CardDescription>
			</CardHeader>
			<CardContent className={'pl-1 w-[calc(100%-1.6em)] h-52 relative'}>
				<Suspense fallback={<Spinner />}>{children}</Suspense>
			</CardContent>
		</Card>
	)
}
