import type {
  DialogueAPI,
  SSEEvent,
  SessionConfig,
  SessionInfo,
  GreetingInfo,
  DialogueReport,
  SentenceEvaluation,
} from '@/types/englishSpeaking'

const API_BASE = 'http://localhost:8000/api/speaking/dialogue'

export class DialogueApiError extends Error {
  status: number
  constructor(message: string, status: number) {
    super(message)
    this.status = status
    this.name = 'DialogueApiError'
  }
}

// ==================== SSE 解析 ====================

async function* parseSSEStream(reader: ReadableStreamDefaultReader<Uint8Array>): AsyncGenerator<SSEEvent> {
  const decoder = new TextDecoder()
  let buffer = ''

  try {
    while (true) {
      const { done, value } = await reader.read()
      if (done) break

      buffer += decoder.decode(value, { stream: true })
      const lines = buffer.split('\n')
      buffer = lines.pop() || ''

      let eventType = ''
      let data = ''

      for (const line of lines) {
        if (line.startsWith('event:')) {
          eventType = line.slice(6).trim()
        } else if (line.startsWith('data:')) {
          data = line.slice(5).trim()
        } else if (line === '' && eventType && data) {
          try {
            const parsed = JSON.parse(data)
            if (eventType === 'transcript') {
              yield { type: 'transcript', text: parsed.text }
            } else if (eventType === 'token') {
              yield { type: 'token', text: parsed.content ?? parsed.text }
            } else if (eventType === 'done') {
              yield { type: 'done', isComplete: parsed.isComplete }
            }
          } catch {
            // skip malformed JSON
          }
          eventType = ''
          data = ''
        }
      }
    }
  } finally {
    reader.releaseLock()
  }
}

// ==================== Backend shape types ====================

interface BackendEvaluation {
  status: 'pending' | 'completed' | 'failed'
  accuracyScore: number | null
  fluencyScore: number | null
  completenessScore: number | null
  prosodyScore: number | null
  wordAnalysis: unknown
  contentFeedback: {
    highlights: string[]
    corrections: { original: string; corrected: string; explanation: string }[]
    suggestions: string[]
  } | null
}

interface BackendRound {
  round: number
  role: 'ai' | 'student'
  content: string
  audioUrl: string | null
  evaluation?: BackendEvaluation
}

interface BackendReportResponse {
  sessionId: string
  topic: string
  status: 'evaluating' | 'ready'
  rounds: BackendRound[]
  summary: string | null
}

function adaptReport(raw: BackendReportResponse): DialogueReport {
  const sentenceEvaluations: SentenceEvaluation[] = raw.rounds.map((r, idx) => ({
    id: `${raw.sessionId}-${idx}`,
    round: r.round,
    role: r.role,
    content: r.content,
    audioUrl: r.audioUrl ?? undefined,
    pronunciation: r.evaluation && r.role === 'student'
      ? {
          accuracy: r.evaluation.accuracyScore ?? 0,
          fluency: r.evaluation.fluencyScore ?? 0,
          intonation: r.evaluation.prosodyScore ?? 0,
          stress: r.evaluation.completenessScore ?? 0,
        }
      : undefined,
    feedback: r.evaluation?.contentFeedback ?? undefined,
  }))

  const studentEvals = sentenceEvaluations.filter(s => s.role === 'student' && s.pronunciation)
  const avg = studentEvals.length > 0
    ? Math.round(
        studentEvals.reduce(
          (sum, s) => sum + (s.pronunciation!.accuracy + s.pronunciation!.fluency + s.pronunciation!.intonation + s.pronunciation!.stress) / 4,
          0,
        ) / studentEvals.length,
      )
    : 0

  return {
    evaluation: {
      overallScore: avg,
      scoreLevel: avg >= 85 ? 'excellent' : avg >= 70 ? 'good' : avg >= 60 ? 'fair' : 'needsWork',
      percentile: 0,
      dimensions: { fluency: 0, interaction: 0, vocabulary: 0, grammar: 0 },
      aiComment: raw.summary ?? '',
      highlights: [],
      improvements: [],
      nextChallenge: {},
      statistics: {
        totalRounds: Math.max(...sentenceEvaluations.map(s => s.round), 0),
        averageScore: avg,
        highestScore: 0,
        highestRound: 0,
        grammarErrors: 0,
        excellentExpressions: 0,
        totalDuration: 0,
      },
      sentenceEvaluations,
    },
  }
}

// ==================== Real API ====================

