import { useEffect, useMemo, useState } from 'react';
import { fetchEntries, fetchEntry } from '../api';
import type { EntryDetail, EntryItems, EntrySummary, QuizQuestionWithEntry } from '../types';
import VideoPlayer from './VideoPlayer';

type Mode = 'all' | 'selected' | 'single';

const TOTAL_QUESTIONS = 10;

interface QuizRunnerProps {
  defaultMode?: Mode;
  defaultEntryId?: string;
  autoStart?: boolean;
}

interface TargetHit {
  group: string;
  item: Record<string, any>;
}

function shuffle<T>(list: T[]) {
  const copy = [...list];
  for (let i = copy.length - 1; i > 0; i -= 1) {
    const j = Math.floor(Math.random() * (i + 1));
    [copy[i], copy[j]] = [copy[j], copy[i]];
  }
  return copy;
}

const normalize = (val: any) => (val === undefined || val === null ? '' : String(val).trim());

function resolveTargets(question: QuizQuestionWithEntry): TargetHit[] {
  const targetIds = new Set((question.targets || []).map((t) => normalize(t)));
  const groups: { label: string; items: any[] }[] = [
    { label: 'Grammar', items: question.items?.grammar || [] },
    { label: 'Vocabulary', items: question.items?.vocab || [] },
    { label: 'Key Phrases', items: question.items?.key_phrases || [] },
    { label: 'Conversation', items: question.items?.conversation || [] },
  ];

  const found: TargetHit[] = [];
  groups.forEach(({ label, items }) => {
    items.forEach((item) => {
      if (item?.id && targetIds.has(normalize(item.id))) {
        found.push({ group: label, item });
      }
    });
  });

  return found;
}

function deriveCorrectText(question: QuizQuestionWithEntry) {
  const options: any[] = Array.isArray(question.payload?.options) ? question.payload?.options : [];
  if (typeof question.answer?.correct_index === 'number' && options[question.answer.correct_index]) {
    return options[question.answer.correct_index];
  }
  if (question.answer?.correct_text) return question.answer.correct_text;
  if (question.payload?.blanked) return question.payload.blanked;
  const pairs = Array.isArray(question.payload?.pairs) ? question.payload.pairs : [];
  if (pairs.length) {
    return pairs.map((p: any) => `${p.left} → ${p.right}`).join(' | ');
  }
  return '';
}

function checkClozeAnswer(question: QuizQuestionWithEntry, response: string) {
  if (!response) return false;
  const expected = [question.answer?.correct_text, question.answer?.correct, question.payload?.blanked].filter(Boolean).map(normalize);
  const answer = normalize(response);
  return expected.some((val) => val === answer || val.toLowerCase() === answer.toLowerCase());
}

function checkMatchAnswer(question: QuizQuestionWithEntry, response: Record<number, string> | null) {
  const pairs: any[] = Array.isArray(question.payload?.pairs) ? question.payload.pairs : [];
  if (!pairs.length) return false;
  return pairs.every((pair, idx) => {
    const expected = normalize(pair.right);
    const user = normalize(response?.[idx]);
    return expected === user;
  });
}

function checkMcAnswer(question: QuizQuestionWithEntry, response: number | null) {
  if (typeof response !== 'number') return false;
  if (typeof question.answer?.correct_index !== 'number') return false;
  return response === question.answer.correct_index;
}

function QuestionRenderer({
  question,
  response,
  onChange,
  showResult,
  lastCorrect,
}: {
  question: QuizQuestionWithEntry;
  response: any;
  onChange: (val: any) => void;
  showResult: boolean;
  lastCorrect: boolean;
}) {
  const payload = question.payload || {};
  const type = question.type || '';
  const correctIndex = typeof question.answer?.correct_index === 'number' ? question.answer.correct_index : null;

  if (type === 'cloze') {
    const sentence = payload.sentence_jp || payload.sentence || '';
    return (
      <div className="question-block">
        {sentence && <div className="muted">{sentence.replace(payload.blanked || '', '____')}</div>}
        <input
          className="input"
          type="text"
          placeholder="Type the missing text"
          value={response || ''}
          onChange={(e) => onChange(e.target.value)}
        />
        {Array.isArray(payload.options) && payload.options.length > 0 && (
          <div className="option-hints">Hints: {payload.options.join(' • ')}</div>
        )}
      </div>
    );
  }

  if (type === 'match') {
    const pairs: any[] = Array.isArray(payload.pairs) ? payload.pairs : [];
    const rightOptions = useMemo(
      () => shuffle(pairs.map((p) => p.right).filter(Boolean)),
      [question.id, question.entryId]
    );
    return (
      <div className="question-block matches">
        {pairs.map((pair, idx) => (
          <div key={idx} className="match-row">
            <div className="match-left">{pair.left}</div>
            <select
              className="input"
              value={response?.[idx] || ''}
              onChange={(e) => {
                const current = response && typeof response === 'object' ? response : {};
                onChange({ ...current, [idx]: e.target.value });
              }}
            >
              <option value="">Match…</option>
              {rightOptions.map((opt, optionIdx) => (
                <option key={optionIdx} value={opt}>
                  {opt}
                </option>
              ))}
            </select>
          </div>
        ))}
      </div>
    );
  }

  const options: any[] = Array.isArray(payload.options) ? payload.options : [];
  if (!options.length) {
    return <div className="muted">No options provided for this question.</div>;
  }
  return (
    <div className="question-block">
      {options.map((option, idx) => (
        <label
          key={idx}
          className={[
            'option',
            showResult && correctIndex === idx ? 'correct' : '',
            showResult && !lastCorrect && response === idx && correctIndex !== idx ? 'incorrect' : '',
          ].join(' ').trim()}
        >
          <input
            type="radio"
            checked={response === idx}
            onChange={() => onChange(idx)}
            name={`q-${question.id}`}
          />
          <span>{option}</span>
        </label>
      ))}
    </div>
  );
}

