import { useRef, useState } from 'react'
import toast from 'react-hot-toast'
import FileJson from 'lucide-react/dist/esm/icons/file-json'
import Upload from 'lucide-react/dist/esm/icons/upload'
import X from 'lucide-react/dist/esm/icons/x'
import Spinner from '../shared/Spinner'

const QUESTION_TYPES = new Set(['mcq', 'msq', 'nat'])
const OPTION_LETTERS = new Set(['A', 'B', 'C', 'D'])
const NAT_ANSWER_PATTERN = /^-?\d+(\.\d+)?(\s*[-:]\s*-?\d+(\.\d+)?)?$/

const SAMPLE_JSON = JSON.stringify({
  questions: [
    {
      question_type: 'mcq',
      question_text: 'What is the time complexity of binary search?',
      options: ['O(n)', 'O(log n)', 'O(n log n)', 'O(1)'],
      correct_answer: 'B',
      marks: 1,
      negative_marks: 0.33,
      subject: 'Algorithms',
    },
    {
      question_type: 'nat',
      question_text: 'How many distinct binary trees can be formed with 3 nodes?',
      options: [],
      correct_answer: '5',
      marks: 2,
      negative_marks: 0,
      subject: 'Data Structures',
    },
  ],
}, null, 2)

const isPlainObject = (value) =>
  value !== null && typeof value === 'object' && !Array.isArray(value)

const validateStringField = (question, idx, field) => {
  if (typeof question[field] !== 'string' || !question[field].trim()) {
    throw new Error(`Question ${idx + 1} ${field} must be a non-empty string`)
  }
  return question[field].trim()
}

const validateNumberField = (question, idx, field) => {
  if (question[field] === undefined) return undefined
  if (typeof question[field] !== 'number' || !Number.isFinite(question[field])) {
    throw new Error(`Question ${idx + 1} ${field} must be a number`)
  }
  return question[field]
}

const validateAnswer = (questionType, answer, idx) => {
  if (questionType === 'mcq' && !OPTION_LETTERS.has(answer)) {
    throw new Error(`Question ${idx + 1} MCQ correct_answer must be A, B, C, or D`)
  }

  if (questionType === 'msq') {
    const selected = answer.split(',').map(part => part.trim()).filter(Boolean)
    if (!selected.length || selected.some(part => !OPTION_LETTERS.has(part))) {
      throw new Error(`Question ${idx + 1} MSQ correct_answer must use option letters like A,C`)
    }
  }

  if (questionType === 'nat' && !NAT_ANSWER_PATTERN.test(answer)) {
    throw new Error(`Question ${idx + 1} NAT correct_answer must be a number or range`)
  }
}

const validateOptions = (question, questionType, idx) => {
  const options = question.options ?? []
  if (!Array.isArray(options)) {
    throw new Error(`Question ${idx + 1} options must be an array`)
  }

  if (questionType === 'nat') return []

  if (options.length !== 4 || options.some(option => typeof option !== 'string' || !option.trim())) {
    throw new Error(`Question ${idx + 1} options must contain exactly 4 non-empty strings`)
  }

  return options.map(option => option.trim())
}

const validateQuestion = (question, idx) => {
  if (!isPlainObject(question)) {
    throw new Error(`Question ${idx + 1} must be an object`)
  }

  const questionType = typeof question.question_type === 'string'
    ? question.question_type.trim().toLowerCase()
    : ''
  if (!QUESTION_TYPES.has(questionType)) {
    throw new Error(`Question ${idx + 1} question_type must be mcq, msq, or nat`)
  }

  const questionText = validateStringField(question, idx, 'question_text')
  const correctAnswer = validateStringField(question, idx, 'correct_answer').toUpperCase().replace(/\s+/g, '')
  const options = validateOptions(question, questionType, idx)
  validateAnswer(questionType, correctAnswer, idx)

  const marks = validateNumberField(question, idx, 'marks')
  const negativeMarks = validateNumberField(question, idx, 'negative_marks')

  return {
    ...question,
    question_type: questionType,
    question_text: questionText,
    options,
    correct_answer: correctAnswer,
    ...(marks === undefined ? {} : { marks }),
    ...(negativeMarks === undefined ? {} : { negative_marks: questionType === 'mcq' ? negativeMarks : 0 }),
  }
}