export class RealDialogueAPI implements DialogueAPI {
  async createSession(config: SessionConfig): Promise<SessionInfo> {
    const res = await fetch(`${API_BASE}/session`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      credentials: 'include',
      body: JSON.stringify({
        topic: config.topic,
        totalRounds: config.totalRounds,
        roleId: config.roleId,
      }),
    })
    if (!res.ok) {
      throw new DialogueApiError(`createSession failed: ${res.status}`, res.status)
    }
    const body = await res.json()
    return {
      sessionId: body.sessionId,
      totalRounds: body.totalRounds,
      currentRound: body.currentRound,
      expiresAt: body.expiresAt ?? null,
    }
  }

  async generateGreeting(sessionId: string, signal?: AbortSignal): Promise<GreetingInfo> {
    const res = await fetch(`${API_BASE}/session/${sessionId}/greeting`, {
      method: 'POST',
      credentials: 'include',
      signal,
    })
    if (!res.ok) {
      const text = await res.text().catch(() => '')
      throw new DialogueApiError(
        `greeting failed: ${res.status}${text ? ` (${text.slice(0, 100)})` : ''}`,
        res.status,
      )
    }
    const body = await res.json()
    return { aiMessage: body.aiMessage }
  }

  async *speak(sessionId: string, audioBlob: Blob, signal: AbortSignal): AsyncGenerator<SSEEvent> {
    const formData = new FormData()
    formData.append('sessionId', sessionId)
    formData.append('audio', audioBlob, 'recording.webm')

    const res = await fetch(`${API_BASE}/speak`, {
      method: 'POST',
      credentials: 'include',
      body: formData,
      signal,
    })
    if (!res.ok) throw new Error(`speak failed: ${res.status}`)
    if (!res.body) throw new Error('No response body')

    yield* parseSSEStream(res.body.getReader())
  }

  async getReport(sessionId: string): Promise<DialogueReport> {
    const res = await fetch(`${API_BASE}/report?sessionId=${encodeURIComponent(sessionId)}`, {
      credentials: 'include',
    })
    if (!res.ok) throw new Error(`getReport failed: ${res.status}`)
    const raw: BackendReportResponse = await res.json()
    return adaptReport(raw)
  }
}

// ==================== Mock API ====================

const MOCK_AI_REPLIES = [
  'Pandas are adorable! Have you seen them at the zoo?',
  "That's great! What do pandas like to eat?",
  "Bamboo is their favorite! You did a wonderful job talking about animals today!",
]

export class MockDialogueAPI implements DialogueAPI {
  private roundIndex = 0

  async createSession(_config: SessionConfig): Promise<SessionInfo> {
    this.roundIndex = 0
    return {
      sessionId: 'mock-session-' + Date.now(),
      totalRounds: _config.totalRounds,
      currentRound: 1,
      expiresAt: null,
    }
  }

  async generateGreeting(_sessionId: string, signal?: AbortSignal): Promise<GreetingInfo> {
    await delay(300, signal)
    return { aiMessage: "Hi! What's your favorite animal?" }
  }

  async *speak(_sessionId: string, _audioBlob: Blob, signal: AbortSignal): AsyncGenerator<SSEEvent> {
    const mockStudentTexts = [
      'I like pandas. They are very cute!',
      'Yes, I went to the zoo last month.',
      'They like to eat bamboo.',
    ]

    // Simulate transcript
    await delay(300, signal)
    yield { type: 'transcript', text: mockStudentTexts[this.roundIndex] || 'I think so.' }

    // Simulate token streaming
    const aiReply = MOCK_AI_REPLIES[this.roundIndex] || 'That is very interesting!'
    const words = aiReply.split(' ')
    for (const word of words) {
      await delay(80, signal)
      yield { type: 'token', text: word + ' ' }
    }

    this.roundIndex++
    const isComplete = this.roundIndex >= MOCK_AI_REPLIES.length

    await delay(100, signal)
    yield { type: 'done', isComplete }
  }

  async getReport(_sessionId: string): Promise<DialogueReport> {
    return {
      evaluation: {
        overallScore: 85,
        scoreLevel: 'good',
        percentile: 78,
        dimensions: { fluency: 82, interaction: 88, vocabulary: 76, grammar: 90 },
        aiComment: 'Great job! Your pronunciation was clear and your responses were relevant.',
        highlights: ['Clear pronunciation', 'Good use of complete sentences', 'Natural flow'],
        improvements: ['Try more adjectives', 'Practice linking words', 'Expand vocabulary'],
        nextChallenge: { difficulty: 'Medium', unlockedTopic: 'My Dream Job' },
        statistics: {
          totalRounds: 3, averageScore: 83, highestScore: 92,
          highestRound: 2, grammarErrors: 2, excellentExpressions: 4, totalDuration: 180,
        },
        sentenceEvaluations: [],
      },
    }
  }
}

export function createDialogueApi(mode: 'preview' | 'real'): DialogueAPI {
  return mode === 'real' ? new RealDialogueAPI() : new MockDialogueAPI()
}

function delay(ms: number, signal?: AbortSignal): Promise<void> {
  return new Promise((resolve, reject) => {
    if (signal?.aborted) { reject(new DOMException('Aborted', 'AbortError')); return }
    const timer = setTimeout(resolve, ms)
    signal?.addEventListener('abort', () => { clearTimeout(timer); reject(new DOMException('Aborted', 'AbortError')) }, { once: true })
  })
}
