import {
  type Component,
  createSignal,
  createEffect,
  onMount,
  onCleanup,
  Show,
  For,
} from 'solid-js';
import { useNavigate, A } from '@solidjs/router';
import { AlertCircle, AlertTriangle, CheckCircle, Circle, Loader2 } from 'lucide-solid';
import { useI18n } from '~/i18n';
import { synthesesApi } from '~/api/syntheses';
import { settingsApi } from '~/api/settings';
import { configApi } from '~/api/config';
import { themesApi, type ThemeResponse } from '~/api/themes';
import { isApiError, DEFAULT_SETTINGS } from '~/types';
import type { UserSettings, ProviderConfig, ProgressEvent } from '~/types';
import { createSSEConnection, type SSEConnection, type SSEStatus } from '~/utils/sse';
import { providerSupportsWebSearch } from '~/utils/providers';
import LoadingSpinner from '~/components/ui/LoadingSpinner';
import Button from '~/components/ui/Button';

/** Metadata for a single generation pipeline step. */
interface StepInfo {
  key: string;
  label: string;
}

/** Ordered pipeline steps displayed as a checklist during generation. */
const STEPS: StepInfo[] = [
  { key: 'sources', label: 'generate.step.sources' },
  { key: 'websearch', label: 'generate.step.websearch' },
  { key: 'saving', label: 'generate.step.saving' },
];

/**
 * Synthesis generation page with real-time progress tracking.
 *
 * SSE state machine:
 *   idle -> connecting -> connected (progress events) -> complete | error
 *
 * 1. User clicks "Generate": a POST creates a job and returns a `job_id`.
 * 2. An SSE connection is opened to the progress endpoint for that job.
 * 3. `progress` events drive the progress bar and step checklist.
 * 4. A `complete` event carries the `synthesis_id`; the page auto-redirects
 *    after a short delay. An `error` event surfaces the message and stops.
 * 5. On connection loss, exponential backoff retries up to 3 times (see
 *    {@link createSSEConnection}).
 */
