import { useState, useCallback, useEffect } from 'react';
import {
  View, Text, ScrollView, TouchableOpacity, StyleSheet,
  ActivityIndicator, Modal, Pressable, useWindowDimensions,
} from 'react-native';
import { useLocalSearchParams, useNavigation } from 'expo-router';
import { useQuery, useMutation } from '@tanstack/react-query';
import { api } from '@/api/client';
import { adaptParseResponse } from '@/api/parseAdapter';
import { adaptPassagePayload } from '@/api/passageAdapter';
import { getCachedPassage, cachePassage, upsertSrsCard, recordCompletion, getCachedParse, cacheParse, getCachedParadigm, cacheParadigm } from '@/db/local';
import { useNetInfo } from '@react-native-community/netinfo';
import { Colors, Spacing, Typography, Radius } from '@/theme';
import { ParadigmContent } from '@/screens/ParadigmSheet';
import type { Language, Passage, ParseResult } from '@/types';

// Tokenise a passage into words and punctuation/whitespace runs.
function tokenise(text: string): Array<{ index: number; token: string; isWord: boolean }> {
  const tokens: Array<{ index: number; token: string; isWord: boolean }> = [];
  // Split on whitespace and punctuation boundaries while keeping delimiters
  const parts = text.split(/(\s+|[,;:.·!?⟨⟩()\[\]"'—–-]+)/u);
  let idx = 0;
  for (const part of parts) {
    if (!part) continue;
    const isWord = /\p{L}/u.test(part);
    tokens.push({ index: idx++, token: part, isWord });
  }
  return tokens;
}

// Extract unique word forms from passage text (same split logic as tokenise).
function uniqueWordForms(text: string): string[] {
  return [
    ...new Set(
      text
        .split(/[\s,;:.·!?⟨⟩()[\]"'—–\-]+/u)
        .map((w) => w.replace(/^[^\p{L}]+|[^\p{L}]+$/gu, ''))
        .filter((w) => w.length > 1 && /\p{L}/u.test(w)),
    ),
  ];
}

export default function ReadScreen() {
  const { id } = useLocalSearchParams<{ id: string }>();
  const navigation = useNavigation();
  const [selectedWord, setSelectedWord] = useState<string | null>(null);
  const [wordsParsed, setWordsParsed] = useState(0);
  const { isConnected } = useNetInfo();

  // Load passage — try server, fall back to cache
  const { data: passage, isLoading } = useQuery<Passage>({
    queryKey: ['passage', id],
    queryFn: async () => {
      // Try local cache first (seeded at install)
      const cached = await getCachedPassage(id);
      if (cached) return cached;
      // Fall back to server
      try {
        const raw = await api.get(`/api/passage/id/${id}`);
        const passage = adaptPassagePayload(raw);
        await cachePassage(passage);
        return passage;
      } catch {
        throw new Error('Passage unavailable offline');
      }
    },
  });

  // Parse a tapped word
  const parseQuery = useQuery<ParseResult[]>({
    queryKey: ['parse', selectedWord],
    queryFn: async () => {
      try {
        const raw = await api.get(`/api/parse?q=${encodeURIComponent(selectedWord!)}`);
        await cacheParse(selectedWord!, raw);
        return adaptParseResponse(raw);
      } catch (err) {
        const cached = await getCachedParse(selectedWord!);
        if (cached) return adaptParseResponse(cached);
        throw err;
      }
    },
    enabled: !!selectedWord,
    staleTime: Infinity,
    retry: false,
  });

  // When a passage loads while online, silently pre-cache parse + paradigm for
  // every word so the passage is fully parseable and all paradigm tables are
  // available offline on future reads.
  useEffect(() => {
    if (!passage || !isConnected) return;
    let cancelled = false;

    async function prefetch() {
      const words = uniqueWordForms(passage!.passageText);
      const lemmasSeen = new Set<string>();

      for (const word of words) {
        if (cancelled) return;

        // Use cached parse if we already have it; otherwise fetch and store.
        let raw: unknown = await getCachedParse(word);
        if (!raw) {
          try {
            raw = await api.get(`/api/parse?q=${encodeURIComponent(word)}`);
            if (!cancelled && raw) await cacheParse(word, raw);
          } catch { continue; }
        }

        // For each unique lemma in the parse results, also cache the paradigm.
        for (const entry of (raw as any)?.entries ?? []) {
          const lemma: string | undefined = entry?.lemma?.headword;
          if (!lemma || lemmasSeen.has(lemma)) continue;
          lemmasSeen.add(lemma);

          const rawLang: string = entry?.lemma?.language ?? passage!.language;
          const lang = rawLang === 'biblical_greek' ? 'greek' : rawLang;

          if (cancelled) return;
          const alreadyCached = await getCachedParadigm(lemma, lang);
          if (alreadyCached) continue;

          try {
            const paradigm = await api.get(
              `/api/paradigm?lemma=${encodeURIComponent(lemma)}&language=${lang}`,
            );
            if (!cancelled) await cacheParadigm(lemma, lang, paradigm);
          } catch { /* no paradigm for this lemma — skip */ }
        }
      }
    }

    void prefetch();
    return () => { cancelled = true; };
  }, [passage?.id, isConnected]);

  // Add to SRS deck
  const addToSrsMutation = useMutation({
    mutationFn: async (result: ParseResult) => {
      if (!result.lemmaId) return;
      const today = new Date().toISOString().slice(0, 10);
      await upsertSrsCard({
        lemmaId: result.lemmaId,
        headword: result.lemma,
        shortDef: result.shortDef ?? '',
        language: passage!.language,
        interval: 1,
        easeFactor: 2.5,
        repetitions: 0,
        nextReview: today,
      });
    },
  });

  const onWordPress = useCallback((word: string) => {
    setSelectedWord(word);
    setWordsParsed((n) => n + 1);
  }, []);

  async function markDone() {
    if (passage) await recordCompletion(passage.id, wordsParsed);
    navigation.goBack();
  }

  if (isLoading) {
    return (
      <View style={styles.centered}>
        <ActivityIndicator color={Colors.primary} size="large" />
      </View>
    );
  }

  if (!passage) {
    return (
      <View style={styles.centered}>
        <Text style={styles.errorText}>Passage unavailable</Text>
      </View>
    );
  }

  const tokens = tokenise(passage.passageText);

  return (
    <>
      <ScrollView style={styles.container} contentContainerStyle={styles.content}>
        {/* Header */}
        <View style={styles.header}>
          <Text style={styles.author}>{passage.author}</Text>
          <Text style={styles.workRef}>{passage.work} · {passage.reference}</Text>
        </View>

        {/* Context card */}
        {passage.contextCard?.contextNote && (
          <View style={styles.contextCard}>
            <Text style={styles.contextText}>{passage.contextCard.contextNote}</Text>
          </View>
        )}

        {/* Passage text — tap any word */}
        <View style={styles.passageWrapper}>
          <Text style={styles.passageBase}>
            {tokens.map((t) =>
              t.isWord ? (
                <Text
                  key={t.index}
                  style={styles.word}
                  onPress={() => onWordPress(t.token)}
                >
                  {t.token}
                </Text>
              ) : (
                <Text key={t.index} style={styles.punct}>
                  {t.token}
                </Text>
              ),
            )}
          </Text>
        </View>

        {/* Translation (collapsible) */}
        {passage.translationText && (
          <CollapsibleTranslation text={passage.translationText} />
        )}

        {/* Done button */}
        <TouchableOpacity style={styles.doneButton} onPress={markDone}>
          <Text style={styles.doneButtonText}>Mark as done</Text>
        </TouchableOpacity>
      </ScrollView>

      {/* Word parse popup */}
      <ParseModal
        word={selectedWord}
        results={parseQuery.data}
        isLoading={parseQuery.isLoading}
        isError={parseQuery.isError}
        error={parseQuery.error as Error | null}
        passageLanguage={passage.language}
        onClose={() => setSelectedWord(null)}
        onAddToSrs={(r) => addToSrsMutation.mutate(r)}
        addingLemmaId={addToSrsMutation.isPending ? '' : null}
      />
    </>
  );
}

function CollapsibleTranslation({ text }: { text: string }) {
  const [open, setOpen] = useState(false);
  return (
    <View style={styles.translationCard}>
      <TouchableOpacity onPress={() => setOpen((o) => !o)} style={styles.translationHeader}>
        <Text style={styles.translationLabel}>Translation</Text>
        <Text style={styles.translationToggle}>{open ? '▲' : '▼'}</Text>
      </TouchableOpacity>
      {open && <Text style={styles.translationText}>{text}</Text>}
    </View>
  );
}

function ParseModal({
  word, results, isLoading, isError, error, passageLanguage, onClose, onAddToSrs, addingLemmaId,
}: {
  word: string | null;
  results?: ParseResult[];
  isLoading: boolean;
  isError: boolean;
  error: Error | null;
  passageLanguage: Language;
  onClose: () => void;
  onAddToSrs: (r: ParseResult) => void;
  addingLemmaId: string | null;
}) {
  const [paradigmResult, setParadigmResult] = useState<ParseResult | null>(null);
  const { height: screenHeight } = useWindowDimensions();

  if (!word) return null;

  const inParadigm = paradigmResult !== null;
  // Always use explicit height — flex:1 on ScrollView won't work with only maxHeight.
  const sheetHeight = screenHeight * 0.85;

  return (
    <Modal
      visible
      animationType="slide"
      transparent
      onRequestClose={inParadigm ? () => setParadigmResult(null) : onClose}
    >
      <View style={styles.modalOverlay}>
        {/* flex:1 Pressable fills space above the sheet — tap it to close */}
        <Pressable
          style={styles.modalBackdrop}
          onPress={inParadigm ? () => setParadigmResult(null) : onClose}
        />
        {/* Plain View — no Pressable wrapper so ScrollView gets gestures freely */}
        <View style={[styles.modalSheet, { height: sheetHeight }]}>
          <View style={styles.handle} />

          {inParadigm ? (
            /* ── Paradigm view — rendered inline, no second Modal ── */
            <>
              <View style={styles.paradigmHeader}>
                <TouchableOpacity onPress={() => setParadigmResult(null)}>
                  <Text style={styles.backText}>← {word}</Text>
                </TouchableOpacity>
                <Text style={styles.modalWord}>{paradigmResult.lemma}</Text>
              </View>
              <ScrollView
                style={styles.sheetScrollArea}
                contentContainerStyle={styles.paradigmScrollContent}
                showsVerticalScrollIndicator
              >
                <ParadigmContent
                  lemma={paradigmResult.lemma}
                  language={paradigmResult.language ?? passageLanguage}
                  fullDef={paradigmResult.fullDef}
                />
              </ScrollView>
            </>
          ) : (
            /* ── Parse results view ── */
            <>
              <View style={styles.parseHeader}>
                <Text style={styles.modalWord}>{word}</Text>
              </View>
              <ScrollView
                style={styles.sheetScrollArea}
                contentContainerStyle={styles.parseScrollContent}
                showsVerticalScrollIndicator={false}
              >
                {isLoading && <ActivityIndicator color={Colors.primary} />}
                {isError && (
                  <Text style={styles.noResults}>
                    {error?.message?.includes('401')
                      ? 'Sign in required'
                      : `Parse error: ${error?.message ?? 'unknown'}`}
                  </Text>
                )}
                {results?.map((r, i) => (
                  <View key={i} style={styles.parseResult}>
                    <View style={styles.parseResultHeader}>
                      <Text style={styles.parseLemma}>{r.lemma}</Text>
                      <Text style={styles.parsePos}>{r.partOfSpeech}</Text>
                    </View>
                    <Text style={styles.parseMorphology}>{r.morphology}</Text>
                    {r.shortDef && <Text style={styles.parseDef}>{r.shortDef}</Text>}
                    <View style={styles.parseActions}>
                      <TouchableOpacity
                        style={styles.actionBtn}
                        onPress={() => setParadigmResult(r)}
                      >
                        <Text style={styles.actionBtnText}>Paradigm</Text>
                      </TouchableOpacity>
                      {r.lemmaId && (
                        <TouchableOpacity
                          style={styles.addToSrsButton}
                          onPress={() => onAddToSrs(r)}
                        >
                          <Text style={styles.addToSrsText}>+ Deck</Text>
                        </TouchableOpacity>
                      )}
                    </View>
                  </View>
                ))}
                {results?.length === 0 && !isError && (
                  <Text style={styles.noResults}>No parse results found</Text>
                )}
              </ScrollView>
            </>
          )}
        </View>
      </View>
    </Modal>
  );
}

const styles = StyleSheet.create({
  container: { flex: 1, backgroundColor: Colors.background },
  content: { padding: Spacing.lg, gap: Spacing.lg, paddingBottom: 80 },
  centered: { flex: 1, alignItems: 'center', justifyContent: 'center', padding: Spacing.xl },
  header: { gap: 4 },
  author: {
    fontSize: Typography.size.sm,
    fontWeight: Typography.weight.semibold,
    color: Colors.primary,
    textTransform: 'uppercase',
    letterSpacing: 0.8,
  },
  workRef: { fontSize: Typography.size.xs, color: Colors.textMuted },
  contextCard: {
    borderLeftWidth: 3,
    borderLeftColor: Colors.parchment,
    paddingLeft: Spacing.md,
  },
  contextText: {
    fontSize: Typography.size.sm,
    color: Colors.textMuted,
    fontStyle: 'italic',
    lineHeight: Typography.size.sm * Typography.lineHeight.normal,
  },
  passageWrapper: {
    backgroundColor: Colors.surface,
    borderRadius: Radius.lg,
    borderWidth: 1,
    borderColor: Colors.border,
    padding: Spacing.xl,
  },
  passageBase: {
    fontSize: Typography.size.lg,
    lineHeight: Typography.size.lg * Typography.lineHeight.relaxed,
    color: Colors.text,
    flexWrap: 'wrap',
  },
  word: {
    color: Colors.text,
    textDecorationLine: 'underline',
    textDecorationStyle: 'dotted',
    textDecorationColor: Colors.parchment,
  },
  punct: { color: Colors.textMuted },
  translationCard: {
    backgroundColor: Colors.surface,
    borderRadius: Radius.md,
    borderWidth: 1,
    borderColor: Colors.border,
    overflow: 'hidden',
  },
  translationHeader: {
    flexDirection: 'row',
    justifyContent: 'space-between',
    padding: Spacing.md,
  },
  translationLabel: {
    fontSize: Typography.size.sm,
    fontWeight: Typography.weight.medium,
    color: Colors.textMuted,
    textTransform: 'uppercase',
    letterSpacing: 0.8,
  },
  translationToggle: { color: Colors.textMuted },
  translationText: {
    fontSize: Typography.size.sm,
    color: Colors.text,
    lineHeight: Typography.size.sm * Typography.lineHeight.normal,
    padding: Spacing.md,
    paddingTop: 0,
  },
  doneButton: {
    backgroundColor: Colors.primary,
    borderRadius: Radius.md,
    paddingVertical: Spacing.md,
    alignItems: 'center',
  },
  doneButtonText: {
    color: '#fff',
    fontWeight: Typography.weight.semibold,
    fontSize: Typography.size.base,
  },
  errorText: { color: Colors.error, fontSize: Typography.size.base },
  // Modal
  modalOverlay: {
    flex: 1,
  },
  modalBackdrop: {
    flex: 1,
    backgroundColor: 'rgba(0,0,0,0.4)',
  },
  modalSheet: {
    backgroundColor: Colors.surface,
    borderTopLeftRadius: 20,
    borderTopRightRadius: 20,
    minHeight: 200,
    // height set dynamically via useWindowDimensions
  },
  handle: {
    width: 40, height: 4,
    backgroundColor: Colors.border,
    borderRadius: Radius.full,
    alignSelf: 'center',
    marginTop: Spacing.md,
    marginBottom: Spacing.sm,
  },
  // Parse results layout
  parseHeader: {
    paddingHorizontal: Spacing.xl,
    paddingBottom: Spacing.sm,
  },
  sheetScrollArea: { flex: 1 },
  parseScrollContent: {
    paddingHorizontal: Spacing.xl,
    paddingBottom: 48,
    gap: Spacing.md,
  },
  // Paradigm layout
  paradigmHeader: {
    paddingHorizontal: Spacing.xl,
    paddingBottom: Spacing.sm,
    gap: Spacing.xs,
    borderBottomWidth: 1,
    borderBottomColor: Colors.border,
  },
  backText: {
    fontSize: Typography.size.sm,
    color: Colors.primary,
    fontWeight: Typography.weight.medium,
    marginBottom: Spacing.xs,
  },
  paradigmScrollContent: {
    paddingHorizontal: Spacing.xl,
    paddingTop: Spacing.md,
    paddingBottom: 48,
    gap: Spacing.lg,
  },
  modalWord: {
    fontSize: Typography.size.xl,
    fontWeight: Typography.weight.bold,
    color: Colors.primary,
    fontStyle: 'italic',
  },
  parseResult: {
    borderTopWidth: 1,
    borderTopColor: Colors.border,
    paddingTop: Spacing.md,
    gap: Spacing.sm,
  },
  parseResultHeader: { flexDirection: 'row', alignItems: 'baseline', gap: Spacing.sm },
  parseLemma: {
    fontSize: Typography.size.lg,
    fontWeight: Typography.weight.semibold,
    color: Colors.text,
  },
  parsePos: {
    fontSize: Typography.size.xs,
    color: Colors.textMuted,
    textTransform: 'uppercase',
    letterSpacing: 0.8,
  },
  parseMorphology: {
    fontSize: Typography.size.xs,
    color: Colors.textMuted,
    backgroundColor: Colors.background,
    padding: Spacing.xs,
    borderRadius: Radius.sm,
  },
  parseDef: {
    fontSize: Typography.size.sm,
    color: Colors.text,
    lineHeight: Typography.size.sm * Typography.lineHeight.normal,
  },
  parseActions: { flexDirection: 'row', gap: Spacing.sm, flexWrap: 'wrap' },
  actionBtn: {
    backgroundColor: Colors.primary + '18',
    borderRadius: Radius.sm,
    paddingHorizontal: Spacing.sm,
    paddingVertical: 4,
  },
  actionBtnText: {
    fontSize: Typography.size.xs,
    color: Colors.primary,
    fontWeight: Typography.weight.semibold,
  },
  addToSrsButton: {
    alignSelf: 'flex-start',
    backgroundColor: Colors.accentLight,
    borderRadius: Radius.sm,
    paddingHorizontal: Spacing.sm,
    paddingVertical: 4,
  },
  addToSrsText: {
    fontSize: Typography.size.xs,
    color: Colors.accent,
    fontWeight: Typography.weight.semibold,
  },
  noResults: { color: Colors.textMuted, fontSize: Typography.size.sm, textAlign: 'center' },
});