export function extractQuestions(parsed) {
  let questions

  if (Array.isArray(parsed)) {
    questions = parsed
  } else if (isPlainObject(parsed) && Array.isArray(parsed.questions)) {
    questions = parsed.questions
  } else {
    throw new Error('JSON root must be an array or an object with a "questions" array')
  }

  if (questions.length === 0) {
    throw new Error('JSON does not contain any questions')
  }

  return questions.map(validateQuestion)
}

export function parseQuestionsJson(value) {
  try {
    return extractQuestions(JSON.parse(value))
  } catch (err) {
    if (err instanceof SyntaxError) {
      throw new Error(`Invalid JSON: ${err.message}`)
    }
    throw err
  }
}

export function readJsonFile(file) {
  return new Promise((resolve, reject) => {
    const reader = new FileReader()
    reader.onload = event => resolve(event.target?.result || '')
    reader.onerror = () => reject(new Error('Failed to read file'))
    reader.readAsText(file)
  })
}

const formatServerDetail = (detail) => {
  if (!detail) return 'Failed to upload questions file'
  if (typeof detail === 'string') return detail
  if (Array.isArray(detail)) {
    return detail.map(item => item?.msg || item?.message || String(item)).join('; ')
  }
  if (Array.isArray(detail.errors)) {
    const lines = detail.errors.slice(0, 5).map(err => {
      const question = err.question_index ? `Q${err.question_index}` : 'Question'
      const field = err.field ? ` ${err.field}` : ''
      return `${question}${field}: ${err.message}`
    })
    if (detail.errors.length > 5) lines.push(`...and ${detail.errors.length - 5} more`)
    return [detail.message, ...lines].filter(Boolean).join(' | ')
  }
  return detail.message || JSON.stringify(detail)
}