const GenerateSynthesis: Component = () => {
  const { t } = useI18n();
  const navigate = useNavigate();

  const [settings, setSettings] = createSignal<UserSettings>({ ...DEFAULT_SETTINGS });
  const [providers, setProviders] = createSignal<ProviderConfig[]>([]);
  const [themes, setThemes] = createSignal<ThemeResponse[]>([]);
  const [selectedThemeId, setSelectedThemeId] = createSignal<string>('');
  const [loadingSettings, setLoadingSettings] = createSignal(true);
  const [generating, setGenerating] = createSignal(false);
  const [error, setError] = createSignal<string | null>(null);
  const [success, setSuccess] = createSignal(false);
  const [sseConnection, setSSEConnection] = createSignal<SSEConnection | null>(null);
  const [jobId, setJobId] = createSignal<string | null>(null);

  onMount(async () => {
    try {
      const [data, providerList, themeList] = await Promise.all([
        settingsApi.get().catch((err) => {
          if (isApiError(err) && err.status === 404) return null;
          throw err;
        }),
        configApi.listProviders().catch(() => [] as ProviderConfig[]),
        themesApi.list().catch(() => [] as ThemeResponse[]),
      ]);
      if (data) setSettings(data);
      setProviders(providerList);
      setThemes(themeList);
      if (themeList.length > 0) {
        setSelectedThemeId(themeList[0].id);
      }
    } catch {
      // Non-404 settings error — use defaults silently
    } finally {
      setLoadingSettings(false);
    }
  });

  const selectedProvider = (): ProviderConfig | undefined => {
    return providers().find((p) => p.provider_name === settings().ai_provider);
  };

  const providerDisplayName = (): string => {
    return selectedProvider()?.display_name ?? settings().ai_provider;
  };

  const modelDisplayName = (): string => {
    const provider = selectedProvider();
    if (!provider) return settings().ai_model;
    const model = provider.models_scraping.find((m) => m.model_id === settings().ai_model);
    return model?.display_name ?? settings().ai_model;
  };

  const hasWebSearch = (): boolean => {
    return providerSupportsWebSearch(settings().ai_provider);
  };

  const currentStep = (): string | null => {
    const conn = sseConnection();
    if (!conn) return null;
    const progress = conn.latestProgress();
    return progress?.step ?? null;
  };

  const currentPercent = (): number => {
    const conn = sseConnection();
    if (!conn) return 0;
    const progress = conn.latestProgress();
    return progress?.percent ?? 0;
  };

  const currentMessage = (): string => {
    const conn = sseConnection();
    if (!conn) return '';
    const progress = conn.latestProgress();
    return progress?.message ?? '';
  };

  const sseStatus = (): SSEStatus => {
    const conn = sseConnection();
    if (!conn) return 'idle';
    return conn.status();
  };

  // Collect completed steps from progress events
  const completedSteps = (): Set<string> => {
    const conn = sseConnection();
    if (!conn) return new Set();
    const events = conn.events();
    const completed = new Set<string>();
    const progressEvents = events
      .filter((e) => e.type === 'progress')
      .map((e) => (e as { type: 'progress'; data: ProgressEvent }).data);

    // Mark all steps before the current one as completed
    const current = currentStep();
    for (const step of STEPS) {
      if (step.key === current) break;
      // If we've received any progress event for a later step, earlier ones are done
      const hasLaterEvent = progressEvents.some((pe) => {
        const stepIndex = STEPS.findIndex((s) => s.key === pe.step);
        const thisIndex = STEPS.findIndex((s) => s.key === step.key);
        return stepIndex > thisIndex;
      });
      if (hasLaterEvent) {
        completed.add(step.key);
      }
    }

    // Also mark steps that had a 100%-equivalent high percent
    for (const pe of progressEvents) {
      const stepIndex = STEPS.findIndex((s) => s.key === pe.step);
      const currentStepIndex = current ? STEPS.findIndex((s) => s.key === current) : -1;
      if (stepIndex >= 0 && stepIndex < currentStepIndex) {
        completed.add(pe.step);
      }
    }

    return completed;
  };

  const stepStatus = (stepKey: string): 'done' | 'in-progress' | 'pending' => {
    const conn = sseConnection();
    if (!conn) return 'pending';

    if (sseStatus() === 'complete') return 'done';
    if (completedSteps().has(stepKey)) return 'done';
    if (currentStep() === stepKey) return 'in-progress';

    // Check if this step comes before the current step
    const thisIndex = STEPS.findIndex((s) => s.key === stepKey);
    const currentIndex = currentStep()
      ? STEPS.findIndex((s) => s.key === currentStep())
      : -1;
    if (thisIndex < currentIndex) return 'done';

    return 'pending';
  };

  // Auto-redirect on completion
  createEffect(() => {
    const conn = sseConnection();
    if (!conn) return;
    const synthId = conn.completedSynthesisId();
    if (synthId) {
      setSuccess(true);
      const navTimer = setTimeout(() => {
        navigate(`/synthesis/${synthId}`);
      }, 2000);
      onCleanup(() => clearTimeout(navTimer));
    }
  });

  // Handle SSE errors
  createEffect(() => {
    const conn = sseConnection();
    if (!conn) return;
    const errMsg = conn.errorMessage();
    if (errMsg) {
      setError(errMsg);
      setGenerating(false);
    }
  });

  const handleGenerate = async () => {
    if (generating()) return;

    setGenerating(true);
    setError(null);
    setSuccess(false);

    try {
      const response = await synthesesApi.generate(selectedThemeId());
      setJobId(response.job_id);
      const url = synthesesApi.progressUrl(response.job_id);
      const conn = createSSEConnection(url);
      setSSEConnection(conn);
    } catch (err) {
      setGenerating(false);
      if (isApiError(err)) {
        setError(err.message);
      } else {
        setError(t('generate.error'));
      }
    }
  };

  const handleStop = async () => {
    const id = jobId();
    if (!id) return;
    try {
      await synthesesApi.stop(id);
    } catch {
      // ignore errors — the pipeline will stop on its own
    }
  };

  const handleRetry = () => {
    // Close existing connection
    const conn = sseConnection();
    if (conn) {
      conn.close();
    }
    setSSEConnection(null);
    setJobId(null);
    setError(null);
    setSuccess(false);
    setGenerating(false);
  };

  const isInProgress = () => generating() && !success() && !error();

  return (
    <Show when={!loadingSettings()} fallback={<LoadingSpinner />}>
      <div class="max-w-3xl mx-auto px-4 sm:px-6 lg:px-8 py-12">
        <div class="bg-white shadow sm:rounded-lg">
          <div class="px-4 py-5 sm:p-6">
            <h3 class="text-lg leading-6 font-medium text-gray-900">
              {t('generate.title')}
            </h3>
            <div class="mt-2 max-w-xl text-sm text-gray-500">
              <p class="text-gray-600">
                {t('generate.description')}
              </p>
              <Show when={settings().ai_provider}>
                <p class="mt-2 text-sm text-gray-500">
                  <span class="font-medium text-gray-600">{t('generate.provider')}</span>{' '}
                  {providerDisplayName()}
                  {' · '}
                  <span class="font-medium text-gray-600">{t('generate.model')}</span>{' '}
                  {modelDisplayName()}
                </p>
              </Show>
              <p class="mt-2 text-xs text-gray-400">
                {t('generate.note')}
              </p>
            </div>

            {/* Theme selection */}
            <Show
              when={themes().length > 0}
              fallback={
                <div class="mt-4 bg-yellow-50 border border-yellow-200 rounded-md p-4">
                  <p class="text-sm text-yellow-800">
                    {t('generate.noThemes')}{' '}
                    <A href="/themes" class="font-medium underline hover:text-yellow-900">
                      {t('generate.createThemeLink')}
                    </A>
                  </p>
                </div>
              }
            >
              <div class="mt-4">
                <label for="theme-select" class="block text-sm font-medium text-gray-700">
                  {t('generate.selectTheme')}
                </label>
                <select
                  id="theme-select"
                  value={selectedThemeId()}
                  onInput={(e) => setSelectedThemeId(e.currentTarget.value)}
                  class="mt-1 block w-full sm:w-64 rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm"
                >
                  <For each={themes()}>
                    {(theme) => (
                      <option value={theme.id}>{theme.name}</option>
                    )}
                  </For>
                </select>
              </div>
            </Show>

            {/* No web search warning */}
            <Show when={settings().ai_provider && !hasWebSearch()}>
              <div class="mt-4 bg-yellow-50 border border-yellow-200 rounded-md p-4">
                <div class="flex">
                  <div class="flex-shrink-0">
                    <AlertTriangle class="h-5 w-5 text-yellow-600" aria-hidden="true" />
                  </div>
                  <div class="ml-3">
                    <p class="text-sm text-yellow-800">
                      {t('generate.noWebSearchWarning')}
                    </p>
                  </div>
                </div>
              </div>
            </Show>

            {/* Error display */}
            <Show when={error()}>
              <div class="mt-4 bg-red-50 border-l-4 border-red-400 p-4">
                <div class="flex">
                  <div class="flex-shrink-0">
                    <AlertCircle class="h-5 w-5 text-red-400" aria-hidden="true" />
                  </div>
                  <div class="ml-3">
                    <p class="text-sm text-red-700">{error()}</p>
                  </div>
                </div>
              </div>
            </Show>

            {/* Success display */}
            <Show when={success()}>
              <div class="mt-4 bg-green-50 border-l-4 border-green-400 p-4">
                <div class="flex">
                  <div class="flex-shrink-0">
                    <CheckCircle class="h-5 w-5 text-green-400" aria-hidden="true" />
                  </div>
                  <div class="ml-3">
                    <p class="text-sm text-green-700">{t('generate.complete')}</p>
                  </div>
                </div>
              </div>
            </Show>

            {/* Progress display */}
            <Show when={isInProgress() && sseConnection()}>
              <div class="mt-6 space-y-4">
                {/* Progress bar */}
                <div>
                  <div class="flex justify-between text-sm text-gray-600 mb-1">
                    <span>{currentMessage()}</span>
                    <span>{currentPercent()}%</span>
                  </div>
                  <div class="w-full bg-gray-200 rounded-full h-3">
                    <div
                      class="bg-indigo-600 rounded-full h-3 transition-all duration-500 ease-out"
                      style={{ width: `${currentPercent()}%` }}
                    />
                  </div>
                </div>

                {/* Step checklist */}
                <div class="space-y-3 mt-4">
                  <For each={STEPS}>
                    {(step) => {
                      const status = () => stepStatus(step.key);
                      return (
                        <div class="flex items-center gap-3">
                          <Show when={status() === 'done'}>
                            <CheckCircle class="h-5 w-5 text-green-500 flex-shrink-0" />
                          </Show>
                          <Show when={status() === 'in-progress'}>
                            <Loader2 class="h-5 w-5 text-indigo-600 animate-spin flex-shrink-0" />
                          </Show>
                          <Show when={status() === 'pending'}>
                            <Circle class="h-5 w-5 text-gray-300 flex-shrink-0" />
                          </Show>
                          <span
                            class={`text-sm ${
                              status() === 'done'
                                ? 'text-green-700'
                                : status() === 'in-progress'
                                  ? 'text-indigo-700 font-medium'
                                  : 'text-gray-400'
                            }`}
                          >
                            {t(step.label as Parameters<typeof t>[0])}
                          </span>
                        </div>
                      );
                    }}
                  </For>
                </div>

                {/* Can leave note */}
                <p class="text-xs text-gray-400 mt-4 italic">
                  {t('generate.canLeave')}
                </p>

                {/* Stop generation button */}
                <Button
                  variant="danger"
                  onClick={handleStop}
                  class="mt-4"
                >
                  {t('generate.stop')}
                </Button>
              </div>
            </Show>

            {/* Action buttons */}
            <div class="mt-5">
              <Show
                when={!error()}
                fallback={
                  <Button onClick={handleRetry}>
                    {t('generate.retry')}
                  </Button>
                }
              >
                <Button
                  onClick={handleGenerate}
                  disabled={generating() || success() || !selectedThemeId()}
                  loading={generating() && !success()}
                >
                  {generating() && !success()
                    ? t('generate.inProgress')
                    : t('generate.launch')}
                </Button>
              </Show>
            </div>
          </div>
        </div>
      </div>
    </Show>
  );
};

export default GenerateSynthesis;
