import React, { useCallback, useEffect, useMemo, useState } from 'react';
import { useNavigate, useParams } from 'react-router-dom';
import {
  Alert,
  Autocomplete,
  Box,
  Button,
  Card,
  CardContent,
  Chip,
  CircularProgress,
  Container,
  Divider,
  FormControl,
  Grid,
  InputLabel,
  MenuItem,
  Paper,
  Select,
  Stack,
  Tab,
  Tabs,
  TextField,
  Typography,
} from '@mui/material';
import ArrowBackIcon from '@mui/icons-material/ArrowBack';
import SaveIcon from '@mui/icons-material/Save';
import SyncIcon from '@mui/icons-material/Sync';

import { logger } from '../../debugConfig';
import { examRepository } from '../../services/exam/examRepository';
import type {
  ExamQuestion,
  ExamTemplateDetail,
  MarkScheme,
  MarkSchemeType,
  SpecPoint,
} from '../../types/exam.types';

type Drafts = Record<string, PartDraft>;

interface PartDraft {
  max_marks: string;
  answer_type: 'written' | 'mcq' | 'short' | 'diagram';
  spec_ref: string;
  schemeType: MarkSchemeType;
  body: string;
  notes: string;
}

const ANSWER_TYPES: Array<PartDraft['answer_type']> = ['written', 'short', 'mcq', 'diagram'];
const SCHEME_TYPES: Array<{ value: MarkSchemeType; label: string; helper: string }> = [
  { value: 'points', label: 'Points', helper: 'One mark point per line, e.g. "1 | Mentions resultant force".' },
  { value: 'levels', label: 'Levels', helper: 'One level per line, e.g. "L2 | 3-4 | Clear linked explanation".' },
  { value: 'parts', label: 'Parts', helper: 'One sub-part per line, e.g. "a | 2 | Correct substitution".' },
  { value: 'checklist', label: 'Checklist', helper: 'One checklist item per line, e.g. "1 | Correct unit".' },
  { value: 'free', label: 'Free text', helper: 'Paste or type the mark scheme exactly as it should be stored.' },
];

function deriveSpecCode(template: ExamTemplateDetail | null): string {
  const examCode = template?.exam_code ?? '';
  const match = examCode.match(/^([A-Z]+-[A-Z]+-\d+)/i);
  return match?.[1] ?? '';
}

function schemeToDraft(scheme: MarkScheme | null | undefined): Pick<PartDraft, 'schemeType' | 'body' | 'notes'> {
  const type = (scheme?.type as MarkSchemeType | undefined) ?? 'points';
  const notes = typeof scheme?.notes === 'string' ? scheme.notes : '';

  if (type === 'levels' && Array.isArray(scheme?.levels)) {
    return {
      schemeType: 'levels',
      body: scheme.levels.map((l) => `${l.level ?? ''} | ${l.min ?? 0}-${l.max ?? 0} | ${l.descriptor ?? ''}`).join('\n'),
      notes,
    };
  }
  if (type === 'parts' && Array.isArray(scheme?.parts)) {
    return {
      schemeType: 'parts',
      body: scheme.parts.map((p) => `${p.label ?? ''} | ${p.marks ?? 0} | ${p.guidance ?? ''}`).join('\n'),
      notes,
    };
  }
  if (type === 'checklist' && Array.isArray(scheme?.checklist)) {
    return {
      schemeType: 'checklist',
      body: scheme.checklist.map((item) => `${item.marks ?? 1} | ${item.text ?? ''}`).join('\n'),
      notes,
    };
  }
  if (type === 'free') {
    return { schemeType: 'free', body: typeof scheme?.text === 'string' ? scheme.text : '', notes };
  }
  if (Array.isArray(scheme?.points)) {
    return {
      schemeType: 'points',
      body: scheme.points.map((p) => `${p.mark ?? 1} | ${p.text ?? ''}`).join('\n'),
      notes,
    };
  }
  return { schemeType: type, body: '', notes };
}

function parseNumber(value: string, fallback = 0): number {
  const parsed = Number(value);
  return Number.isFinite(parsed) ? parsed : fallback;
}

function splitLine(line: string): string[] {
  return line.split('|').map((part) => part.trim());
}

