import { useCoreServices } from '@learnitive/context';
import type {
  LearnitiveCompiler,
  LearnitiveCompileTarget,
  LearnitivePdfCompileMode,
} from '@learnitive/project-storage';
import type { ProjectCompileState } from '@learnitive/service-core';
import {
  Button,
  Dialog,
  DialogContent,
  DialogDescription,
  DialogHeader,
  DialogTitle,
  Select,
  SelectContent,
  SelectItem,
  SelectTrigger,
  SelectValue,
} from '@learnitive/ui-components';
import { useAtomValue } from 'jotai';
import { Check, CircleAlert, Loader2, Play, Settings, Square } from 'lucide-react';
import React from 'react';
import { flushProjectEditorDrafts } from '../project-compile-ui';
import {
  COMPILE_AUTH_REQUIRED_EVENT,
  ensureCompileAuthentication,
  type CompileAuthRequiredDetail,
} from '../compile-auth-gate';
import { InteractiveAuthCard, currentClientReturnPath } from '../saas/auth-card';
import { getPublicSaasConfig, type PublicSaasConfig } from '../saas/saas-api';

const COMPILER_ENGINES: readonly LearnitiveCompiler[] = [
  'pdflatex',
  'xelatex',
  'lualatex',
];

const COMPILER_LABELS: Record<LearnitiveCompiler, string> = {
  pdflatex: 'pdfLaTeX',
  xelatex: 'XeLaTeX',
  lualatex: 'LuaLaTeX',
};

function isLearnitiveCompiler(value: string | null): value is LearnitiveCompiler {
  return value === 'pdflatex' || value === 'xelatex' || value === 'lualatex';
}

function isCompileTarget(value: string | null): value is LearnitiveCompileTarget {
  return value === 'local' || value === 'cloud';
}

