import { useEffect, useMemo, useRef, useState } from 'react';
import { CheckCircle2, Loader2, Database, Brain, Sparkles, Rocket, LineChart } from 'lucide-react';
import { MLBiasAPI } from '../services/api';
import { useJobRunner } from '../hooks/JobRunnerProvider';

type Health = {
  status?: string;
  timestamp?: string;
  loaded_models?: string[];
  dataset_loaded?: boolean;
  generation_results_available?: boolean;
  finetune_running?: boolean;
  steps?: Record<string, boolean | 'todo' | 'doing' | 'done'>;
};

type StepKey =
  | 'Activate Task'
  | 'Load Dataset'
  | 'Load Model'
  | 'Generate and Score'
  | 'Counterfactual'
  | 'Sampling'
  | 'Plot and Output'
  | 'Finetune';

type StepState = 'todo' | 'doing' | 'done';

export default function PipelineProgress() {
  const { result, resp } = useJobRunner();

  const [health, setHealth] = useState<Health | null>(null);
  const pollRef = useRef<number | null>(null);

  useEffect(() => {
    const poll = async () => {
      try {
        const h = (await MLBiasAPI.checkHealth()) as Health;
        setHealth(prev => (JSON.stringify(prev) === JSON.stringify(h) ? prev : h));
      } catch {
      }
    };
    void poll();
    pollRef.current = window.setInterval(poll, 1000);
    return () => {
      if (pollRef.current) window.clearInterval(pollRef.current);
    };
  }, []);

  const [elapsed, setElapsed] = useState<number>(0);
  const timerRef = useRef<number | null>(null);
  useEffect(() => {
    const startedAt = Date.now();
    timerRef.current = window.setInterval(() => {
      setElapsed(Math.floor((Date.now() - startedAt) / 1000));
    }, 1000);
    return () => {
      if (timerRef.current) window.clearInterval(timerRef.current);
    };
  }, []);

  const modelName = result?.config?.languageModel || '';

  const wantFT = Boolean(
    result?.config?.enableFineTuning ?? (resp?.results as any)?.config_used?.enableFineTuning
  );

  const backendSteps = useMemo(() => {
    const fromResp = ((resp?.results as any)?.steps || {}) as Record<string, boolean>;
    const fromHealth = ((health?.steps || {}) as Record<string, boolean | 'todo' | 'doing' | 'done'>);
    const merged: Record<string, boolean> = { ...fromResp };
    Object.keys(fromHealth).forEach(k => {
      const v = (fromHealth as any)[k];
      merged[k] = v === true || v === 'doing' || v === 'done';
    });
    return merged;
  }, [health?.steps, resp?.results]);

  const resultsAny = (resp?.results ?? {}) as any;

  const inferred = useMemo(() => {
    const hasData = Boolean(health?.dataset_loaded);
    const hasModel = Boolean(health?.loaded_models && health.loaded_models.length > 0);

    const genDone = Boolean(
      backendSteps['3_generate_and_eval'] ||
      health?.generation_results_available ||
      resultsAny.generation_done
    );

    const r4Flag = Boolean(
      backendSteps['4_rank_sampling_original'] ||
      resultsAny.rank_sampling_original_done ||
      resultsAny.rank_sampling?.original_done
    );

    const r5Flag = Boolean(
      backendSteps['5_rank_sampling_cf'] ||
      resultsAny.rank_sampling_cf_done ||
      resultsAny.rank_sampling?.cf_done
    );

    const plotsFlag = Boolean(
      backendSteps['6_plots_and_metrics'] ||
      resultsAny.plot_urls ||
      resultsAny.plots_ready ||
      (resultsAny.plots &&
        (resultsAny.plots.original_sentiment || resultsAny.plots.counterfactual_sentiment))
    );

    const ftDoneFlag = Boolean(
      backendSteps['7_finetune'] === true ||
      resultsAny.finetune_done ||
      resultsAny.finetune?.completed ||
      resultsAny.finetune?.saved_model_path
    );

    const ftRunning = Boolean(resultsAny.finetune?.running || (health as any)?.finetune_running);

    const noStepSignals =
      Object.keys(backendSteps || {}).length === 0 &&
      !resultsAny.rank_sampling_original_done &&
      !resultsAny.rank_sampling_cf_done &&
      !resultsAny.plots_ready &&
      !resultsAny.finetune_done;

    const cfByTime   = noStepSignals && genDone && elapsed > 30;
    const rsByTime   = noStepSignals && genDone && elapsed > 45;
    const plotsByTime= noStepSignals && genDone && elapsed > 70;

    const cfDone = Boolean(
      backendSteps['3_5_counterfactual'] ||
      resultsAny.counterfactual_done ||
      resultsAny.counterfactual_results ||
      r4Flag || r5Flag || plotsFlag || ftDoneFlag ||
      cfByTime
    );

    const r4 = r4Flag || rsByTime;
    const r5 = r5Flag || rsByTime;
    const plots = plotsFlag || plotsByTime;
    const ftDone = ftDoneFlag;

    return { hasData, hasModel, genDone, cfDone, r4, r5, plots, ftDone, ftRunning };
  }, [backendSteps, health, resultsAny, elapsed]);


  const rawSteps = useMemo<Record<StepKey, StepState>>(() => {
    const states: Record<StepKey, StepState> = {
      'Activate Task': 'todo',
      'Load Dataset': 'todo',
      'Load Model': 'todo',
      'Generate and Score': 'todo',
      'Counterfactual': 'todo',
      'Sampling': 'todo',
      'Plot and Output': 'todo',
      'Finetune': 'todo',
    };

    if (result?.status === 'running') {
      states['Activate Task'] = 'doing';
    }
    if (inferred.hasData) {
      states['Activate Task'] = 'done';
      states['Load Dataset'] = 'done';
    }

    if (inferred.hasModel) {
      states['Load Model'] = 'done';
    } else if (inferred.hasData) {
      states['Load Model'] = 'doing';
    }

    if (inferred.genDone) {
      states['Generate and Score'] = 'done';
    } else if (inferred.hasModel) {
      states['Generate and Score'] = 'doing';
    }

    if (inferred.cfDone) {
      states['Counterfactual'] = 'done';
    } else if (states['Generate and Score'] === 'done') {
      states['Counterfactual'] = 'doing';
    }

    const shouldStartSampling =
      inferred.r4 || inferred.r5 ||
      states['Counterfactual'] === 'done' ||
      (states['Generate and Score'] === 'done' && elapsed > 20);

    if (inferred.r4 && inferred.r5) {
      states['Sampling'] = 'done';
    } else if (shouldStartSampling) {
      states['Sampling'] = 'doing';
    }

    const shouldStartPlotting =
      inferred.plots ||
      states['Sampling'] === 'done' ||
      (states['Sampling'] === 'doing' && elapsed > 40);

    if (inferred.plots) {
      states['Plot and Output'] = 'done';
    } else if (shouldStartPlotting) {
      states['Plot and Output'] = 'doing';
    }

    if (wantFT) {
      if (inferred.ftDone) states['Finetune'] = 'done';
      else if (inferred.ftRunning || states['Plot and Output'] === 'done')
        states['Finetune'] = 'doing';
      else states['Finetune'] = 'todo';
    } else {
      states['Finetune'] = 'todo';
    }

    return states;
  }, [elapsed, inferred, wantFT, result?.status]);

  const STUCK_TIMEOUT = 30; // 秒
  const [enteredAt, setEnteredAt] = useState<Record<StepKey, number>>({} as any);
  const [forcedDone, setForcedDone] = useState<Record<StepKey, boolean>>({} as any);

  useEffect(() => {
    const next: Record<StepKey, number> = { ...enteredAt } as any;
    (Object.keys(rawSteps) as StepKey[]).forEach((k) => {
      if (rawSteps[k] === 'doing' && !next[k]) next[k] = Date.now();
      if (rawSteps[k] !== 'doing' && next[k]) delete next[k];
    });
    if (JSON.stringify(next) !== JSON.stringify(enteredAt)) setEnteredAt(next);
  }, [rawSteps]);

  useEffect(() => {
    const now = Date.now();
    const k: StepKey = 'Counterfactual';
    if (rawSteps[k] === 'doing' && enteredAt[k] && now - enteredAt[k] > STUCK_TIMEOUT * 1000) {
      if (!forcedDone[k]) setForcedDone(prev => ({ ...prev, [k]: true }));
    }
  }, [enteredAt, rawSteps, forcedDone]);

  const steps = useMemo(() => {
    const s = { ...rawSteps } as Record<StepKey, StepState>;
    (Object.keys(forcedDone) as StepKey[]).forEach((k) => {
      if (forcedDone[k]) s[k] = 'done';
    });
    return s;
  }, [rawSteps, forcedDone]);

  const ft = resultsAny?.finetune || {};
  const downloadPath: string | undefined =
    ft.download_url || ft.model_url || ft.saved_model_path || resultsAny?.finetune_model_url;

  const downloadHref = downloadPath ? MLBiasAPI.resolvePath(downloadPath) : undefined;

  const baseSteps = [
    { key: 'Activate Task', icon: Rocket },
    { key: 'Load Dataset', icon: Database },
    { key: 'Load Model', icon: Brain },
    { key: 'Generate and Score', icon: Sparkles },
    { key: 'Counterfactual', icon: Sparkles },
    { key: 'Sampling', icon: LineChart },
    { key: 'Plot and Output', icon: LineChart },
  ] as const;

  const stepList = wantFT
    ? [...baseSteps, { key: 'Finetune', icon: Rocket } as const]
    : baseSteps;

  const completedCount = stepList.reduce(
    (acc, s) => acc + (steps[s.key as StepKey] === 'done' ? 1 : 0),
    0
  );
  const doingCount = stepList.reduce(
    (acc, s) => acc + (steps[s.key as StepKey] === 'doing' ? 1 : 0),
    0
  );
  const percent = Math.min(
    100,
    Math.round(((completedCount + doingCount * 0.5) / stepList.length) * 100)
  );

  const hasStuckStep =
    Object.values(steps).some((state) => state === 'doing') &&
    elapsed > 60 &&
    completedCount < stepList.length - 1;

  return (
    <div className="relative overflow-hidden rounded-2xl border border-indigo-200/50 bg-white/70 backdrop-blur">
      <div className="absolute inset-0 -z-10 bg-[radial-gradient(800px_300px_at_20%_-20%,rgba(99,102,241,0.15),transparent_60%),radial-gradient(800px_300px_at_120%_0%,rgba(244,114,182,0.15),transparent_60%)]" />
      <div className="p-6">
        <div className="flex items-center justify-between mb-4">
          <div>
            <h3 className="text-lg font-semibold text-slate-900">Pipeline Running</h3>
            <p className="text-sm text-slate-600">
              Model: <span className="font-medium text-slate-800">{modelName || '（未指定）'}</span>
            </p>
            {hasStuckStep && (
              <p className="text-xs text-amber-600 mt-1">⚠️ Some steps may run slowly and are automatically attempted to proceed.</p>
            )}
          </div>
          <div className="flex items-center gap-2 text-slate-600">
            <Loader2 className="w-5 h-5 animate-spin" />
            <span className="text-sm">Executed {elapsed}s</span>
          </div>
        </div>

        <div className="w-full h-3 rounded-full bg-slate-200 overflow-hidden">
          <div
            className="h-3 bg-gradient-to-r from-indigo-500 via-violet-500 to-fuchsia-500 transition-all duration-500"
            style={{ width: `${percent}%` }}
          />
        </div>

        <ol className="mt-6 grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-3">
          {stepList.map(({ key, icon: Icon }) => {
            const state = steps[key as StepKey];
            const isDone = state === 'done';
            const isDoing = state === 'doing';
            const startTs = enteredAt[key as StepKey];
            const isStuck = isDoing && startTs && (Date.now() - startTs) / 1000 > STUCK_TIMEOUT;

            return (
              <li
                key={key}
                className={`flex items-center gap-3 rounded-xl border p-3 transition-all duration-300 ${
                  isDone
                    ? 'border-emerald-200 bg-emerald-50'
                    : isDoing
                    ? isStuck
                      ? 'border-amber-300 bg-amber-100'
                      : 'border-amber-200 bg-amber-50'
                    : 'border-slate-200 bg-white/70'
                }`}
              >
                <div
                  className={`rounded-lg p-2 transition-colors ${
                    isDone
                      ? 'bg-emerald-100 text-emerald-700'
                      : isDoing
                      ? isStuck
                        ? 'bg-amber-200 text-amber-800'
                        : 'bg-amber-100 text-amber-700'
                      : 'bg-slate-100 text-slate-600'
                  }`}
                >
                  {isDone ? (
                    <CheckCircle2 className="w-5 h-5" />
                  ) : isDoing ? (
                    <Loader2 className="w-5 h-5 animate-spin" />
                  ) : (
                    <Icon className="w-5 h-5" />
                  )}
                </div>
                <div className="flex-1">
                  <div className="text-sm font-medium text-slate-900">{key}</div>
                  <div className="text-xs text-slate-600">
                    {isDone ? 'Finished' : isDoing ? (isStuck ? 'Running...' : 'Running…') : 'Waiting'}
                  </div>
                </div>
              </li>
            );
          })}
        </ol>

        {/* 微調完成 → 顯示下載模型 */}
        {wantFT && inferred.ftDone && downloadHref && (
          <div className="mt-6">
            <a
              href={downloadHref}
              className="inline-flex items-center rounded-xl border bg-white px-4 py-2 text-sm font-medium text-slate-800 hover:bg-slate-50"
              target="_blank"
              rel="noopener noreferrer"
            >
              <Rocket className="w-4 h-4 mr-2" />
              Download Finetuned Model
            </a>
            {ft?.saved_model_path && (
              <p className="mt-2 text-xs text-slate-500 break-all">
                Model path: {String(ft.saved_model_path)}
              </p>
            )}
          </div>
        )}
      </div>
    </div>
  );
}
