import {
  Modal, Pressable, ScrollView, StyleSheet, Text, View,
  ActivityIndicator, Platform, useWindowDimensions,
} from 'react-native';
import { useQuery } from '@tanstack/react-query';
import { api } from '@/api/client';
import { getCachedParadigm, cacheParadigm } from '@/db/local';
import { Colors, Spacing, Typography, Radius } from '@/theme';
import type { Language } from '@/types';

// ── Label maps ──────────────────────────────────────────────────────────────
const CASE_LABELS: Record<string, string> = {
  nom: 'Nom', gen: 'Gen', dat: 'Dat', acc: 'Acc',
  abl: 'Abl', voc: 'Voc', loc: 'Loc', inst: 'Inst',
};
const TENSE_LABELS: Record<string, string> = {
  pres: 'Present', imperf: 'Imperfect', fut: 'Future',
  perf: 'Perfect', pluperf: 'Pluperfect', futperf: 'Fut. Perf.',
  aor: 'Aorist', pf: 'Perfect',
};
const MOOD_LABELS: Record<string, string> = {
  ind: 'Indicative', subj: 'Subjunctive', opt: 'Optative', imper: 'Imperative',
};
const VOICE_LABELS: Record<string, string> = {
  act: 'Active', pass: 'Passive', mid: 'Middle',
};
const PERSON_LABELS: Record<string, string> = {
  '1sg': '1 sg', '2sg': '2 sg', '3sg': '3 sg',
  '1pl': '1 pl', '2pl': '2 pl', '3pl': '3 pl',
};
const PERSON_ORDER = ['1sg', '2sg', '3sg', '1pl', '2pl', '3pl'];
const TENSE_ORDER  = ['pres', 'imperf', 'fut', 'perf', 'pluperf', 'futperf', 'aor', 'pf'];
const MOOD_ORDER   = ['ind', 'subj', 'opt', 'imper'];
const CASE_ORDER   = ['nom', 'gen', 'dat', 'acc', 'abl', 'voc', 'loc', 'inst'];

// ── Server types ─────────────────────────────────────────────────────────────
interface NounParadigm {
  type: 'noun';
  cases: Record<string, Record<string, string>>;
}
interface VerbParadigm {
  type: 'verb';
  finite?: Record<string, Record<string, Record<string, Record<string, string>>>>;
  nonFinite?: Record<string, Record<string, Record<string, string>>>;
}
type Paradigm = NounParadigm | VerbParadigm;
interface ParadigmResponse { lemma: string; language: string; paradigm: Paradigm | null; }