export function CompilePanel({ wsName }: { wsName: string }) {
  const { projectCompile, projectCompileOrchestrator, workspaceState } = useCoreServices();
  const state = useAtomValue(projectCompile.$state);
  const universalState = useAtomValue(projectCompileOrchestrator.$state);
  const currentFile = useAtomValue(workspaceState.$currentWsFilePath);
  const capabilitiesState = useAtomValue(projectCompile.$capabilities);
  const [open, setOpen] = React.useState(false);
  const [compileTarget, setCompileTarget] = React.useState<LearnitiveCompileTarget>('local');
  const [cloudFallbackSticky, setCloudFallbackSticky] = React.useState(false);
  const [cloudCapabilities, setCloudCapabilities] = React.useState<Awaited<ReturnType<typeof projectCompile.getCloudCompilerCapabilities>>>();
  const [projectCompiler, setProjectCompiler] =
    React.useState<LearnitiveCompiler>('pdflatex');
  const [pdfCompileMode, setPdfCompileMode] =
    React.useState<LearnitivePdfCompileMode>('full');
  const [compilerConfigError, setCompilerConfigError] = React.useState<string>();
  const [buttonFeedback, setButtonFeedback] = React.useState<'success' | 'error'>();
  const [authOpen, setAuthOpen] = React.useState(false);
  const [authReturnTo, setAuthReturnTo] = React.useState('/projects');
  const [authConfig, setAuthConfig] = React.useState<PublicSaasConfig>();
  const [authConfigLoading, setAuthConfigLoading] = React.useState(false);
  const [authConfigError, setAuthConfigError] = React.useState<string>();

  const relevant: ProjectCompileState =
    state.wsName === wsName ? state : { status: 'idle' };
  const universalRelevant = universalState.wsName === wsName ? universalState : undefined;
  const busy = universalRelevant
    ? ['saving', 'queued', 'preparing', 'compiling', 'running'].includes(universalRelevant.phase)
    : relevant.status === 'initializing' || relevant.status === 'compiling';
  const isLatexContext = currentFile?.wsName === wsName && /\.(?:tex|ltx)$/iu.test(currentFile.path);
  const busyLabel = universalRelevant?.phase === 'saving' ? 'Saving…' : universalRelevant?.phase === 'queued' ? 'Queued…' : universalRelevant?.phase === 'running' ? 'Running…' : universalRelevant?.phase === 'preparing' ? 'Preparing…' : 'Compiling…';
  const runtime =
    relevant.runtimeDiagnostics ?? capabilitiesState.runtimeDiagnostics;
  const cloudTargetLabel = 'Cloud Compile';
  const cloudSelectable = cloudCapabilities?.enabled === true;

  React.useEffect(() => {
    let active = true;
    void Promise.all([
      projectCompile.getProjectCompileTarget(wsName),
      projectCompile.getProjectCompiler(wsName),
      projectCompile.getProjectPdfCompileMode(wsName),
      projectCompile.getProjectCloudFallbackSticky(wsName),
      projectCompile.getCloudCompilerCapabilities(),
    ])
      .then(([target, compiler, mode, sticky, cloud]) => {
        if (!active) return;
        setCompileTarget(target);
        setProjectCompiler(compiler);
        setPdfCompileMode(mode);
        setCloudFallbackSticky(sticky);
        setCloudCapabilities(cloud);
      })
      .catch(() => {});
    return () => {
      active = false;
    };
  }, [projectCompile, wsName]);

  React.useEffect(() => {
    if (!open) return;
    let active = true;
    setCompilerConfigError(undefined);
    void Promise.all([
      projectCompile.getProjectCompileTarget(wsName),
      projectCompile.getProjectCompiler(wsName),
      projectCompile.getProjectPdfCompileMode(wsName),
      projectCompile.getProjectCloudFallbackSticky(wsName),
      projectCompile.getCompilerCapabilities(),
      projectCompile.getCloudCompilerCapabilities(),
    ])
      .then(([target, compiler, mode, sticky, _local, cloud]) => {
        if (!active) return;
        setCompileTarget(target);
        setProjectCompiler(compiler);
        setPdfCompileMode(mode);
        setCloudFallbackSticky(sticky);
        setCloudCapabilities(cloud);
      })
      .catch((error) => {
        if (!active) return;
        setCompilerConfigError(
          error instanceof Error
            ? error.message
            : 'Compiler status could not be loaded.',
        );
      });
    return () => {
      active = false;
    };
  }, [open, projectCompile, wsName]);

  const updateCompileTarget = React.useCallback(
    async (nextValue: string | null) => {
      if (!isCompileTarget(nextValue)) return;
      setCompilerConfigError(undefined);
      try {
        await projectCompile.setProjectCompileTarget(wsName, nextValue);
        setCompileTarget(nextValue);
        setCloudFallbackSticky(false);
        if (nextValue === 'cloud') {
          const cloud = cloudCapabilities ?? await projectCompile.getCloudCompilerCapabilities();
          setCloudCapabilities(cloud);
          if (!cloud.allowedEngines.includes(projectCompiler) && cloud.allowedEngines[0]) {
            await projectCompile.setProjectCompiler(wsName, cloud.allowedEngines[0]);
            setProjectCompiler(cloud.allowedEngines[0]);
          }
        }
      } catch (error) {
        setCompilerConfigError(error instanceof Error ? error.message : 'Compile target could not be saved.');
      }
    },
    [cloudCapabilities, projectCompile, projectCompiler, wsName],
  );

  const updateCompiler = React.useCallback(
    async (nextValue: string | null) => {
      if (!isLearnitiveCompiler(nextValue)) return;
      setCompilerConfigError(undefined);
      try {
        await projectCompile.setProjectCompiler(wsName, nextValue);
        setProjectCompiler(nextValue);
        setCloudFallbackSticky(false);
      } catch (error) {
        setCompilerConfigError(
          error instanceof Error
            ? error.message
            : 'Compiler selection could not be saved.',
        );
      }
    },
    [projectCompile, wsName],
  );

  const updatePdfCompileMode = React.useCallback(
    async (nextValue: string | null) => {
      if (nextValue !== 'full' && nextValue !== 'fast') return;
      setCompilerConfigError(undefined);
      try {
        await projectCompile.setProjectPdfCompileMode(wsName, nextValue);
        setPdfCompileMode(nextValue);
        setCloudFallbackSticky(false);
      } catch (error) {
        setCompilerConfigError(
          error instanceof Error
            ? error.message
            : 'pdfLaTeX mode could not be saved.',
        );
      }
    },
    [projectCompile, wsName],
  );

  React.useEffect(() => {
    if (!universalRelevant || (universalRelevant.phase !== 'success' && universalRelevant.phase !== 'error')) return;
    setButtonFeedback(universalRelevant.phase);
    const timer = window.setTimeout(() => setButtonFeedback(undefined), 1200);
    return () => window.clearTimeout(timer);
  }, [universalRelevant?.phase, universalRelevant?.executionId, universalRelevant?.duration]);

  React.useEffect(() => {
    const onCompileAuthRequired = (raw: Event) => {
      const event = raw as CustomEvent<CompileAuthRequiredDetail>;
      setAuthReturnTo(event.detail?.returnTo || currentClientReturnPath());
      setAuthOpen(true);
    };
    window.addEventListener(COMPILE_AUTH_REQUIRED_EVENT, onCompileAuthRequired);
    return () => window.removeEventListener(COMPILE_AUTH_REQUIRED_EVENT, onCompileAuthRequired);
  }, []);

  React.useEffect(() => {
    if (!authOpen || authConfig) return;
    let active = true;
    setAuthConfigLoading(true);
    setAuthConfigError(undefined);
    void getPublicSaasConfig()
      .then((config) => {
        if (active) setAuthConfig(config);
      })
      .catch(() => {
        if (active) setAuthConfigError('Sign in options could not be loaded. Please try again.');
      })
      .finally(() => {
        if (active) setAuthConfigLoading(false);
      });
    return () => {
      active = false;
    };
  }, [authConfig, authOpen]);

  const runCompile = React.useCallback(async () => {
    if (busy) return;
    if (!(await ensureCompileAuthentication())) return;
    try {
      await projectCompileOrchestrator.compile(wsName, {
        activePath: currentFile?.wsName === wsName ? currentFile.path : undefined,
        prepare: () => flushProjectEditorDrafts(wsName),
      });
      if (isLatexContext) {
        setCloudFallbackSticky(await projectCompile.getProjectCloudFallbackSticky(wsName));
      }
    } catch {
      // The orchestrator owns durable result/error state shown in the result pane.
    }
  }, [busy, currentFile?.path, currentFile?.wsName, isLatexContext, projectCompile, projectCompileOrchestrator, wsName]);



  return (
    <>
      <div className="desktop-titlebar-no-drag flex shrink-0 items-center gap-1">
        <Button
          aria-label={busy ? busyLabel : buttonFeedback === 'success' ? 'Compile succeeded' : buttonFeedback === 'error' ? 'Compile failed' : 'Compile project'}
          className="interactive-compile-primary h-7 gap-1.5 px-2 sm:px-2.5"
          disabled={busy}
          size="sm"
          title={busy ? busyLabel : 'Compile · Ctrl/Cmd + Enter'}
          onClick={() => void runCompile()}
        >
          {busy ? <Loader2 className="size-3.5 animate-spin" /> : buttonFeedback === 'success' ? <Check className="size-3.5" /> : buttonFeedback === 'error' ? <CircleAlert className="size-3.5" /> : <Play className="size-3.5" />}
          <span className="hidden sm:inline">{busy ? busyLabel : buttonFeedback === 'success' ? 'Success' : buttonFeedback === 'error' ? 'Error' : 'Compile'}</span>
        </Button>
        {isLatexContext && (
          <Button
            aria-label="Compiler settings"
            className="h-7 w-7"
            size="icon"
            title="Compiler settings"
            variant="ghost"
            onClick={() => setOpen(true)}
          >
            <Settings className="size-4" />
          </Button>
        )}
      </div>

      <Dialog open={open} onOpenChange={setOpen}>
        <DialogContent className="flex max-h-[calc(100dvh-1rem)] flex-col overflow-y-auto sm:max-h-[82vh] sm:max-w-2xl">
          <DialogHeader>
            <DialogTitle>{t.app.compile.title}</DialogTitle>
            <DialogDescription>Choose compile target, engine and PDF mode. <a className="underline underline-offset-2" href="/docs/compiling-latex">Compilation guide</a></DialogDescription>
          </DialogHeader>

          <div className="rounded-md border bg-muted/20 p-3">
            <div className="mb-3 flex flex-wrap items-center justify-between gap-3 border-b pb-3">
              <div className="font-medium text-sm">Compile target</div>
              <Select value={compileTarget} onValueChange={(value) => void updateCompileTarget(value)}>
                <SelectTrigger aria-label="Compile target" className="w-48" disabled={busy}>
                  <SelectValue />
                </SelectTrigger>
                <SelectContent align="end">
                  <SelectItem value="local">Local Compile</SelectItem>
                  <SelectItem disabled={!cloudSelectable} value="cloud">Cloud Compile</SelectItem>
                </SelectContent>
              </Select>
            </div>
            {compileTarget === 'local' && cloudFallbackSticky && (
              <div className="mb-3 flex flex-wrap items-center justify-between gap-2 rounded-md border bg-background/70 p-2.5 text-xs">
                <span className="text-muted-foreground">Docker TeX Live Full is active for this project.</span>
                <Button
                  className="h-7"
                  disabled={busy}
                  size="sm"
                  variant="outline"
                  onClick={() => void updateCompileTarget('local')}
                >
                  Retry browser compiler
                </Button>
              </div>
            )}
            {compileTarget === 'cloud' && (
              <div className="mb-3 rounded-md border bg-background/70 p-2.5 text-muted-foreground text-xs">
                Cloud Compile uses Learnitive’s isolated Docker TeX Live environment.
              </div>
            )}
            <div className="flex flex-wrap items-center justify-between gap-3">
              <div className="font-medium text-sm">
                {t.app.compile.compilerEngine}
              </div>
              <Select
                value={projectCompiler}
                onValueChange={(value) => void updateCompiler(value)}
              >
                <SelectTrigger
                  aria-label={t.app.compile.compilerEngine}
                  className="w-40"
                  disabled={busy || (compileTarget === 'local' && capabilitiesState.status === 'loading')}
                >
                  <SelectValue />
                </SelectTrigger>
                <SelectContent align="end" className="min-w-56">
                  {COMPILER_ENGINES.map((engine) => {
                    const capability = capabilitiesState.capabilities?.engines[engine];
                    const available = compileTarget === 'cloud'
                      ? cloudCapabilities?.allowedEngines.includes(engine) === true
                      : capability ? capability.available : engine === 'pdflatex';
                    return (
                      <SelectItem
                        key={engine}
                        disabled={!available}
                        value={engine}
                      >
                        <span>{COMPILER_LABELS[engine]}</span>
                        {!available && (
                          <span className="text-muted-foreground text-xs">
                            {t.app.compile.unavailable}
                          </span>
                        )}
                      </SelectItem>
                    );
                  })}
                </SelectContent>
              </Select>
            </div>

            {compileTarget === 'local' && projectCompiler === 'pdflatex' && (
              <div className="mt-3 flex flex-wrap items-center justify-between gap-3 border-t pt-3">
                <div className="font-medium text-sm">
                  {t.app.compile.pdfCompileMode}
                </div>
                <Select
                  value={pdfCompileMode}
                  onValueChange={(value) => void updatePdfCompileMode(value)}
                >
                  <SelectTrigger
                    aria-label={t.app.compile.pdfCompileMode}
                    className="w-44"
                    disabled={busy || capabilitiesState.status === 'loading'}
                  >
                    <SelectValue />
                  </SelectTrigger>
                  <SelectContent align="end">
                    <SelectItem
                      disabled={
                        capabilitiesState.capabilities
                          ? !capabilitiesState.capabilities.engines.pdflatex
                              .supportsSyncTeX
                          : true
                      }
                      value="full"
                    >
                      {t.app.compile.fullSyncMode}
                    </SelectItem>
                    <SelectItem value="fast">
                      {t.app.compile.fastPreviewMode}
                    </SelectItem>
                  </SelectContent>
                </Select>
              </div>
            )}
          </div>

          {compileTarget === 'local' && capabilitiesState.capabilities && (
            <div className="rounded-md border bg-muted/20 p-3 text-xs">
              <div className="font-medium text-sm">Browser compiler runtime</div>
              <div className="mt-1 text-muted-foreground">
                TeX Live bundle · {capabilitiesState.capabilities.texlive.bundled.fileCount} {t.app.compile.files}
                {runtime ? ` · ${runtime.status}` : ''}
              </div>
              {runtime?.error && (
                <div className="mt-2 text-destructive" role="alert">
                  {runtime.error}
                </div>
              )}
            </div>
          )}

          {(compilerConfigError || (compileTarget === 'local' ? capabilitiesState.error : undefined)) && (
            <div className="text-destructive text-xs" role="alert">
              {compilerConfigError ?? (compileTarget === 'local' ? capabilitiesState.error : undefined)}
            </div>
          )}

          <div className="flex items-center justify-end gap-2">
            {busy && (
              <Button variant="outline" onClick={() => projectCompile.cancel()}>
                <Square className="mr-2 size-3" />
                {t.app.compile.cancel}
              </Button>
            )}
            <Button variant="outline" onClick={() => setOpen(false)}>
              Close
            </Button>
          </div>
        </DialogContent>
      </Dialog>

      <Dialog open={authOpen} onOpenChange={setAuthOpen}>
        <DialogContent className="interactive-auth-dialog p-0 sm:max-w-md">
          <DialogHeader className="sr-only">
            <DialogTitle>Sign in</DialogTitle>
            <DialogDescription>Sign in to compile this project.</DialogDescription>
          </DialogHeader>
          <InteractiveAuthCard
            config={authConfig}
            returnTo={authReturnTo}
            description="Sign in to compile this project. You will return here after authentication."
            providersLoading={authConfigLoading}
            providerError={authConfigError}
            onContinueWithoutAccount={() => setAuthOpen(false)}
          />
        </DialogContent>
      </Dialog>
    </>
  );
}