function draftToScheme(draft: PartDraft): MarkScheme {
  const lines = draft.body.split('\n').map((line) => line.trim()).filter(Boolean);
  if (draft.schemeType === 'free') return { type: 'free', text: draft.body, notes: draft.notes || undefined };
  if (draft.schemeType === 'levels') {
    return {
      type: 'levels',
      levels: lines.map((line, idx) => {
        const [level, range, descriptor] = splitLine(line);
        const [minRaw, maxRaw] = (range || '').split('-').map((part) => part.trim());
        return {
          level: level || `L${idx + 1}`,
          min: parseNumber(minRaw, 0),
          max: parseNumber(maxRaw, parseNumber(minRaw, 0)),
          descriptor: descriptor || line,
        };
      }),
      notes: draft.notes || undefined,
    };
  }
  if (draft.schemeType === 'parts') {
    return {
      type: 'parts',
      parts: lines.map((line, idx) => {
        const [label, marks, guidance] = splitLine(line);
        return { label: label || String(idx + 1), marks: parseNumber(marks, 1), guidance: guidance || line };
      }),
      notes: draft.notes || undefined,
    };
  }
  if (draft.schemeType === 'checklist') {
    return {
      type: 'checklist',
      checklist: lines.map((line) => {
        const [marks, text] = splitLine(line);
        return { marks: parseNumber(marks, 1), text: text || line };
      }),
      notes: draft.notes || undefined,
    };
  }
  return {
    type: 'points',
    points: lines.map((line) => {
      const [mark, text] = splitLine(line);
      return { mark: parseNumber(mark, 1), text: text || line };
    }),
    notes: draft.notes || undefined,
  };
}

function makeDraft(question: ExamQuestion): PartDraft {
  const scheme = schemeToDraft(question.mark_scheme);
  return {
    max_marks: String(question.max_marks ?? 0),
    answer_type: (question.answer_type as PartDraft['answer_type'] | null) ?? 'written',
    spec_ref: question.spec_ref ?? '',
    schemeType: scheme.schemeType,
    body: scheme.body,
    notes: scheme.notes,
  };
}