import IgMetaBlock from './IgMetaBlock';

function ExplanationPanel({ question, targets }: { question: QuizQuestionWithEntry; targets: TargetHit[] }) {
  return (
    <div className="explanation">
      <h4>Explanation</h4>
      {targets.length ? (
        <div className="explanation-grid">
          {targets.map(({ group, item }) => (
            <div key={`${group}-${item.id || item.jp}`} className="panel-card">
              <div className="panel-card__title">
                {item.jp || item.pattern || item.id}
                {item.kana && <div className="subline">{item.kana}</div>}
              </div>
              <div className="muted">
                {item.meaning_en || item.meaning || item.en || item.when_to_use_en || item.note_en}
              </div>
              {item.use_note_en && <div className="subline">{item.use_note_en}</div>}
              {item.when_to_use_en && <div className="subline">{item.when_to_use_en}</div>}
              {item.register && <span className="pill pill--ghost">{item.register}</span>}
              {(item.example || item.example_en || item.example_jp) && (
                <div className="item-row">
                  <span className="label">Example</span>
                  <div className="subline">{item.example?.jp || item.example_jp}</div>
                  <div className="subline muted">{item.example?.kana || item.example_kana}</div>
                  <div className="subline muted">{item.example?.en || item.example_en}</div>
                </div>
              )}
              <div className="tag">{group}</div>
            </div>
          ))}
        </div>
      ) : (
        <p className="muted">No linked study items were found for this question.</p>
      )}
      <VideoPlayer src={question.video_url} />
      {question.ig_meta && <IgMetaBlock ig={question.ig_meta} entryId={question.entryId} />}
    </div>
  );
}

