import { useState, useEffect, useRef, useCallback } from 'react';
import { useNavigate, useLocation, useParams } from 'react-router';
import { useCreateClinic } from '@/shared/api/generated/clinics/clinics';
import { useStartAnalysis, getAnalysisStatus } from '@/shared/api/generated/analysis/analysis';
import { AnalysisStatus } from '@/shared/api/model/analysisStatus';
import { sleep } from '@/shared/lib/utils';

// AnalyzePayload(MultiChannelInput) 와 호환되도록 string | string[] 둘 다 허용.
// 백엔드 SDK 는 단일 string 만 받으므로 내부에서 첫 요소만 추출.
export type ManualChannels = {
  instagram?: string | string[];
  youtube?: string | string[];
  facebook?: string | string[];
  naverBlog?: string | string[];
  gangnamUnni?: string | string[];
};

function pickFirst(v: string | string[] | undefined): string | null {
  if (!v) return null;
  if (Array.isArray(v)) return v[0] ?? null;
  return v;
}

export type Phase =
  | 'resuming'
  | 'discovering'
  | 'collecting'
  | 'generating'
  | 'planning'
  | 'complete';

const SESSION_KEYS = {
  reportId: 'infinith_reportId',
  clinicId: 'infinith_clinicId',
  runId: 'infinith_runId',
  url: 'infinith_url',
};

function saveSession(data: { reportId: string; clinicId?: string; runId?: string; url?: string }) {
  sessionStorage.setItem(SESSION_KEYS.reportId, data.reportId);
  if (data.clinicId) sessionStorage.setItem(SESSION_KEYS.clinicId, data.clinicId);
  if (data.runId) sessionStorage.setItem(SESSION_KEYS.runId, data.runId);
  if (data.url) sessionStorage.setItem(SESSION_KEYS.url, data.url);
}

function loadSession() {
  return {
    reportId: sessionStorage.getItem(SESSION_KEYS.reportId),
    clinicId: sessionStorage.getItem(SESSION_KEYS.clinicId),
    runId: sessionStorage.getItem(SESSION_KEYS.runId),
    url: sessionStorage.getItem(SESSION_KEYS.url),
  };
}

function clearSession() {
  Object.values(SESSION_KEYS).forEach((k) => sessionStorage.removeItem(k));
}

// 백엔드 6단계(discovering/collecting/analyzing/planning/completed/failed)를
// UI 4단계로 매핑. analyzing + planning 은 사용자 입장에서 같은 "생성" 단계라 합침.

interface UseAnalysisPipelineResult {
  phase: Phase;
  error: string | null;
  errorCode: string | null;
  errorDomain: string | null;
  errorDetails: Record<string, string> | null;
  url: string | undefined;
  sessionUrl: string | null;
  handleRetry: () => void;
  handleAbort: () => void;
}