// ── HTML → readable text ─────────────────────────────────────────────────────
// Raw LSJ HTML has literal \n chars AND <br> between every citation/sense label.
// Use a sentinel to collapse all line-producing markup, strip tags, then convert.
function htmlToText(html: string): string {
  const NL = '\x01';
  return html
    // Raw newlines/tabs in the HTML source are whitespace-formatting, not semantic
    .replace(/[\r\n\t]+/g, ' ')
    // Block-level tags -> sentinel
    .replace(/<\/?(?:div|p|blockquote|li|tr|h[1-6])[^>]*>/gi, NL)
    // Any run of <br> -> sentinel
    .replace(/(?:<br\s*\/?>)+/gi, NL)
    // Strip remaining tags
    .replace(/<[^>]+>/g, '')
    // Decode common HTML entities
    .replace(/&lt;/g, '<').replace(/&gt;/g, '>').replace(/&amp;/g, '&')
    .replace(/&nbsp;/g, ' ').replace(/&emsp;/g, '  ').replace(/&ensp;/g, ' ')
    .replace(/&mdash;/g, '\u2014').replace(/&ndash;/g, '\u2013')
    .replace(/&#(\d+);/g, (_, n) => String.fromCharCode(Number(n)))
    // Collapse horizontal whitespace, then sentinel runs
    .replace(/[ \t]+/g, ' ')
    .replace(/ ?\x01 ?/g, NL)
    .replace(/\x01+/g, NL)
    .replace(/^\x01|\x01$/g, '')
    // Sentinel -> newline
    .replace(/\x01/g, '\n')
    .trim();
}

// ── Paradigm content (no Modal wrapper — usable inside another Modal) ────────
export function ParadigmContent({
  lemma, language, fullDef,
}: {
  lemma: string;
  language: Language;
  fullDef?: string;
}) {
  const lang = language === 'biblical_greek' ? 'greek' : language;
  const { data, isLoading, isError } = useQuery<ParadigmResponse>({
    queryKey: ['paradigm', lemma, lang],
    queryFn: async () => {
      try {
        const data = await api.get(`/api/paradigm?lemma=${encodeURIComponent(lemma)}&language=${lang}`);
        await cacheParadigm(lemma, lang, data);
        return data as ParadigmResponse;
      } catch (err) {
        const cached = await getCachedParadigm(lemma, lang);
        if (cached) return cached as ParadigmResponse;
        throw err;
      }
    },
    staleTime: Infinity,
    retry: false,
  });

  return (
    <>
      {isLoading && <ActivityIndicator color={Colors.primary} style={{ marginVertical: 24 }} />}
      {isError && <Text style={styles.empty}>Paradigm unavailable</Text>}

      {fullDef && (
        <View style={styles.defBox}>
          <Text style={styles.defText}>{htmlToText(fullDef)}</Text>
        </View>
      )}

      {!isLoading && !isError && data?.paradigm == null && (
        <Text style={styles.empty}>No paradigm found</Text>
      )}
      {data?.paradigm?.type === 'noun' && (
        <>
          <Text style={styles.sectionTitle}>Declension</Text>
          <NounTable paradigm={data.paradigm as NounParadigm} />
        </>
      )}
      {data?.paradigm?.type === 'verb' && (
        <VerbTable paradigm={data.paradigm as VerbParadigm} />
      )}
    </>
  );
}

// ── Standalone sheet (wraps ParadigmContent in its own Modal) ────────────────
export default function ParadigmSheet({
  lemma, language, fullDef, onClose,
}: {
  lemma: string;
  language: Language;
  fullDef?: string;
  onClose: () => void;
}) {
  // Explicit height is required — flex:1 on ScrollView only works when the
  // parent has a defined size; maxHeight alone is not enough in Yoga/RN.
  const { height: screenHeight } = useWindowDimensions();
  const sheetHeight = screenHeight * 0.88;

  return (
    <Modal visible animationType="slide" transparent onRequestClose={onClose}>
      <View style={styles.overlay}>
        {/* flex:1 Pressable fills space above the sheet — tap it to close */}
        <Pressable style={styles.backdrop} onPress={onClose} />

        {/* Plain View — no Pressable wrapper so ScrollView gets gestures freely */}
        <View style={[styles.sheet, { height: sheetHeight }]}>
          <View style={styles.header}>
            <View style={styles.handle} />
            <Text style={styles.heading}>{lemma}</Text>
          </View>
          <ScrollView
            style={styles.scrollArea}
            contentContainerStyle={styles.scrollContent}
            showsVerticalScrollIndicator
          >
            <ParadigmContent lemma={lemma} language={language} fullDef={fullDef} />
          </ScrollView>
        </View>
      </View>
    </Modal>
  );
}

// ── Noun / adj declension ────────────────────────────────────────────────────
function NounTable({ paradigm }: { paradigm: NounParadigm }) {
  const cases = paradigm.cases;
  const firstCase = Object.values(cases)[0] ?? {};
  const isAdj = Object.keys(firstCase).some(k => k.includes('.'));

  if (isAdj) {
    const COLS = ['sg.m', 'sg.f', 'sg.n', 'pl.m', 'pl.f', 'pl.n'];
    const HDR  = ['M Sg', 'F Sg', 'N Sg', 'M Pl', 'F Pl', 'N Pl'];
    return (
      <View style={styles.table}>
        <View style={[styles.row, styles.headerRow]}>
          <Text style={[styles.cell, styles.labelCell]} />
          {HDR.map(h => <Text key={h} style={[styles.cell, styles.headerCell]}>{h}</Text>)}
        </View>
        {CASE_ORDER.filter(c => cases[c]).map((c, i) => (
          <View key={c} style={[styles.row, i % 2 === 0 ? styles.rowEven : null]}>
            <Text style={[styles.cell, styles.labelCell]}>{CASE_LABELS[c] ?? c}</Text>
            {COLS.map(col => <Text key={col} style={styles.cell}>{cases[c][col] ?? '—'}</Text>)}
          </View>
        ))}
      </View>
    );
  }

  return (
    <View style={styles.table}>
      <View style={[styles.row, styles.headerRow]}>
        <Text style={[styles.cell, styles.labelCell]} />
        <Text style={[styles.cell, styles.headerCell]}>Sg</Text>
        <Text style={[styles.cell, styles.headerCell]}>Pl</Text>
      </View>
      {CASE_ORDER.filter(c => cases[c]).map((c, i) => (
        <View key={c} style={[styles.row, i % 2 === 0 ? styles.rowEven : null]}>
          <Text style={[styles.cell, styles.labelCell]}>{CASE_LABELS[c] ?? c}</Text>
          <Text style={styles.cell}>{cases[c]['sg'] ?? '—'}</Text>
          <Text style={styles.cell}>{cases[c]['pl'] ?? '—'}</Text>
        </View>
      ))}
    </View>
  );
}

// ── Verb conjugation ─────────────────────────────────────────────────────────
function VerbTable({ paradigm }: { paradigm: VerbParadigm }) {
  const sections: React.ReactNode[] = [];
  const finite = paradigm.finite;

  if (finite) {
    for (const mood of MOOD_ORDER) {
      const moodForms = finite[mood];
      if (!moodForms) continue;
      for (const tense of TENSE_ORDER) {
        const tenseForms = moodForms[tense];
        if (!tenseForms) continue;
        const voices = Object.keys(tenseForms);
        const title = `${TENSE_LABELS[tense] ?? tense} ${MOOD_LABELS[mood] ?? mood}`;
        const persons = PERSON_ORDER.filter(p => voices.some(v => tenseForms[v]?.[p]));
        sections.push(
          <View key={`${mood}-${tense}`}>
            <Text style={styles.sectionTitle}>{title}</Text>
            <View style={styles.table}>
              <View style={[styles.row, styles.headerRow]}>
                <Text style={[styles.cell, styles.labelCell]} />
                {voices.map(v => (
                  <Text key={v} style={[styles.cell, styles.headerCell]}>
                    {VOICE_LABELS[v] ?? v}
                  </Text>
                ))}
              </View>
              {persons.map((p, i) => (
                <View key={p} style={[styles.row, i % 2 === 0 ? styles.rowEven : null]}>
                  <Text style={[styles.cell, styles.labelCell]}>{PERSON_LABELS[p] ?? p}</Text>
                  {voices.map(v => <Text key={v} style={styles.cell}>{tenseForms[v]?.[p] ?? '—'}</Text>)}
                </View>
              ))}
            </View>
          </View>
        );
      }
    }
  }

  // Infinitives
  const inf = paradigm.nonFinite?.inf;
  if (inf) {
    const rows: React.ReactNode[] = [];
    for (const tense of TENSE_ORDER) {
      if (!inf[tense]) continue;
      for (const [voice, form] of Object.entries(inf[tense])) {
        rows.push(
          <View key={`${tense}-${voice}`} style={[styles.row, rows.length % 2 === 0 ? styles.rowEven : null]}>
            <Text style={[styles.cell, styles.labelCell, { width: 120 }]}>
              {`${TENSE_LABELS[tense] ?? tense} ${VOICE_LABELS[voice] ?? voice}`}
            </Text>
            <Text style={styles.cell}>{form as string}</Text>
          </View>
        );
      }
    }
    if (rows.length > 0) {
      sections.push(
        <View key="inf">
          <Text style={styles.sectionTitle}>Infinitives</Text>
          <View style={styles.table}>{rows}</View>
        </View>
      );
    }
  }

  // Participles
  const ptcpl = paradigm.nonFinite?.ptcpl;
  if (ptcpl) {
    const rows: React.ReactNode[] = [];
    for (const tense of TENSE_ORDER) {
      if (!ptcpl[tense]) continue;
      for (const [voice, form] of Object.entries(ptcpl[tense])) {
        rows.push(
          <View key={`${tense}-${voice}`} style={[styles.row, rows.length % 2 === 0 ? styles.rowEven : null]}>
            <Text style={[styles.cell, styles.labelCell, { width: 120 }]}>
              {`${TENSE_LABELS[tense] ?? tense} ${VOICE_LABELS[voice] ?? voice}`}
            </Text>
            <Text style={styles.cell}>{form as string}</Text>
          </View>
        );
      }
    }
    if (rows.length > 0) {
      sections.push(
        <View key="ptcpl">
          <Text style={styles.sectionTitle}>Participles</Text>
          <View style={styles.table}>{rows}</View>
        </View>
      );
    }
  }

  return <>{sections}</>;
}

// ── Styles ───────────────────────────────────────────────────────────────────
const styles = StyleSheet.create({
  overlay: {
    flex: 1,
    // No justifyContent needed — flex:1 backdrop pushes sheet to bottom naturally
  },
  backdrop: {
    flex: 1,
    backgroundColor: 'rgba(0,0,0,0.45)',
  },
  sheet: {
    backgroundColor: Colors.surface,
    borderTopLeftRadius: 20,
    borderTopRightRadius: 20,
    // height set dynamically via useWindowDimensions
  },
  header: {
    paddingTop: Spacing.md,
    paddingHorizontal: Spacing.xl,
    paddingBottom: Spacing.sm,
    borderBottomWidth: 1,
    borderBottomColor: Colors.border,
  },
  handle: {
    width: 40, height: 4,
    backgroundColor: Colors.border,
    borderRadius: Radius.full,
    alignSelf: 'center',
    marginBottom: Spacing.sm,
  },
  heading: {
    fontSize: Typography.size['2xl'] ?? 24,
    fontWeight: Typography.weight.bold,
    color: Colors.primary,
    fontStyle: 'italic',
  },
  // The key: flex:1 makes the ScrollView fill remaining height inside the sheet
  scrollArea: {
    flex: 1,
  },
  scrollContent: {
    padding: Spacing.xl,
    paddingTop: Spacing.md,
    paddingBottom: 48,
    gap: Spacing.lg,
  },
  empty: { color: Colors.textMuted, textAlign: 'center', paddingVertical: Spacing.xl },
  defBox: {
    backgroundColor: Colors.background,
    borderRadius: Radius.md,
    padding: Spacing.md,
    borderLeftWidth: 3,
    borderLeftColor: Colors.parchment,
  },
  defText: {
    fontSize: Typography.size.sm,
    color: Colors.text,
    lineHeight: Typography.size.sm * 1.7,
    fontFamily: Platform.OS === 'ios' ? 'Georgia' : 'serif',
  },
  sectionTitle: {
    fontSize: Typography.size.xs,
    fontWeight: Typography.weight.semibold,
    color: Colors.textMuted,
    textTransform: 'uppercase',
    letterSpacing: 0.8,
    marginBottom: Spacing.xs,
  },
  table: {
    borderWidth: 1,
    borderColor: Colors.border,
    borderRadius: Radius.sm,
    overflow: 'hidden',
  },
  row: { flexDirection: 'row', minHeight: 32 },
  rowEven: { backgroundColor: Colors.background },
  headerRow: { backgroundColor: Colors.primary + '18' },
  cell: {
    flex: 1,
    fontSize: Typography.size.sm,
    color: Colors.text,
    paddingHorizontal: Spacing.xs,
    paddingVertical: 6,
    fontFamily: Platform.OS === 'ios' ? 'Menlo' : 'monospace',
    textAlign: 'center',
  },
  labelCell: {
    flex: 0,
    width: 52,
    fontFamily: Platform.OS === 'ios' ? 'System' : undefined,
    fontWeight: Typography.weight.semibold,
    color: Colors.textMuted,
    fontSize: Typography.size.xs,
    textAlign: 'left',
    paddingLeft: Spacing.xs,
  },
  headerCell: {
    fontFamily: Platform.OS === 'ios' ? 'System' : undefined,
    fontWeight: Typography.weight.semibold,
    color: Colors.primary,
    fontSize: Typography.size.xs,
  },
});