export default function JSONUploadForm({ onAdd, onUploadFile, onClose }) {
  const [text, setText] = useState('')
  const [loading, setLoading] = useState(false)
  const [preview, setPreview] = useState(null)
  const [error, setError] = useState('')
  const [selectedFile, setSelectedFile] = useState(null)
  const [fileLoading, setFileLoading] = useState(false)
  const [fileMessage, setFileMessage] = useState('')
  const [fileError, setFileError] = useState('')
  const fileRef = useRef()
  const uploadFileRef = useRef()

  const validate = (value) => {
    setError('')
    setPreview(null)
    if (!value.trim()) return

    try {
      setPreview(parseQuestionsJson(value))
    } catch (err) {
      setError(err.message)
    }
  }

  const handleChange = (value) => {
    setText(value)
    validate(value)
  }

  const loadFile = async (event) => {
    const file = event.target.files[0]
    if (!file) return

    try {
      const content = await readJsonFile(file)
      setText(content)
      validate(content)
    } catch (err) {
      const message = err.message || 'Failed to read JSON file'
      setError(message)
      toast.error(message)
    }
  }

  const loadSample = () => {
    setText(SAMPLE_JSON)
    validate(SAMPLE_JSON)
  }

  const handleUploadFileChange = (event) => {
    const file = event.target.files[0] || null
    setSelectedFile(file)
    setFileMessage('')
    setFileError('')
  }

  const submitFile = async () => {
    if (!selectedFile) { toast.error('Select a JSON file first'); return }
    if (!selectedFile.name.toLowerCase().endsWith('.json')) { toast.error('Only .json files are accepted'); return }

    setFileLoading(true)
    setFileMessage('')
    setFileError('')
    try {
      const result = await onUploadFile(selectedFile)
      const message = result?.message || `Imported ${result?.imported_count || 0} questions from JSON file`
      setFileMessage(message)
      setSelectedFile(null)
      if (uploadFileRef.current) uploadFileRef.current.value = ''
      toast.success(message)
    } catch (err) {
      const message = formatServerDetail(err.response?.data?.detail)
      setFileError(message)
      toast.error(message)
    } finally {
      setFileLoading(false)
    }
  }

  const submit = async () => {
    if (!preview || preview.length === 0) { toast.error('No valid questions to upload'); return }

    setLoading(true)
    setError('')
    try {
      await onAdd(preview)
      toast.success(`${preview.length} questions added!`)
      onClose()
    } catch (err) {
      const message = formatServerDetail(err.response?.data?.detail) || 'Failed to add questions'
      setError(message)
      toast.error(message)
    } finally {
      setLoading(false)
    }
  }

  return (
    <div className="gate-card p-5 space-y-4">
      <div className="flex items-center justify-between">
        <h4 className="font-semibold theme-text">Upload Questions via JSON</h4>
        <button onClick={onClose} aria-label="Close" className="theme-muted hover:opacity-80">
          <X size={16}/>
        </button>
      </div>

      <div className="px-3 py-3 rounded-lg border theme-panel-card space-y-3">
        <div>
          <label className="label">Upload JSON file directly</label>
          <div className="flex flex-col sm:flex-row gap-2">
            <input
              ref={uploadFileRef}
              type="file"
              accept=".json,application/json"
              onChange={handleUploadFileChange}
              className="input text-sm flex-1"
            />
            <button
              onClick={submitFile}
              disabled={fileLoading || !selectedFile}
              className="btn-primary flex items-center justify-center gap-2 text-sm py-2 sm:w-64"
            >
              {fileLoading ? <Spinner size={14}/> : <Upload size={14}/>}
              {fileLoading ? 'Uploading...' : 'Upload Questions File (.json)'}
            </button>
          </div>
        </div>
        {fileMessage && (
          <div className="px-3 py-2 rounded-lg bg-green-500/10 border border-green-500/20 text-green-400 text-sm">
            {fileMessage}
          </div>
        )}
        {fileError && (
          <div className="px-3 py-2 rounded-lg bg-red-500/10 border border-red-500/20 text-red-400 text-sm">
            {fileError}
          </div>
        )}
      </div>

      <div className="flex gap-2">
        <button onClick={() => fileRef.current?.click()} className="btn-ghost flex items-center gap-2 text-sm py-2">
          <Upload size={14}/> Load File into Editor
        </button>
        <button onClick={loadSample} className="btn-ghost flex items-center gap-2 text-sm py-2">
          <FileJson size={14}/> Load Sample
        </button>
        <input type="file" accept=".json" ref={fileRef} className="hidden" onChange={loadFile} />
      </div>

      <div className="px-3 py-2 rounded-lg border theme-panel-card text-xs theme-muted">
        Format: <span className="font-mono theme-muted">{"{ \"questions\": [ { question_type, question_text, options, correct_answer, marks, negative_marks } ] }"}</span>
        <br/>
        question_type: <span className="text-sky-400">mcq</span> / <span className="text-amber-400">msq</span> / <span className="text-green-400">nat</span>
        {' '} &middot; correct_answer: <span className="text-sky-400">A</span> or <span className="text-amber-400">A,C</span> or <span className="text-green-400">42</span>
      </div>

      <div>
        <label className="label">Paste JSON here</label>
        <textarea
          className="input resize-none font-mono text-xs"
          rows={10}
          value={text}
          onChange={event => handleChange(event.target.value)}
          placeholder={'{\n  "questions": [\n    {\n      "question_type": "mcq",\n      "question_text": "Your question here?",\n      "options": ["A text", "B text", "C text", "D text"],\n      "correct_answer": "B",\n      "marks": 1,\n      "negative_marks": 0.33\n    }\n  ]\n}'}
        />
      </div>

      {error && (
        <div className="px-3 py-2 rounded-lg bg-red-500/10 border border-red-500/20 text-red-400 text-sm">
          {error}
        </div>
      )}

      {preview && !error && (
        <div className="px-3 py-2 rounded-lg bg-green-500/10 border border-green-500/20 text-green-400 text-sm">
          Valid JSON - {preview.length} question{preview.length !== 1 ? 's' : ''} ready to upload
          <div className="mt-1 space-y-0.5">
            {preview.slice(0, 3).map((question, idx) => (
              <p key={`${question.question_type}-${idx}`} className="text-green-400/70 text-xs truncate">
                {idx + 1}. [{question.question_type?.toUpperCase()}] {question.question_text}
              </p>
            ))}
            {preview.length > 3 && <p className="text-green-400/50 text-xs">...and {preview.length - 3} more</p>}
          </div>
        </div>
      )}

      <div className="flex gap-3">
        <button onClick={onClose} className="btn-ghost flex-1">Cancel</button>
        <button
          onClick={submit}
          disabled={loading || !preview || !!error}
          className="btn-primary flex-1 flex items-center justify-center gap-2"
        >
          {loading && <Spinner size={14}/>}
          {loading ? 'Uploading...' : `Upload ${preview ? preview.length : 0} Questions`}
        </button>
      </div>
    </div>
  )
}