export function useAnalysisPipeline(): UseAnalysisPipelineResult {
  const [phase, setPhase] = useState<Phase>('discovering');
  const [error, setError] = useState<string | null>(null);
  const [errorCode, setErrorCode] = useState<string | null>(null);
  const [errorDomain, setErrorDomain] = useState<string | null>(null);
  const [errorDetails, setErrorDetails] = useState<Record<string, string> | null>(null);

  const navigate = useNavigate();
  const location = useLocation();
  const { reportId: urlReportId } = useParams<{ reportId?: string }>();

  // ?run_id= 쿼리로 진입하면 createClinic/startAnalysis 건너뛰고 폴링부터 시작.
  // path 파라미터(/report/loading/:reportId) 와 동일 의미로 취급.
  const queryRunId = new URLSearchParams(location.search).get('run_id');
  const resumeRunId = urlReportId || queryRunId || undefined;

  const locState = (location.state as { url?: string; manualChannels?: ManualChannels }) ?? {};
  const url = locState.url;
  const manualChannels = locState.manualChannels;

  const hasStarted = useRef(false);
  const { mutateAsync: createClinicAsync } = useCreateClinic();
  const { mutateAsync: startAnalysisAsync } = useStartAnalysis();

  const runPipeline = useCallback(
    async (
      startUrl?: string,
      resumeFrom?: { reportId: string; clinicId?: string; runId?: string },
    ) => {
      try {
        let reportId = resumeFrom?.reportId || '';
        let clinicId = resumeFrom?.clinicId;
        let runId = resumeFrom?.runId;

        const mapPhase = (s: AnalysisStatus): Phase => {
          if (s === AnalysisStatus.discovering) return 'discovering';
          if (s === AnalysisStatus.collecting) return 'collecting';
          if (s === AnalysisStatus.analyzing) return 'generating';
          if (s === AnalysisStatus.planning) return 'planning';
          if (s === AnalysisStatus.completed) return 'complete';
          return 'discovering';
        };

        // Phase 1: 신규 분석이면 createClinic → startAnalysis
        if (!resumeFrom?.runId) {
          if (!startUrl) throw new Error('No URL provided');
          setPhase('discovering');

          // 1-a) 클리닉 등록
          const clinicRes = await createClinicAsync({ data: { url: startUrl } });
          if (clinicRes.status !== 201) throw new Error('병원 등록에 실패했습니다.');
          clinicId = clinicRes.data.id;

          // 1-b) 분석 시작
          const startRes = await startAnalysisAsync({
            data: {
              clinic_id: clinicId,
              channels: {
                youtube: pickFirst(manualChannels?.youtube),
                instagram: pickFirst(manualChannels?.instagram),
                facebook: pickFirst(manualChannels?.facebook),
                naver_blog: pickFirst(manualChannels?.naverBlog),
                gangnam_unni: pickFirst(manualChannels?.gangnamUnni),
              },
            },
          });
          if (startRes.status !== 202) throw new Error('분석 시작에 실패했습니다.');
          runId = startRes.data.analysis_run_id;
          reportId = runId; // 백엔드: analysis_run_id 가 report 조회 키와 동일

          saveSession({ reportId, clinicId, runId, url: startUrl });
          // 새로고침·공유 시 createClinic/startAnalysis 재호출 없이 폴링만 재개되도록
          // run_id 를 쿼리 파라미터로 URL 에 박아둔다.
          window.history.replaceState(null, '', `/report/loading?run_id=${reportId}`);
        }

        if (!runId) throw new Error('runId 없음');

        // Phase 2-4: 상태 폴링 (1.5초 간격, 최대 ~3분)
        const maxAttempts = 120;
        let attempts = 0;
        while (attempts < maxAttempts) {
          attempts++;
          const statusRes = await getAnalysisStatus(runId);
          if (statusRes.status !== 200) throw new Error('분석 상태 조회에 실패했습니다.');
          const statusData = statusRes.data;
          setPhase(mapPhase(statusData.status));
          if (statusData.channel_errors && Object.keys(statusData.channel_errors).length > 0) {
            console.warn('[pipeline] Partial failures:', statusData.channel_errors);
          }
          if (statusData.status === AnalysisStatus.completed) break;
          if (statusData.status === AnalysisStatus.failed) {
            throw new Error('파이프라인 실패: failed');
          }
          await sleep(1500);
        }

        setPhase('complete');
        clearSession();
        navigate(`/report/${reportId}`, { replace: true });
      } catch (err) {
        const msg = err instanceof Error ? err.message : 'An error occurred';
        setError(msg);
        if (err && typeof err === 'object' && 'code' in err) {
          setErrorCode((err as { code?: string }).code || null);
        }
        if (err && typeof err === 'object' && 'domain' in err) {
          setErrorDomain((err as { domain?: string }).domain || null);
        }
      }
    },
    [navigate, manualChannels, createClinicAsync, startAnalysisAsync],
  );

  const handleRetry = useCallback(() => {
    setError(null);
    setErrorDetails(null);
    const session = loadSession();
    if (session.reportId) {
      runPipeline(undefined, {
        reportId: session.reportId,
        clinicId: session.clinicId || undefined,
        runId: session.runId || undefined,
      });
    } else if (url || session.url) {
      hasStarted.current = false;
      runPipeline(url || session.url || undefined);
    }
  }, [url, runPipeline]);

  const handleAbort = useCallback(() => {
    clearSession();
    navigate('/', { replace: true });
  }, [navigate]);

  useEffect(() => {
    if (hasStarted.current) return;
    hasStarted.current = true;

    // 1. URL 파라미터/쿼리 resume (예: /report/loading/abc-123 또는 /report/loading?run_id=abc-123)
    if (resumeRunId) {
      setPhase('resuming');
      const session = loadSession();
      const runId = session.runId || resumeRunId;
      getAnalysisStatus(runId)
        .then((res) => {
          if (res.status !== 200) throw new Error('status fetch failed');
          const status = res.data;
          if (status.status === AnalysisStatus.completed) {
            navigate(`/report/${resumeRunId}`, { replace: true });
            return;
          }
          if (status.status === AnalysisStatus.failed) {
            setError('Data collection failed. Please retry.');
            setPhase('collecting');
            return;
          }
          saveSession({
            reportId: resumeRunId,
            clinicId: session.clinicId || undefined,
            runId,
            url: session.url || undefined,
          });
          runPipeline(undefined, {
            reportId: resumeRunId,
            clinicId: session.clinicId || undefined,
            runId,
          });
        })
        .catch(() => {
          setError('Could not resume analysis. Please try again.');
        });
      return;
    }

    // 2. sessionStorage resume
    const session = loadSession();
    if (session.reportId && !url) {
      setPhase('resuming');
      const runId = session.runId || session.reportId;
      getAnalysisStatus(runId)
        .then((res) => {
          if (res.status !== 200) throw new Error('status fetch failed');
          const status = res.data;
          if (status.status === AnalysisStatus.completed) {
            clearSession();
            navigate(`/report/${session.reportId}`, { replace: true });
            return;
          }
          runPipeline(undefined, {
            reportId: session.reportId!,
            clinicId: session.clinicId || undefined,
            runId,
          });
        })
        .catch(() => {
          clearSession();
          navigate('/', { replace: true });
        });
      return;
    }

    // 3. URL 없이 진입 → 홈으로
    if (!url) {
      navigate('/', { replace: true });
      return;
    }

    // 4. 신규 분석 시작
    runPipeline(url);
  }, [url, resumeRunId, navigate, runPipeline]);

  return {
    phase,
    error,
    errorCode,
    errorDomain,
    errorDetails,
    url,
    sessionUrl: loadSession().url,
    handleRetry,
    handleAbort,
  };
}
