'use client';

import { useCallback, useRef, useState } from 'react';
import { ApiCode } from '@pac/types';
import { env } from '@/lib/env';
import { tryRefresh } from '@/lib/api-client';
import { useAuthStore } from '@/stores/auth-store';

/**
 * 流式重新生成 plan 摘要(3 type 一次性生成)。
 *
 * SSE 帧:
 *   start    {invocationId, modelId, promptVersion}
 *   partial  {structured:{onePage,medicalRecord,treatmentChain}}     ← 多次
 *   done     {agentInvocationId, source, summaries:[3 行], costYuan, ...}
 *   error    {message}
 */

export interface SummaryParts {
  onePage: string;
  medicalRecord: string;
  treatmentChain: string;
}

export type SummaryStreamState =
  | { status: 'idle' }
  | {
      status: 'streaming';
      partial: Partial<SummaryParts>;
      modelId?: string;
      invocationId?: string;
    }
  | {
      status: 'done';
      parts: SummaryParts;
      source: 'agent' | 'template_fallback';
      costYuan: number;
      promptTokens: number;
      completionTokens: number;
      fallbackReason?: string;
    }
  | { status: 'error'; message: string };

export interface UseSummaryStream {
  state: SummaryStreamState;
  regenerate: (planId: string, options?: { model?: string }) => Promise<void>;
  abort: () => void;
  reset: () => void;
}

export function useSummaryStream(): UseSummaryStream {
  const [state, setState] = useState<SummaryStreamState>({ status: 'idle' });
  const abortRef = useRef<AbortController | null>(null);

  const reset = useCallback(() => setState({ status: 'idle' }), []);
  const abort = useCallback(() => {
    abortRef.current?.abort();
    abortRef.current = null;
  }, []);

  const regenerate = useCallback(async (planId: string, options?: { model?: string }) => {
    abortRef.current?.abort();
    const controller = new AbortController();
    abortRef.current = controller;

    const url = new URL(
      `/pac/v1/plans/${encodeURIComponent(planId)}/summary:stream`,
      env.apiBaseUrl,
    );
    if (options?.model) url.searchParams.set('model', options.model);

    setState({ status: 'streaming', partial: {} });

    try {
      const openStream = async (retried: boolean): Promise<Response> => {
        const token = useAuthStore.getState().accessToken;
        if (!token) throw new Error('未鉴权 — 请从宿主系统重新打开页面');
        const res = await fetch(url, {
          method: 'GET',
          headers: { Authorization: `Bearer ${token}`, Accept: 'text/event-stream' },
          signal: controller.signal,
        });
        if (!res.ok || !res.headers.get('content-type')?.includes('text/event-stream')) {
          if (!retried && useAuthStore.getState().canRefresh()) {
            const cloned = res.clone();
            let isAuth = !res.ok && (res.status === 401 || res.status === 403);
            try {
              const body = await cloned.json();
              if (
                body?.code === ApiCode.AUTH_TOKEN_EXPIRED ||
                body?.code === ApiCode.AUTH_TOKEN_MISSING ||
                body?.code === ApiCode.AUTH_TOKEN_INVALID
              ) {
                isAuth = true;
              }
            } catch {}
            if (isAuth) {
              const refreshed = await tryRefresh();
              if (refreshed) return openStream(true);
            }
          }
          const text = await res.text().catch(() => '');
          throw new Error(`HTTP ${res.status} ${text.slice(0, 200) || res.statusText}`);
        }
        return res;
      };

      const res = await openStream(false);
      if (!res.body) throw new Error('SSE 响应无 body');

      const reader = res.body.pipeThrough(new TextDecoderStream()).getReader();
      let buffer = '';

      while (true) {
        const { value, done } = await reader.read();
        if (done) break;
        buffer += value;
        let sep: number;
        while ((sep = buffer.indexOf('\n\n')) !== -1) {
          const frame = buffer.slice(0, sep);
          buffer = buffer.slice(sep + 2);
          const evt = parseSseFrame(frame);
          if (!evt) continue;

          if (evt.type === 'start') {
            setState({
              status: 'streaming',
              partial: {},
              modelId: evt.modelId,
              invocationId: evt.invocationId,
            });
          } else if (evt.type === 'partial') {
            setState((prev) =>
              prev.status === 'streaming'
                ? { ...prev, partial: evt.structured }
                : prev,
            );
          } else if (evt.type === 'done') {
            setState({
              status: 'done',
              parts: evt.structured,
              source: evt.source,
              costYuan: evt.costYuan,
              promptTokens: evt.promptTokens,
              completionTokens: evt.completionTokens,
              fallbackReason: evt.fallbackReason,
            });
          } else if (evt.type === 'error') {
            setState({ status: 'error', message: evt.message });
          }
        }
      }
    } catch (err) {
      if (controller.signal.aborted) {
        setState({ status: 'idle' });
        return;
      }
      setState({
        status: 'error',
        message: err instanceof Error ? err.message : String(err),
      });
    } finally {
      if (abortRef.current === controller) abortRef.current = null;
    }
  }, []);

  return { state, regenerate, abort, reset };
}

// ─────────────────────────────────────────────

interface SseStartEvent {
  type: 'start';
  invocationId: string;
  modelId: string;
  promptVersion: string;
}
interface SsePartialEvent {
  type: 'partial';
  structured: Partial<SummaryParts>;
}
interface SseDoneEvent {
  type: 'done';
  agentInvocationId: string;
  source: 'agent' | 'template_fallback';
  costYuan: number;
  promptTokens: number;
  completionTokens: number;
  structured: SummaryParts;
  summaries: Array<{ type: string; planSummaryId: string | null; content: string }>;
  fallbackReason?: string;
}
interface SseErrorEvent {
  type: 'error';
  message: string;
}
type SseEvent = SseStartEvent | SsePartialEvent | SseDoneEvent | SseErrorEvent;

function parseSseFrame(frame: string): SseEvent | null {
  const dataLines: string[] = [];
  for (const line of frame.split('\n')) {
    if (line.startsWith('data:')) dataLines.push(line.slice(5).trimStart());
  }
  if (dataLines.length === 0) return null;
  try {
    return JSON.parse(dataLines.join('\n')) as SseEvent;
  } catch {
    return null;
  }
}