const MarkSchemePage: React.FC = () => {
  const navigate = useNavigate();
  const { templateId } = useParams<{ templateId: string }>();

  const [template, setTemplate] = useState<ExamTemplateDetail | null>(null);
  const [selectedId, setSelectedId] = useState<string | null>(null);
  const [drafts, setDrafts] = useState<Drafts>({});
  const [specCode, setSpecCode] = useState('');
  const [specPoints, setSpecPoints] = useState<SpecPoint[]>([]);
  const [loading, setLoading] = useState(true);
  const [savingId, setSavingId] = useState<string | null>(null);
  const [syncing, setSyncing] = useState(false);
  const [error, setError] = useState<string | null>(null);
  const [notice, setNotice] = useState<string | null>(null);
  const [specEndpointMissing, setSpecEndpointMissing] = useState(false);

  const load = useCallback(async () => {
    if (!templateId) return;
    setLoading(true);
    setError(null);
    try {
      const detail = await examRepository.getTemplate(templateId);
      const leafParts = detail.questions.filter((q) => !q.is_container).sort((a, b) => a.order - b.order || a.label.localeCompare(b.label));
      setTemplate(detail);
      setSelectedId((prev) => prev ?? leafParts[0]?.id ?? null);
      setDrafts(Object.fromEntries(leafParts.map((q) => [q.id, makeDraft(q)])));
      setSpecCode((prev) => prev || deriveSpecCode(detail));
    } catch (e) {
      const msg = e instanceof Error ? e.message : String(e);
      logger.error('cc-exam-marker', 'Failed to load mark scheme template', { message: msg, templateId });
      setError(msg);
    } finally {
      setLoading(false);
    }
  }, [templateId]);

  useEffect(() => {
    void load();
  }, [load]);

  const loadSpecPoints = useCallback(async () => {
    if (!specCode.trim()) return;
    setSpecEndpointMissing(false);
    try {
      setSpecPoints(await examRepository.listSpecPoints(specCode.trim()));
    } catch (e) {
      const msg = e instanceof Error ? e.message : String(e);
      logger.warn('cc-exam-marker', 'SpecPoint endpoint unavailable; falling back to manual spec_ref entry', { message: msg, specCode });
      setSpecEndpointMissing(true);
      setSpecPoints([]);
    }
  }, [specCode]);

  useEffect(() => {
    void loadSpecPoints();
  }, [loadSpecPoints]);

  const parts = useMemo(
    () => (template?.questions ?? []).filter((q) => !q.is_container).sort((a, b) => a.order - b.order || a.label.localeCompare(b.label)),
    [template],
  );
  const selected = parts.find((p) => p.id === selectedId) ?? null;
  const draft = selected ? drafts[selected.id] : null;
  const selectedSpecPoint = draft?.spec_ref ? specPoints.find((p) => p.ref === draft.spec_ref) ?? null : null;

  const updateDraft = (questionId: string, patch: Partial<PartDraft>) => {
    setDrafts((prev) => ({ ...prev, [questionId]: { ...prev[questionId], ...patch } }));
  };

  const savePart = async (question: ExamQuestion) => {
    const partDraft = drafts[question.id];
    if (!partDraft) return;
    setSavingId(question.id);
    setError(null);
    setNotice(null);
    try {
      await examRepository.patchQuestion(question.id, {
        max_marks: parseNumber(partDraft.max_marks, question.max_marks),
        answer_type: partDraft.answer_type,
        mark_scheme: draftToScheme(partDraft),
        spec_ref: partDraft.spec_ref.trim() || null,
      });
      setTemplate((prev) => {
        if (!prev) return prev;
        return {
          ...prev,
          questions: prev.questions.map((q) => q.id === question.id ? {
            ...q,
            max_marks: parseNumber(partDraft.max_marks, question.max_marks),
            answer_type: partDraft.answer_type,
            mark_scheme: draftToScheme(partDraft),
            spec_ref: partDraft.spec_ref.trim() || null,
          } : q),
        };
      });
      setNotice(`Saved mark scheme for Part ${question.label}.`);
    } catch (e) {
      const msg = e instanceof Error ? e.message : String(e);
      logger.error('cc-exam-marker', 'Save mark scheme failed', { message: msg, questionId: question.id });
      setError(msg);
    } finally {
      setSavingId(null);
    }
  };

  const syncGraph = async () => {
    if (!templateId) return;
    setSyncing(true);
    setError(null);
    setNotice(null);
    try {
      const res = await examRepository.syncTemplateToGraph(templateId);
      setNotice(`Neo4j projection ${res.status}; ${JSON.stringify(res.projection ?? {})}`);
    } catch (e) {
      const msg = e instanceof Error ? e.message : String(e);
      logger.error('cc-exam-marker', 'Neo4j sync failed', { message: msg, templateId });
      setError(msg);
    } finally {
      setSyncing(false);
    }
  };

  if (loading) {
    return <Box sx={{ display: 'flex', justifyContent: 'center', py: 10 }}><CircularProgress /></Box>;
  }

  return (
    <Container maxWidth="xl" sx={{ py: 4 }}>
      <Stack spacing={3}>
        <Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', flexWrap: 'wrap', gap: 2 }}>
          <Box>
            <Button startIcon={<ArrowBackIcon />} onClick={() => navigate('/exam-marker')} sx={{ mb: 1 }}>
              Back to templates
            </Button>
            <Typography variant="h3" component="h1">Mark Scheme editor</Typography>
            <Typography variant="body1" color="text.secondary">
              {template?.title ?? 'Template'} · Edit per-Part mark schemes and link assessed specification points.
            </Typography>
          </Box>
          <Stack direction="row" spacing={1}>
            {templateId && <Button variant="outlined" onClick={() => navigate(`/exam-marker/${templateId}/setup`)}>Setup canvas</Button>}
            <Button variant="outlined" startIcon={<SyncIcon />} onClick={syncGraph} disabled={syncing || !templateId}>
              {syncing ? 'Syncing…' : 'Sync to graph'}
            </Button>
          </Stack>
        </Box>

        {error && <Alert severity="error" onClose={() => setError(null)}>{error}</Alert>}
        {notice && <Alert severity="success" onClose={() => setNotice(null)}>{notice}</Alert>}
        {specEndpointMissing && (
          <Alert severity="warning">
            SpecPoint picker endpoint was not available from this API build. You can still type a spec_ref manually; the backend projection resolves it during neo4j-sync.
          </Alert>
        )}

        <Paper variant="outlined" sx={{ p: 2 }}>
          <Grid container spacing={2} alignItems="center">
            <Grid item xs={12} md={4}>
              <TextField
                label="Specification code"
                value={specCode}
                onChange={(e) => setSpecCode(e.target.value)}
                fullWidth
                helperText="Example: AQA-PHYS-8463. Used by the SpecPoint picker."
              />
            </Grid>
            <Grid item xs={12} md={8}>
              <Typography variant="body2" color="text.secondary">
                Save writes PATCH /api/exam/questions/:id. Spec refs are persisted on the Part and projected as (:Part)-[:ASSESSES]-&gt;(:SpecPoint).
              </Typography>
            </Grid>
          </Grid>
        </Paper>

        {parts.length === 0 ? (
          <Alert severity="info">
            This template has no Parts yet. Draw Part boxes on the setup canvas first, then return here to enter mark schemes.
          </Alert>
        ) : (
          <Grid container spacing={3}>
            <Grid item xs={12} md={3}>
              <Card variant="outlined">
                <CardContent>
                  <Typography variant="h6" gutterBottom>Parts</Typography>
                  <Tabs
                    orientation="vertical"
                    value={selectedId ?? false}
                    onChange={(_, value) => setSelectedId(value)}
                    variant="scrollable"
                    sx={{ borderRight: 1, borderColor: 'divider', maxHeight: '65vh' }}
                  >
                    {parts.map((part) => (
                      <Tab
                        key={part.id}
                        value={part.id}
                        label={
                          <Box sx={{ textAlign: 'left', width: '100%' }}>
                            <Typography variant="body2">Part {part.label}</Typography>
                            <Stack direction="row" spacing={0.5} sx={{ mt: 0.5, flexWrap: 'wrap' }}>
                              <Chip size="small" label={`${drafts[part.id]?.max_marks ?? part.max_marks} marks`} />
                              {(drafts[part.id]?.spec_ref || part.spec_ref) && <Chip size="small" color="info" label={drafts[part.id]?.spec_ref || part.spec_ref || ''} />}
                            </Stack>
                          </Box>
                        }
                      />
                    ))}
                  </Tabs>
                </CardContent>
              </Card>
            </Grid>

            <Grid item xs={12} md={9}>
              {selected && draft && (
                <Card variant="outlined">
                  <CardContent>
                    <Stack spacing={3}>
                      <Box sx={{ display: 'flex', justifyContent: 'space-between', gap: 2, flexWrap: 'wrap' }}>
                        <Box>
                          <Typography variant="h5">Part {selected.label}</Typography>
                          <Typography variant="body2" color="text.secondary">
                            {selected.parent_id ? `Parent question ${selected.parent_id}` : 'No parent question'}
                          </Typography>
                        </Box>
                        <Button
                          variant="contained"
                          startIcon={<SaveIcon />}
                          disabled={savingId === selected.id}
                          onClick={() => savePart(selected)}
                        >
                          {savingId === selected.id ? 'Saving…' : 'Save part'}
                        </Button>
                      </Box>

                      <Grid container spacing={2}>
                        <Grid item xs={12} sm={4}>
                          <TextField
                            label="Max marks"
                            value={draft.max_marks}
                            onChange={(e) => updateDraft(selected.id, { max_marks: e.target.value })}
                            type="number"
                            inputProps={{ min: 0, step: 0.5 }}
                            fullWidth
                          />
                        </Grid>
                        <Grid item xs={12} sm={4}>
                          <FormControl fullWidth>
                            <InputLabel id="answer-type-label">Answer type</InputLabel>
                            <Select
                              labelId="answer-type-label"
                              label="Answer type"
                              value={draft.answer_type}
                              onChange={(e) => updateDraft(selected.id, { answer_type: e.target.value as PartDraft['answer_type'] })}
                            >
                              {ANSWER_TYPES.map((type) => <MenuItem key={type} value={type}>{type}</MenuItem>)}
                            </Select>
                          </FormControl>
                        </Grid>
                        <Grid item xs={12} sm={4}>
                          <FormControl fullWidth>
                            <InputLabel id="scheme-type-label">Scheme form</InputLabel>
                            <Select
                              labelId="scheme-type-label"
                              label="Scheme form"
                              value={draft.schemeType}
                              onChange={(e) => updateDraft(selected.id, { schemeType: e.target.value as MarkSchemeType })}
                            >
                              {SCHEME_TYPES.map((type) => <MenuItem key={type.value} value={type.value}>{type.label}</MenuItem>)}
                            </Select>
                          </FormControl>
                        </Grid>
                      </Grid>

                      <Autocomplete
                        options={specPoints}
                        value={selectedSpecPoint}
                        onChange={(_, value) => updateDraft(selected.id, { spec_ref: value?.ref ?? '' })}
                        getOptionLabel={(option) => `${option.ref} — ${option.description}`}
                        isOptionEqualToValue={(option, value) => option.ref === value.ref}
                        renderInput={(params) => (
                          <TextField
                            {...params}
                            label="SpecPoint picker"
                            helperText={specPoints.length ? 'Choose a seeded SpecPoint, or type below to override.' : 'No picker results loaded; type spec_ref manually below.'}
                          />
                        )}
                      />

                      <TextField
                        label="spec_ref"
                        value={draft.spec_ref}
                        onChange={(e) => updateDraft(selected.id, { spec_ref: e.target.value })}
                        helperText="Persisted to exam_questions.spec_ref; graph sync creates the ASSESSES edge when the ref matches a seeded SpecPoint."
                        fullWidth
                      />

                      <Divider />

                      <TextField
                        label={`${SCHEME_TYPES.find((type) => type.value === draft.schemeType)?.label ?? 'Mark scheme'} body`}
                        value={draft.body}
                        onChange={(e) => updateDraft(selected.id, { body: e.target.value })}
                        helperText={SCHEME_TYPES.find((type) => type.value === draft.schemeType)?.helper}
                        multiline
                        minRows={10}
                        fullWidth
                      />

                      <TextField
                        label="Internal marking notes"
                        value={draft.notes}
                        onChange={(e) => updateDraft(selected.id, { notes: e.target.value })}
                        multiline
                        minRows={3}
                        fullWidth
                      />
                    </Stack>
                  </CardContent>
                </Card>
              )}
            </Grid>
          </Grid>
        )}
      </Stack>
    </Container>
  );
};

export default MarkSchemePage;
