import { useRouter } from 'next/router'
import { useGroups } from '../contexts/GroupProvider'
import { FC, useMemo, useState } from 'react'
import { calculateInputs } from '../../src/calculateInputs'
import { DetailGraphNode, NodeDetails } from './NodeDetails/NodeDetails'
import { groupBy, isNonNullable, uniquify } from '../../src/utils'
import { EnrichedEntity } from '../../src/types'
import Head from 'next/head'
import { ScrollContainer } from './ScrollContainer/ScrollContainer'
import { ProducingGraph } from './ProducingGraph/ProducingGraph'
import { useFactories } from '../contexts/FactoryProvider'
import { i18n, I18n } from '../shared/I18n/I18n'
import { useIntl } from 'react-intl'
import { Button } from '../shared/Button/Button'
import { useAsyncEffect } from '../../src/hooks/useAsyncEffect'

export const PageDetails: FC = () => {
  const intl = useIntl()
  const {
    query: { name }
  } = useRouter()
  const { exportedFactories, baseFactories, groups } = useGroups()
  const { findFactory } = useFactories()

  const group = typeof name === 'string' ? groups[name] : undefined

  const [inputFactories, intermediateFactories] = useMemo<
    ReturnType<typeof calculateInputs>
  >(() => {
    if (!group) return [[], []]
    return calculateInputs(
      [...group.exports, ...group.malls],
      baseFactories,
      exportedFactories,
      findFactory
    )
  }, [baseFactories, exportedFactories, findFactory, group])

  const producingNodes: DetailGraphNode[] = useMemo(() => {
    if (!group) return []
    const nodes = uniquify([...intermediateFactories, ...group.exports, ...group.malls])
      .map(findFactory)
      .filter(isNonNullable)
      .map(
        (factory: EnrichedEntity) =>
          ({
            inputs: Object.keys(factory.recipe?.prerequisites ?? {}).sort((a, b) =>
              a.localeCompare(b)
            ),
            outputs: Object.keys(factory.recipe?.output ?? {}).sort((a, b) => a.localeCompare(b)),
            name: factory.name,
            recipes: [factory.recipe]
          } as DetailGraphNode)
      )
    return Object.values(groupBy(nodes, node => node.inputs.join())).map(
      nodesOfInput =>
        ({
          inputs: nodesOfInput[0].inputs,
          outputs: uniquify(nodesOfInput.flatMap(node => node.outputs)),
          name: nodesOfInput.map(node => node.name).join(', '),
          recipes: nodesOfInput.flatMap(node => node.recipes)
        } as DetailGraphNode)
    )
  }, [findFactory, group, intermediateFactories])

  const [savingModuleText, setSavingModuleText] = useState<string | undefined>()
  // eslint-disable-next-line react-hooks/exhaustive-deps
  useAsyncEffect(async () => {
    const res = await fetch('/factorio/modules/energy-saving.csv')
    if (res.ok) setSavingModuleText(await res.text())
  }, [])

  const [optimalModuleText, setOptimalModuleText] = useState<string | undefined>()
  // eslint-disable-next-line react-hooks/exhaustive-deps
  useAsyncEffect(async () => {
    const res = await fetch('/factorio/modules/few-beacons.csv')
    if (res.ok) setOptimalModuleText(await res.text())
  }, [])

  const statisticsUrl = useMemo(() => {
    const toDashedKey = (n: string) => n.slice(1).toLowerCase().replace(/_/g, '-')
    const checkIfPresent = (dashedKey: string) => {
      const b =
        !!optimalModuleText?.match(new RegExp(`${dashedKey};`)) && dashedKey !== 'rail-signal'
      return b
    }
    const getModuleString = (dashedKey: string, powerSaving?: boolean) => {
      const match = (powerSaving ? savingModuleText : optimalModuleText)?.match(
        new RegExp(`${dashedKey};.*;(\\d+);(\\d+);(\\d+);(\\d+)`)
      )
      if (match) {
        const map = 'spe'
        const [beacons, ...modules] = match.slice(1).map(x => +x)
        let beaconModules = beacons == 1 ? 8 : beacons == 5 ? 12 : 0
        const maxIdx = modules.reduce((m, c, i, arr) => (c > arr[m] ? i : m), 0)
        modules[maxIdx] -= beaconModules
        if (modules[maxIdx] < 0) {
          const newMaxIdx = modules.reduce((m, c, i, arr) => (c > arr[m] ? i : m), 0)
          modules[newMaxIdx] += modules[maxIdx]
          beaconModules += modules[maxIdx]
          modules[maxIdx] = 0
        }
        let res = [
          dashedKey,
          ...[0, 1, 2].flatMap(idx => Array(modules[idx]).fill(`${map[idx]}3`))
        ].join(':')
        if (beacons + modules[0] + modules[1] === 0 && modules[2] === 2)
          res = res.replace(/e3/g, 'e2')
        else if (beacons + modules[0] + modules[1] === 0 && modules[2] >= 3)
          res = res.replace(/e3/g, 'ee')
        // console.log(beacons, modules, res, dashedKey)
        return res + (beaconModules ? `;${map[maxIdx]}3:${beaconModules}` : '')
      }
      return dashedKey
    }
    const params = {
      data: '1-1-19',
      rate: 's',
      cp: '2',
      min: '3',
      belt: 'express-transport-belt',
      items: [
        group?.malls
          .map(toDashedKey)
          .filter(checkIfPresent)
          .map(n => `${n}:r:1`) ?? [],
        group?.exports
          .map(toDashedKey)
          .filter(checkIfPresent)
          .map(n => `${n}:r:45`) ?? []
      ].join(','),
      ignore: inputFactories.map(toDashedKey).filter(checkIfPresent).join(','),
      modules: [
        ...([...(group?.exports ?? []), ...intermediateFactories, ...inputFactories]
          .map(toDashedKey)
          .filter(checkIfPresent)
          .map(n => getModuleString(n)) ?? []),
        ...((group?.malls ?? [])
          .map(toDashedKey)
          .filter(checkIfPresent)
          .map(n => getModuleString(n, true)) ?? [])
      ].join(',')
    }
    const paramString = Object.entries(params)
      .map(keyValue => keyValue.join('='))
      .join('&')
    // console.log('params', 'https://kirkmcdonald.github.io/calc.html#' + paramString)
    return 'https://kirkmcdonald.github.io/calc.html#' + paramString
  }, [group, inputFactories, savingModuleText, optimalModuleText, intermediateFactories])

  return (
    <>
      <Head>
        <title>
          <I18n id={'page.visualize.details.title'} values={{ name: group?.name ?? '' }} />
        </title>
        <meta name='description' content={i18n(intl, 'page.home.head.meta.description')} />
      </Head>
      <main>
        <ScrollContainer>
          <h1>
            <I18n id={'page.visualize.details.title'} values={{ name: group?.name ?? '' }} />
          </h1>
          <Button onClick={() => window.open(statisticsUrl, '_blank')}>
            <I18n id={'page.visualize.details.statistics.button'} />
          </Button>
          <ProducingGraph
            nodes={producingNodes}
            inputs={inputFactories}
            outputs={group?.exports}
            childType={NodeDetails}
          />
        </ScrollContainer>
      </main>
    </>
  )
}