export default function QuizRunner({ defaultMode = 'all', defaultEntryId, autoStart }: QuizRunnerProps) {
  const [entries, setEntries] = useState<EntrySummary[]>([]);
  const [loadingEntries, setLoadingEntries] = useState(true);
  const [mode] = useState<Mode>(defaultMode);
  const [selectedIds] = useState<string[]>(defaultEntryId ? [defaultEntryId] : []);
  const [questions, setQuestions] = useState<QuizQuestionWithEntry[]>([]);
  const [status, setStatus] = useState<'setup' | 'loading' | 'running' | 'finished'>('setup');
  const [currentIndex, setCurrentIndex] = useState(0);
  const [score, setScore] = useState(0);
  const [response, setResponse] = useState<any>(null);
  const [showResult, setShowResult] = useState(false);
  const [lastCorrect, setLastCorrect] = useState(false);
  const [lastSkipped, setLastSkipped] = useState(false);
  const [showExplanation, setShowExplanation] = useState(false);
  const [error, setError] = useState<string | null>(null);

  useEffect(() => {
    setLoadingEntries(true);
    fetchEntries()
      .then((data) => setEntries(data))
      .catch(() => setError('Could not load entries.'))
      .finally(() => setLoadingEntries(false));
  }, []);

  useEffect(() => {
    if (autoStart && entries.length > 0) {
      startQuiz();
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [autoStart, defaultEntryId, entries.length]);

  const currentQuestion = useMemo(() => questions[currentIndex], [questions, currentIndex]);

  const resetQuestionState = () => {
    setResponse(null);
    setShowResult(false);
    setLastCorrect(false);
    setLastSkipped(false);
    setShowExplanation(false);
  };

  const startQuiz = async () => {
    const ids: string[] =
      mode === 'all' ? entries.map((e) => e.id) : selectedIds.length ? selectedIds.slice(0, 1) : entries.map((e) => e.id);

    if (!ids.length) {
      setError('Pick at least one entry to quiz on.');
      return;
    }

    setError(null);
    setStatus('loading');

    try {
      const uniqueIds = Array.from(new Set(ids));
      const details: EntryDetail[] = await Promise.all(uniqueIds.map((id) => fetchEntry(id)));
      const pool: QuizQuestionWithEntry[] = details.flatMap((entry) => {
        const safeItems: EntryItems = entry.items || { grammar: [], vocab: [], conversation: [], key_phrases: [] };
        return (entry.quiz || []).map((q) => ({
          ...q,
          entryId: entry.id,
          entryTitle: entry.title,
          items: safeItems,
          video_url: entry.video_url,
          ig_meta: entry.ig_meta,
          targets: q.targets || [],
          type: q.type || 'unknown',
          payload: q.payload || {},
          answer: q.answer || {},
        }));
      });

      if (!pool.length) {
        setError('No quiz questions found in the selected entries.');
        setStatus('setup');
        return;
      }

      const chosen = shuffle(pool).slice(0, Math.min(TOTAL_QUESTIONS, pool.length));
      setQuestions(chosen);
      setCurrentIndex(0);
      setScore(0);
      resetQuestionState();
      setStatus('running');
    } catch (err: any) {
      setError(err?.message || 'Could not start quiz.');
      setStatus('setup');
    }
  };

  const handleSubmit = (skip = false) => {
    if (!currentQuestion || showResult) return;
    let correct = false;
    if (!skip) {
      if ((currentQuestion.type || '').startsWith('mc') || currentQuestion.type === 'choose_best_reply') {
        correct = checkMcAnswer(currentQuestion, response);
      } else if (currentQuestion.type === 'cloze') {
        correct = checkClozeAnswer(currentQuestion, response);
      } else if (currentQuestion.type === 'match') {
        correct = checkMatchAnswer(currentQuestion, response);
      } else if (typeof currentQuestion.answer?.correct_index === 'number') {
        correct = checkMcAnswer(currentQuestion, response);
      }
    }

    if (correct) {
      setScore((s) => s + 1);
    }
    setLastCorrect(correct);
    setLastSkipped(skip);
    setShowResult(true);
    setShowExplanation(!correct);
  };

  const goNext = () => {
    if (currentIndex + 1 >= questions.length) {
      setStatus('finished');
    } else {
      setCurrentIndex((idx) => idx + 1);
      resetQuestionState();
    }
  };

  if (loadingEntries) {
    return <div className="loading">Loading quiz setup…</div>;
  }

  if (status === 'setup') {
    return (
      <div className="quiz-setup">
        <h2>Building your quiz…</h2>
        <p className="muted">Preparing 10 questions from {mode === 'single' ? 'this entry' : 'all entries'}.</p>
        {error && <div className="error">{error}</div>}
        {!autoStart && (
          <button className="button button--primary" onClick={startQuiz}>
            Start quiz
          </button>
        )}
      </div>
    );
  }

  if (status === 'loading') {
    return <div className="loading">Building your quiz…</div>;
  }

  if (status === 'finished') {
    return (
      <div className="quiz-finished">
        <h2>Nice work!</h2>
        <p className="muted">You scored {score} out of {questions.length}.</p>
        <div className="actions">
          <button className="button" onClick={() => setStatus('setup')}>
            Play again
          </button>
        </div>
      </div>
    );
  }

  if (!currentQuestion) {
    return <div className="error">No questions available.</div>;
  }

  const targets = resolveTargets(currentQuestion);
  const correctText = deriveCorrectText(currentQuestion);
  const shouldShowExplanation = showResult && (!lastCorrect || showExplanation);

  return (
    <div className="quiz-runner">
      <div className="quiz-top">
        <div>
          <p className="eyebrow">{currentQuestion.entryTitle}</p>
          <h2>{currentQuestion.prompt_en || 'Answer the prompt'}</h2>
        </div>
        <div className="score-box">
          <div className="muted">{currentIndex + 1} / {questions.length}</div>
          <div className="score">Score: {score}</div>
        </div>
      </div>

      <QuestionRenderer
        question={currentQuestion}
        response={response}
        onChange={setResponse}
        showResult={showResult}
        lastCorrect={lastCorrect}
      />

      {showResult && (
        <div className={lastCorrect ? 'callout success' : 'callout'}>
          {lastCorrect ? 'Correct!' : lastSkipped ? null : 'Not quite.'}
          {(lastSkipped || !lastCorrect) && correctText && <div className="subline">Answer: {correctText}</div>}
        </div>
      )}

      <div className="quiz-actions">
        {!showResult && (
          <>
            <button className="button" onClick={() => handleSubmit(false)}>
              Submit
            </button>
            <button className="button button--ghost" onClick={() => handleSubmit(true)}>
              Don’t know
            </button>
          </>
        )}
        {showResult && (
          <>
            <button className="button button--primary" onClick={goNext}>
              {currentIndex + 1 === questions.length ? 'Finish' : 'Next'}
            </button>
            {lastCorrect && !showExplanation && (
              <button className="button button--ghost" onClick={() => setShowExplanation(true)}>
                Show explanation
              </button>
            )}
          </>
        )}
      </div>

      {shouldShowExplanation && <ExplanationPanel question={currentQuestion} targets={targets} />}
    </div>
  );
}
