import { notifier } from '@tidbcloud/uikit'
import { observer } from 'mobx-react'
import React, { Dispatch, SetStateAction, useCallback, useEffect, useRef, useState } from 'react'

import config from 'config'
import { SharedCluster } from 'dbaas/features/ClusterManagement/cluster'
import { listBranches } from 'dbaas/services/api-clients/legacy/serverless'
import { ConsoleListBranchesResp as ServerlessListBranchesResp } from 'dbaas/services/api-clients/legacy/serverless/interface'
import {
  exchangeTokenService,
  getConnectionService,
  getGithubAccountsService,
  getGithubRepoListService
} from 'dbaas/services/integration'
import { GitAccountsVO, GitRepositoriesVO, GitTiDBGitConnectionVO } from 'dbaas/services/integration/interface'
import useStores from 'dbaas/stores/useStores'

type SetAction<T> = Dispatch<SetStateAction<T>>

export interface ServerlessBranchContext {
  isConnect: boolean
  connectModalShow: boolean
  setConnectModalShow: SetAction<boolean>
  getBranchList: (size?: number, page?: number, showLoading?: boolean) => void
  branchList?: ServerlessListBranchesResp
  branchListLoading: boolean
  githubConnect: () => void
  accountList?: GitAccountsVO[]
  getRepoList: (installationId: string, repoName?: string) => void
  repoList: GitRepositoriesVO[]
  getGithubAccounts: () => void
  connectionLoading: boolean
  getGithubConnect: () => void
  connectInfo?: GitTiDBGitConnectionVO
  branchListLoaded: boolean
}

export const ServerlessBranchContext = React.createContext<ServerlessBranchContext | null>(null)

export const ServerlessBranchProvider: React.FC<React.PropsWithChildren<{ cluster: SharedCluster }>> = observer(
  (props) => {
    const {
      store: { tenantData, activeProject: project }
    } = useStores()

    const windowHandler = useRef<Window | null>(null)

    const accessToken = useRef<string | null>(null)

    const refreshToken = useRef<string | null>(null)

    const [isConnect, setIsConnect] = useState<boolean>(false)

    const [connectInfo, setConnectInfo] = useState<GitTiDBGitConnectionVO>()

    const [isActiveToken, setIsActiveToken] = useState<boolean>(false)

    const [connectModalShow, setConnectModalShow] = useState<boolean>(false)

    const [branchList, setBranchList] = useState<ServerlessListBranchesResp>()

    const [branchListLoading, setBranchListLoading] = useState<boolean>(true)
    const [branchListLoaded, setBranchListLoaded] = useState(false)

    const [accountList, setAccountList] = useState<GitAccountsVO[]>()

    const [repoList, setRepoList] = useState<GitRepositoriesVO[]>([])

    const [connectionLoading, setConnectionLoading] = useState<boolean>(false)

    useEffect(() => {
      accessToken.current = localStorage.getItem('github_access_token')
      refreshToken.current = localStorage.getItem('github_refresh_token')
      window.addEventListener('message', exchangeTokenByCode)
      getGithubConnect()
      return () => window.removeEventListener('message', exchangeTokenByCode)
    }, [])

    const getGithubConnect = useCallback(async () => {
      try {
        const resp = await getConnectionService(tenantData.id, project.id, props.cluster.id)
        setConnectInfo(resp.connection)
        setIsConnect(true)
      } catch (_e) {
        setIsConnect(false)
      }
    }, [props.cluster.id])

    const githubConnect = useCallback(() => {
      getGithubAccounts()
    }, [])

    const exchangeTokenByCode = useCallback(async (event: MessageEvent) => {
      if (event.data.githubCode) {
        windowHandler.current?.close()
        windowHandler.current = null
        try {
          const resp = await exchangeTokenService({ code: event.data.githubCode })
          if (resp.access_token && resp.refresh_token) {
            localStorage.setItem('github_access_token', resp.access_token)
            localStorage.setItem('github_refresh_token', resp.refresh_token)
            accessToken.current = resp.access_token
            refreshToken.current = resp.refresh_token
            getGithubAccounts()
          }
        } finally {
          setConnectionLoading(false)
        }
      }
    }, [])

    const getGithubAccounts = useCallback(async () => {
      if (accessToken.current && refreshToken.current) {
        if (isActiveToken) {
          setConnectModalShow(true)
        } else {
          try {
            setConnectionLoading(true)
            const resp = await getGithubAccountsService({
              headers: {
                github_access_token: accessToken.current,
                github_refresh_token: refreshToken.current
              }
            })
            setAccountList(resp.accounts)
            setIsActiveToken(true)
            setConnectModalShow(true)
          } catch (e: any) {
            if (e.errorCode === 49900002) {
              setIsActiveToken(false)
              getGithubAuth()
            }
          } finally {
            setConnectionLoading(false)
          }
        }
      } else {
        getGithubAuth()
      }
    }, [setAccountList])

    const getGithubAuth = useCallback(async () => {
      windowHandler.current = openWindow(getGithubAuthUrl(), 'gitHubAuth', 800, 600)
    }, [getGithubAuthUrl])

    const getBranchList = useCallback(async (size?: number, page?: number, showLoading: boolean = true) => {
      showLoading && setBranchListLoading(true)
      try {
        const result = await listBranches(tenantData.id, project.id, props.cluster.id, {
          page_size: size,
          page_token: page
        })
        setBranchList(result)
      } catch (e: any) {
        showLoading && notifier.error(<div dangerouslySetInnerHTML={{ __html: e.message }}></div>)
      } finally {
        showLoading && setBranchListLoading(false)
        setBranchListLoaded(true)
      }
    }, [])

    const getRepoList = useCallback(async (installationId: string, repoName?: string) => {
      const resp = await getGithubRepoListService(
        {
          installation_id: installationId,
          repo_name: repoName
        },
        { headers: { github_access_token: accessToken.current ?? '' } }
      )
      resp.repositories && setRepoList(resp.repositories)
    }, [])

    return (
      <ServerlessBranchContext.Provider
        value={{
          isConnect,
          getBranchList,
          branchList,
          branchListLoading,
          githubConnect,
          accountList,
          getRepoList,
          repoList,
          getGithubAccounts,
          connectModalShow,
          setConnectModalShow,
          connectionLoading,
          getGithubConnect,
          connectInfo,
          branchListLoaded
        }}
      >
        {props.children}
      </ServerlessBranchContext.Provider>
    )
  }
)

const getGithubAuthUrl = () => {
  const githubAuthBaseUrl = 'https://github.com/login/oauth/authorize'
  const protocal = window.location.protocol
  let clientId = config.GITHUB_CLIENT_ID

  const redirectUri = `${protocal}//${window.location.host}/github-auth`
  const completeUrl = `${githubAuthBaseUrl}?client_id=${clientId}&redirect_uri=${redirectUri}`

  return completeUrl
}

export const openWindow = (url: string, name: string, width: number, height: number) => {
  var top = (window.screen.availHeight - 30 - width) / 2
  var left = (window.screen.availWidth - 10 - height) / 2
  const handler = window.open(url, name, `height=${height}, width=${width}, top=${top}, left=${left}`)
  return handler
}
