import { useState, useEffect, useRef } from 'react'
import { useTranslation } from 'react-i18next'
import getMeta from '@/utils/meta'
import { postJSON } from '@/infrastructure/fetch-json'
import { useDetachCompileContext as useCompileContext } from '@/shared/context/detach-compile-context'
import OLButton from '@/shared/components/ol/ol-button'
import OLFormControl from '@/shared/components/ol/ol-form-control'
import OLFormGroup from '@/shared/components/ol/ol-form-group'
import OLFormLabel from '@/shared/components/ol/ol-form-label'
import { FormCheck } from 'react-bootstrap'
import MaterialIcon from '@/shared/components/material-icon'
import OLNotification from '@/shared/components/ol/ol-notification'

type Action = 'save' | 'push' | 'pull'
type Status = 'idle' | 'busy' | 'success' | 'error'

const projectId = getMeta('ol-project_id')
const AUTO_PUSH_KEY = `gitSync.autoPushOnCompile.${projectId}`

export default function GitSyncWidget() {
  const { t } = useTranslation()
  const { compiling, error: compileError, pdfFile, clsiServerId } =
    useCompileContext()

  const [remoteUrl, setRemoteUrl] = useState(getMeta('ol-gitRemote') ?? '')
  const [subPath, setSubPath] = useState(getMeta('ol-gitSyncPath') ?? '')
  const [pdfPath, setPdfPath] = useState(getMeta('ol-gitSyncPdfPath') ?? '')
  const [pushFiles, setPushFiles] = useState(getMeta('ol-gitSyncPushFiles') ?? true)
  const [pushPdf, setPushPdf] = useState(getMeta('ol-gitSyncPushPdf') ?? true)
  const [autoPush, setAutoPush] = useState(
    () => localStorage.getItem(AUTO_PUSH_KEY) === 'true'
  )
  const [lastAction, setLastAction] = useState<Action>('save')
  const [status, setStatus] = useState<Status>('idle')
  const [errorMsg, setErrorMsg] = useState('')

  const wasCompilingRef = useRef(false)
  const isBusyRef = useRef(false)

  useEffect(() => {
    isBusyRef.current = status === 'busy'
  }, [status])

  // Auto-push when compile finishes successfully
  useEffect(() => {
    const wasCompiling = wasCompilingRef.current
    wasCompilingRef.current = compiling

    if (
      wasCompiling &&
      !compiling &&
      !compileError &&
      autoPush &&
      !isBusyRef.current
    ) {
      doPush(pdfFile?.build, clsiServerId)
    }
  }, [compiling]) // eslint-disable-line react-hooks/exhaustive-deps

  async function saveConfig() {
    await postJSON(`/project/${projectId}/git-sync`, {
      body: { remoteUrl, subPath, pdfPath, pushFiles, pushPdf },
    })
  }

  function extractError(err: any): string {
    return err?.data?.error ?? err?.message ?? String(err)
  }

  async function doPush(buildId?: string, serverId?: string) {
    setLastAction('push')
    setStatus('busy')
    setErrorMsg('')
    try {
      await saveConfig()
      await postJSON(`/project/${projectId}/git-sync/push`, {
        body: { buildId, clsiServerId: serverId },
      })
      setStatus('success')
    } catch (err: any) {
      setStatus('error')
      setErrorMsg(extractError(err))
    }
  }

  async function handleSave(e: React.FormEvent) {
    e.preventDefault()
    setLastAction('save')
    setStatus('busy')
    setErrorMsg('')
    try {
      await saveConfig()
      setStatus('success')
    } catch (err: any) {
      setStatus('error')
      setErrorMsg(extractError(err))
    }
  }

  function handlePush() {
    doPush(pdfFile?.build, clsiServerId)
  }

  async function handlePull() {
    setLastAction('pull')
    setStatus('busy')
    setErrorMsg('')
    try {
      await saveConfig()
      await postJSON(`/project/${projectId}/git-sync/pull`, { body: {} })
      setStatus('success')
    } catch (err: any) {
      setStatus('error')
      setErrorMsg(extractError(err))
    }
  }

  function handleAutoPushToggle(e: React.ChangeEvent<HTMLInputElement>) {
    const checked = e.target.checked
    setAutoPush(checked)
    localStorage.setItem(AUTO_PUSH_KEY, String(checked))
  }

  const isBusy = status === 'busy'
  const nothingToPush = !pushFiles && (!pushPdf || !pdfPath.trim())

  return (
    <div className="git-sync-widget">
      <div className="git-sync-widget-header">
        <MaterialIcon type="merge" />
        <span className="git-sync-widget-title">{t('git_sync')}</span>
      </div>
      <p className="git-sync-widget-description">{t('git_sync_description')}</p>

      <form onSubmit={handleSave}>
        <OLFormGroup controlId="git-sync-remote-url">
          <OLFormLabel>{t('git_sync_remote_url')}</OLFormLabel>
          <OLFormControl
            type="text"
            value={remoteUrl}
            onChange={e => setRemoteUrl(e.target.value)}
            placeholder="https://user:token@github.com/org/repo.git"
            disabled={isBusy}
          />
        </OLFormGroup>

        <OLFormGroup controlId="git-sync-sub-path">
          <OLFormLabel>{t('git_sync_sub_path')}</OLFormLabel>
          <OLFormControl
            type="text"
            value={subPath}
            onChange={e => setSubPath(e.target.value)}
            placeholder={t('git_sync_sub_path_placeholder')}
            disabled={isBusy}
          />
        </OLFormGroup>

        <OLFormGroup controlId="git-sync-push-files">
          <FormCheck
            type="switch"
            id="git-sync-push-files"
            label={t('git_sync_push_files')}
            checked={pushFiles}
            onChange={e => setPushFiles(e.target.checked)}
            disabled={isBusy}
          />
        </OLFormGroup>

        <OLFormGroup controlId="git-sync-pdf-path">
          <OLFormLabel>{t('git_sync_pdf_path')}</OLFormLabel>
          <OLFormControl
            type="text"
            value={pdfPath}
            onChange={e => setPdfPath(e.target.value)}
            placeholder={t('git_sync_pdf_path_placeholder')}
            disabled={isBusy}
          />
        </OLFormGroup>

        <OLFormGroup controlId="git-sync-push-pdf">
          <FormCheck
            type="switch"
            id="git-sync-push-pdf"
            label={t('git_sync_push_pdf')}
            checked={pushPdf}
            onChange={e => setPushPdf(e.target.checked)}
            disabled={isBusy || !pdfPath.trim()}
          />
        </OLFormGroup>

        <OLFormGroup controlId="git-sync-auto-push">
          <FormCheck
            type="switch"
            id="git-sync-auto-push"
            label={t('git_sync_auto_push_on_compile')}
            checked={autoPush}
            onChange={handleAutoPushToggle}
            disabled={isBusy || !remoteUrl.trim() || nothingToPush}
          />
        </OLFormGroup>

        <div className="git-sync-widget-actions">
          <OLButton
            type="submit"
            variant="secondary"
            size="sm"
            disabled={isBusy || !remoteUrl.trim()}
            isLoading={isBusy && lastAction === 'save'}
            loadingLabel={t('saving')}
          >
            {t('save')}
          </OLButton>
          <OLButton
            type="button"
            variant="primary"
            size="sm"
            onClick={handlePush}
            disabled={isBusy || !remoteUrl.trim() || nothingToPush}
            isLoading={isBusy && lastAction === 'push'}
            loadingLabel={t('git_sync_pushing')}
          >
            {t('git_sync_push_now')}
          </OLButton>
          <OLButton
            type="button"
            variant="secondary"
            size="sm"
            onClick={handlePull}
            disabled={isBusy || !remoteUrl.trim()}
            isLoading={isBusy && lastAction === 'pull'}
            loadingLabel={t('git_sync_pulling')}
          >
            {t('git_sync_pull_now')}
          </OLButton>
        </div>
      </form>

      {status === 'success' && (
        <OLNotification
          type="success"
          content={
            lastAction === 'push'
              ? t('git_sync_push_success')
              : lastAction === 'pull'
                ? t('git_sync_pull_success')
                : t('git_sync_saved')
          }
        />
      )}
      {status === 'error' && (
        <OLNotification type="error" content={errorMsg} />
      )}
    </div>
  )
}
