import { useEffect, useState, useCallback, useRef } from 'react';
import { useParams, useNavigate, useSearchParams } from 'react-router-dom';
import api from '../api/client.js';
import Spinner from '../components/Spinner.jsx';
import useExamSecurity from '../hooks/useExamSecurity.js';
import { useToast } from '../context/ToastContext.jsx';

const LETTERS = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H'];

// Exam time limit scales with the number of questions served (1 minute each).
const SECONDS_PER_QUESTION = 60;
const fmtTime = (s) => `${Math.floor(s / 60)}:${String(s % 60).padStart(2, '0')}`;

export default function ExamRunner() {
  const { slug } = useParams();
  const [searchParams] = useSearchParams();
  const mode = (searchParams.get('mode') || 'PRACTICE').toUpperCase();
  const navigate = useNavigate();
  const toast = useToast();

  const [loading, setLoading] = useState(true);
  const [error, setError] = useState('');
  const [course, setCourse] = useState(null);
  const [attempt, setAttempt] = useState(null);
  const [questions, setQuestions] = useState([]);
  const [access, setAccess] = useState(null);
  const [current, setCurrent] = useState(0);
  // answers: { [questionId]: { selected: Set, result?: {isCorrect, correctOptionIds, explanation}, submitted } }
  const [answers, setAnswers] = useState({});
  const [ai, setAi] = useState({}); // questionId -> explanation text
  const [aiLoading, setAiLoading] = useState(false);
  const [finishing, setFinishing] = useState(false);
  const [payCfg, setPayCfg] = useState({ stripeConfigured: true, devUnlock: false });
  const tabWarned = useRef(false);
  // Exam countdown
  const [timeLeft, setTimeLeft] = useState(null); // seconds remaining (EXAM mode only)
  const deadlineRef = useRef(null);
  const autoSubmitRef = useRef(false);

  useEffect(() => {
    api.get('/payments/config').then(({ data }) => setPayCfg(data)).catch(() => {});
  }, []);

  // ---- security ----
  useExamSecurity({
    active: true,
    examMode: mode === 'EXAM',
    onTabSwitch: (count) => {
      toast.error(`Warning: leaving the exam tab is not allowed (×${count})`);
      tabWarned.current = true;
    },
    onBlockedAction: (label) => toast.error(label),
  });

  // ---- load ----
  useEffect(() => {
    let cancelled = false;
    (async () => {
      try {
        const { data: cd } = await api.get(`/courses/${slug}`);
        if (cancelled) return;
        setCourse(cd.course);
        const { data } = await api.post('/attempts/start', { courseId: cd.course.id, mode });
        if (cancelled) return;
        setAttempt(data.attempt);
        setQuestions(data.questions);
        setAccess(data.access);
      } catch (e) {
        if (!cancelled) setError(e.message);
      } finally {
        if (!cancelled) setLoading(false);
      }
    })();
    return () => {
      cancelled = true;
    };
  }, [slug, mode]);

  const q = questions[current];
  const ans = q ? answers[q.id] : null;
  const selected = ans?.selected || new Set();

  const recordExam = useCallback(
    async (questionId, selectedArr) => {
      try {
        await api.post(`/attempts/${attempt.id}/answer`, {
          questionId,
          selectedOptionIds: selectedArr,
        });
      } catch {
        /* will retry on finish */
      }
    },
    [attempt]
  );

  const toggleOption = (optionId) => {
    if (!q) return;
    if (ans?.submitted) return; // locked after reveal (practice)

    setAnswers((prev) => {
      const prevSel = new Set(prev[q.id]?.selected || []);
      if (q.type === 'SINGLE') {
        prevSel.clear();
        prevSel.add(optionId);
      } else if (prevSel.has(optionId)) {
        prevSel.delete(optionId);
      } else {
        prevSel.add(optionId);
      }
      const next = { ...prev, [q.id]: { ...prev[q.id], selected: prevSel } };

      // Practice + single answer: reveal immediately
      if (mode === 'PRACTICE' && q.type === 'SINGLE') {
        submitPractice(q.id, [...prevSel]);
      } else if (mode === 'EXAM') {
        recordExam(q.id, [...prevSel]);
      }
      return next;
    });
  };

  const submitPractice = async (questionId, selectedArr) => {
    try {
      const { data } = await api.post(`/attempts/${attempt.id}/answer`, {
        questionId,
        selectedOptionIds: selectedArr,
      });
      setAnswers((prev) => ({
        ...prev,
        [questionId]: {
          ...prev[questionId],
          selected: new Set(selectedArr),
          submitted: true,
          result: {
            isCorrect: data.isCorrect,
            correctOptionIds: data.correctOptionIds,
            explanation: data.explanation,
          },
        },
      }));
    } catch (e) {
      toast.error(e.message);
    }
  };

  const checkPracticeMultiple = () => {
    if (!q || selected.size === 0) return;
    submitPractice(q.id, [...selected]);
  };

  const explainAi = async () => {
    if (!q || ai[q.id]) return;
    setAiLoading(true);
    try {
      const { data } = await api.post('/ai/explain', { questionId: q.id });
      setAi((prev) => ({ ...prev, [q.id]: data.explanation }));
    } catch (e) {
      toast.error(e.message);
    } finally {
      setAiLoading(false);
    }
  };

  const finish = async () => {
    setFinishing(true);
    try {
      // ensure all selected answers are recorded (exam mode safety)
      if (mode === 'EXAM') {
        await Promise.all(
          Object.entries(answers)
            .filter(([, a]) => a.selected && a.selected.size > 0)
            .map(([qid, a]) => recordExam(qid, [...a.selected]))
        );
      }
      const { data } = await api.post(`/attempts/${attempt.id}/finish`);
      navigate(`/result/${data.result.attemptId}`, { replace: true });
    } catch (e) {
      toast.error(e.message);
      setFinishing(false);
    }
  };

  const buy = async () => {
    try {
      if (payCfg.devUnlock) {
        await api.post('/payments/dev-grant', { courseId: course.id });
        toast.success('Full access unlocked — loading all questions…');
        // restart the attempt so the full (unlocked) question set loads
        const { data } = await api.post('/attempts/start', { courseId: course.id, mode });
        setAttempt(data.attempt);
        setQuestions(data.questions);
        setAccess(data.access);
        setAnswers({});
        setCurrent(0);
        // Restart the exam clock for the now-larger question set.
        deadlineRef.current = null;
        autoSubmitRef.current = false;
        setTimeLeft(null);
        return;
      }
      const { data } = await api.post('/payments/checkout', { courseId: course.id });
      window.location.href = data.url;
    } catch (e) {
      toast.error(e.message);
    }
  };

  // ---- exam countdown: start when questions are ready, auto-submit at zero ----
  useEffect(() => {
    if (mode !== 'EXAM' || loading || questions.length === 0) return undefined;
    if (!deadlineRef.current) {
      // Admin-set whole-exam limit (minutes); fall back to 1 minute per question if unset.
      const totalSeconds =
        course?.examMinutes > 0 ? course.examMinutes * 60 : questions.length * SECONDS_PER_QUESTION;
      deadlineRef.current = Date.now() + totalSeconds * 1000;
      setTimeLeft(totalSeconds);
    }
    const id = setInterval(() => {
      const remaining = Math.max(0, Math.round((deadlineRef.current - Date.now()) / 1000));
      setTimeLeft(remaining);
      if (remaining <= 0) {
        clearInterval(id);
        if (!autoSubmitRef.current) {
          autoSubmitRef.current = true;
          toast.error("Time's up — submitting your exam.");
          finish();
        }
      }
    }, 1000);
    return () => clearInterval(id);
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [mode, loading, questions.length]);

  if (loading) return <Spinner />;
  if (error) return <div className="container page"><div className="alert alert-error">{error}</div></div>;
  if (!q) return <div className="container page"><div className="alert alert-info">This exam has no questions yet.</div></div>;

  const answeredCount = Object.values(answers).filter((a) => a.selected && a.selected.size > 0).length;
  const progress = Math.round(((current + 1) / questions.length) * 100);
  const isLast = current === questions.length - 1;
  const result = ans?.result;

  return (
    <div className="container page no-select" style={{ maxWidth: 780 }}>
      <div className="row-between wrap mb-2">
        <div>
          <span className={`badge ${mode === 'EXAM' ? 'badge-amber' : ''}`}>{mode === 'EXAM' ? '⏱️ Exam mode' : '🎯 Practice mode'}</span>
          <h2 style={{ margin: '8px 0 0' }}>{course.title}</h2>
        </div>
        <div className="row" style={{ alignItems: 'center', gap: 12 }}>
          {mode === 'EXAM' && timeLeft != null && (
            <span
              className={`badge ${timeLeft <= 20 ? 'badge-red' : timeLeft <= 60 ? 'badge-amber' : ''}`}
              style={{ fontSize: '0.95rem', fontVariantNumeric: 'tabular-nums' }}
              title="Time remaining"
            >
              ⏱️ {fmtTime(timeLeft)}
            </span>
          )}
          <div className="muted">Question {current + 1} of {questions.length}</div>
        </div>
      </div>

      <div className="progress mb-3"><span style={{ width: `${progress}%` }} /></div>

      <div className="card">
        <h3 style={{ marginBottom: 18 }}>{q.text}</h3>

        {q.type === 'MULTIPLE' && !result && (
          <p className="muted" style={{ marginTop: -8 }}>Select all that apply.</p>
        )}

        {q.options.map((opt, i) => {
          const isSel = selected.has(opt.id);
          let cls = 'option';
          if (result) {
            const isCorrect = result.correctOptionIds.includes(opt.id);
            if (isCorrect) cls += ' correct';
            else if (isSel) cls += ' incorrect';
          } else if (isSel) {
            cls += ' selected';
          }
          return (
            <div key={opt.id} className={cls} onClick={() => toggleOption(opt.id)}>
              <div className="option-marker">{LETTERS[i]}</div>
              <div>{opt.text}</div>
            </div>
          );
        })}

        {/* Practice: multi-answer needs a check button */}
        {mode === 'PRACTICE' && q.type === 'MULTIPLE' && !result && (
          <button className="btn mt-2" disabled={selected.size === 0} onClick={checkPracticeMultiple}>
            Check answer
          </button>
        )}

        {/* Practice reveal box */}
        {result && (
          <div className={`explain-box ${result.isCorrect ? 'correct' : 'incorrect'}`}>
            <b>{result.isCorrect ? '✓ Correct!' : '✗ Not quite.'}</b>
            {result.explanation && <p style={{ margin: '8px 0 0' }}>{result.explanation}</p>}
            <div className="mt-2">
              <button className="btn btn-sm btn-ghost" onClick={explainAi} disabled={aiLoading || !!ai[q.id]}>
                {aiLoading ? 'Thinking…' : ai[q.id] ? 'AI explained ✓' : '🤖 Explain with AI'}
              </button>
            </div>
            {ai[q.id] && (
              <div className="explain-box" style={{ background: '#fff' }}>
                <b>AI explanation</b>
                <p style={{ margin: '6px 0 0', whiteSpace: 'pre-wrap' }}>{ai[q.id]}</p>
              </div>
            )}
          </div>
        )}
      </div>

      {/* Navigation */}
      <div className="row-between mt-3 wrap">
        <button className="btn btn-ghost" disabled={current === 0} onClick={() => setCurrent((c) => c - 1)}>
          ← Previous
        </button>

        {!isLast ? (
          <button className="btn" onClick={() => setCurrent((c) => c + 1)}>Next →</button>
        ) : (
          <button className="btn btn-success" onClick={finish} disabled={finishing}>
            {finishing ? 'Submitting…' : mode === 'EXAM' ? 'Finish & see results' : 'Finish'}
          </button>
        )}
      </div>

      {mode === 'EXAM' && (
        <p className="muted center mt-2" style={{ fontSize: '0.86rem' }}>
          {answeredCount} of {questions.length} answered · answers are revealed after you finish · the exam auto-submits when the timer ends
        </p>
      )}

      {/* Paywall when locked */}
      {access && access.locked && (
        <div className="paywall mt-3">
          <div className="lock-icon">🔒</div>
          <h3>You've reached the free preview limit</h3>
          <p className="muted">
            This is question {questions.length} of {access.totalAvailable}. Unlock the remaining{' '}
            <b>{access.lockedCount}</b> questions for{' '}
            <b>${(course.priceCents / 100).toFixed(2)}</b>.
          </p>
          <button className="btn btn-lg btn-success" onClick={buy}>
            {payCfg.devUnlock ? '🔓 Get full access (test mode)' : '🔓 Get full access — pay with card'}
          </button>
        </div>
      )}
    </div>
  );
}
