/**
 * Floating "?" button + bug report modal — wisi nad każdą stroną aplikacji.
 *
 * UX:
 *  1. Tap FAB → app robi screenshot CIEnte (przed otwarciem modal'u, więc nie
 *     zawiera samego buttonu/modal'u) przez react-native-view-shot.captureScreen.
 *  2. Modal pokazuje preview screenshota + TextInput + opcję "wyślij bez screena"
 *     + Send/Cancel.
 *  3. Send → POST /bug-reports z message + screen_name (z React Navigation focused
 *     route) + scene_id (jeśli dostarczone w nav params) + base64 screenshot.
 *
 * Screenshot omija FLAG_SECURE bo to in-process render (View.draw → bitmap),
 * nie systemowy MediaProjection.
 */
import type { NavigationContainerRef, ParamListBase } from '@react-navigation/native';
import React, { useCallback, useState } from 'react';
import {
  ActivityIndicator,
  Alert,
  Image,
  KeyboardAvoidingView,
  Modal,
  Platform,
  Pressable,
  ScrollView,
  StyleSheet,
  Switch,
  Text,
  TextInput,
  TouchableOpacity,
  View,
} from 'react-native';
import { captureScreen } from 'react-native-view-shot';

import { GoonClient } from '../api';
import { theme } from '../theme';
import { formatLastPlayback } from '../lib/lastPlayback';

interface Props {
  client: GoonClient | null;
  appVersion: string;
  navRef: NavigationContainerRef<ParamListBase>;
}

// Screens na których FAB jest ukryty — Player ma fullscreen controls i FAB
// nakłada się na progress bar / fullscreen button (bug #f53e50b9 screenshot
// pokazał "?" zasłaniający duration label).
const FAB_HIDDEN_SCREENS = new Set(['Player']);

type MyReport = {
  id: string;
  created_at: string;
  screen_name: string | null;
  message: string;
  response: string | null;
  responded_at: string | null;
  response_seen: boolean;
};

export function BugReportFAB({ client, appVersion, navRef }: Props) {
  const [open, setOpen] = useState(false);
  const [mode, setMode] = useState<'report' | 'messages'>('report');
  const [screenshot, setScreenshot] = useState<string | null>(null);
  const [includeScreenshot, setIncludeScreenshot] = useState(true);
  const [message, setMessage] = useState('');
  const [submitting, setSubmitting] = useState(false);
  // "Your messages": własne zgłoszenia + odpowiedzi admina. `unseen` → kropka na FAB.
  const [myReports, setMyReports] = useState<MyReport[]>([]);
  const [unseen, setUnseen] = useState(0);

  const refreshMine = useCallback(async (): Promise<number> => {
    if (!client) return 0;
    try {
      const r = await client.listMyBugReports();
      setMyReports(r.items);
      setUnseen(r.unseen);
      return r.unseen;
    } catch {
      // offline / brak — kropki po prostu nie pokazujemy
      return 0;
    }
  }, [client]);

  // Poll na starcie + co 90s, żeby kropka pojawiła się po odpowiedzi admina.
  React.useEffect(() => {
    refreshMine();
    const t = setInterval(refreshMine, 90_000);
    return () => clearInterval(t);
  }, [refreshMine]);

  // Wejście w 'messages' = przeczytane → gasimy kropkę (lokalnie + na backendzie).
  const openMessages = useCallback(async () => {
    setMode('messages');
    if (unseen > 0 && client) {
      setUnseen(0);
      try {
        await client.markBugRepliesSeen();
      } catch {
        // brak sieci — kropka wróci przy następnym refreshMine
      }
    }
  }, [unseen, client]);
  // Re-render na zmianę current route, żeby FAB pojawiał/znikał per-screen.
  const [currentRoute, setCurrentRoute] = useState<string | null>(null);
  React.useEffect(() => {
    if (!navRef) return;
    const unsub = navRef.addListener('state', () => {
      try {
        const r = navRef.isReady() ? navRef.getCurrentRoute() : null;
        setCurrentRoute(r?.name ?? null);
      } catch {
        setCurrentRoute(null);
      }
    });
    return unsub;
  }, [navRef]);
  const hidden = currentRoute !== null && FAB_HIDDEN_SCREENS.has(currentRoute);

  const onPress = useCallback(async () => {
    let captured: string | null = null;
    try {
      const dataUri = await captureScreen({
        format: 'jpg',
        quality: 0.6,
        result: 'data-uri',
      });
      captured = dataUri.split(',')[1] || null;
    } catch (e) {
      captured = null;
    }
    setScreenshot(captured);
    setIncludeScreenshot(captured !== null);
    setMessage('');
    // Świeży stan wiadomości; jeśli jest nieprzeczytana odpowiedź, otwórz od razu
    // na 'messages' (user kliknął FAB z kropką, chce ją zobaczyć).
    const n = await refreshMine();
    if (n > 0) {
      await openMessages();
    } else {
      setMode('report');
    }
    setOpen(true);
  }, [refreshMine, openMessages]);

  const submit = useCallback(async () => {
    if (!client) {
      Alert.alert('Bug report', 'No connection to the backend.');
      return;
    }
    if (!message.trim()) {
      Alert.alert('Bug report', 'Enter a short description.');
      return;
    }
    setSubmitting(true);
    let routeName: string | null = null;
    let rawSceneId: string | undefined;
    let extraContext: string[] = [];
    try {
      const route = navRef.isReady() ? navRef.getCurrentRoute() : null;
      routeName = route?.name ?? null;
      const params = (route?.params ?? {}) as Record<string, unknown>;
      rawSceneId =
        (params['sceneId'] as string | undefined) ??
        (params['id'] as string | undefined);
      // Zbieramy non-scene entity IDs (siteId/studioId/performerId/movieId) jako
      // hint dla admina — backend schema ma tylko `scene_id`, ale ekrany takie
      // jak SiteScenes/PerformerScenes/StudioScenes raportują bez konkretnej
      // sceny ("sceny z tej strony nie działają", bug-report bda4383a 2026-05-26).
      // Appendujemy do message zamiast schema-migracji.
      const UUID_RE = /^[0-9a-f-]{36}$/;
      for (const key of ['siteId', 'studioId', 'performerId', 'movieId', 'tagId']) {
        const val = params[key];
        if (typeof val === 'string' && UUID_RE.test(val)) {
          extraContext.push(`${key}=${val}`);
        }
      }
      // Identyfikatory tekstowe (nie-UUID): SiteScenes przekazuje stronę jako
      // `origin` (sitetag) + `name` (display), PerformerScenes/TagScenes bywa
      // po nazwie. Bez tego zgłoszenie "ingest tej strony stoi" nie mówi której
      // (bug-report 14f3a655 2026-06-14). Cap długości, żeby nie wlec listy.
      for (const key of ['origin', 'name', 'sitetag', 'tag', 'q']) {
        const val = params[key];
        if (typeof val === 'string' && val.trim() && !UUID_RE.test(val)) {
          extraContext.push(`${key}=${val.trim().slice(0, 64)}`);
        }
      }
    } catch {
      // navRef nie ready — zostawiamy puste, backend i tak przyjmie nullable
    }
    // Model telefonu + wersja Androida — zawsze przydatne przy playback/UI bugach (żeby
    // nie dopytywać usera "z czego korzystasz"). Platform.constants na Androidzie ma Model
    // + Release; iOS fallback na Version.
    const pc = Platform.constants as unknown as { Model?: string; Release?: string };
    const device =
      Platform.OS === 'android'
        ? `${pc?.Model || 'android'} / Android ${pc?.Release || Platform.Version}`
        : `${Platform.OS} ${Platform.Version}`;
    extraContext.push(`device=${device.slice(0, 48)}`);
    // Ostatnie odtwarzanie (serwer/host/pozycja/błąd) — kluczowe dla "nie gra"/"audio
    // się rozjeżdża", bo mówi KTÓRE źródło i gdzie, niezależnie od ekranu zgłoszenia.
    const play = formatLastPlayback();
    if (play) extraContext.push(`play=${play}`);
    const sceneId =
      rawSceneId && /^[0-9a-f-]{36}$/.test(rawSceneId) ? rawSceneId : null;
    const finalMessage = extraContext.length > 0
      ? `${message.trim()}\n\n[auto-context: ${extraContext.join(', ')}]`
      : message.trim();
    try {
      await client.submitBugReport({
        message: finalMessage,
        screen_name: routeName,
        app_version: appVersion,
        scene_id: sceneId,
        screenshot_b64: includeScreenshot ? screenshot : null,
      });
      setOpen(false);
      setMessage('');
      setScreenshot(null);
      refreshMine();
      Alert.alert('Bug report', 'Sent. Thanks!');
    } catch (e) {
      Alert.alert('Bug report', `Failed to send: ${(e as Error).message}`);
    } finally {
      setSubmitting(false);
    }
  }, [client, message, screenshot, includeScreenshot, appVersion, navRef]);

  return (
    <>
      {!hidden ? (
        <TouchableOpacity style={styles.fab} onPress={onPress} activeOpacity={0.7}>
          <Text style={styles.fabIcon}>?</Text>
          {unseen > 0 ? (
            <View style={styles.fabDot}>
              <Text style={styles.fabDotText}>{unseen > 9 ? '9+' : unseen}</Text>
            </View>
          ) : null}
        </TouchableOpacity>
      ) : null}

      <Modal
        visible={open}
        animationType="slide"
        transparent
        onRequestClose={() => setOpen(false)}
      >
        <KeyboardAvoidingView
          style={styles.backdrop}
          behavior={Platform.OS === 'ios' ? 'padding' : undefined}
        >
          <Pressable style={styles.backdropTap} onPress={() => setOpen(false)} />
          <View style={styles.sheet}>
            <View style={styles.tabRow}>
              <Pressable
                style={[styles.tab, mode === 'report' && styles.tabActive]}
                onPress={() => setMode('report')}
              >
                <Text style={[styles.tabText, mode === 'report' && styles.tabTextActive]}>
                  Report a bug
                </Text>
              </Pressable>
              <Pressable
                style={[styles.tab, mode === 'messages' && styles.tabActive]}
                onPress={openMessages}
              >
                <Text style={[styles.tabText, mode === 'messages' && styles.tabTextActive]}>
                  Your messages{myReports.length ? ` (${myReports.length})` : ''}
                </Text>
                {unseen > 0 ? <View style={styles.tabDot} /> : null}
              </Pressable>
            </View>

            {mode === 'report' ? (
              <>
                {screenshot ? (
                  <View style={styles.preview}>
                    <Image
                      source={{ uri: `data:image/jpeg;base64,${screenshot}` }}
                      style={styles.previewImg}
                      resizeMode="contain"
                    />
                    <View style={styles.toggleRow}>
                      <Text style={styles.toggleLabel}>Attach screenshot</Text>
                      <Switch
                        value={includeScreenshot}
                        onValueChange={setIncludeScreenshot}
                        thumbColor={includeScreenshot ? theme.accent : theme.muted}
                        trackColor={{ true: theme.accentDeep, false: theme.border }}
                      />
                    </View>
                  </View>
                ) : (
                  <Text style={styles.noScreenshot}>
                    Screenshot unavailable (capture failed). Sending text only.
                  </Text>
                )}

                <TextInput
                  style={styles.input}
                  value={message}
                  onChangeText={setMessage}
                  placeholder="What's wrong? Scene title / what you're trying to do / what you saw"
                  placeholderTextColor={theme.mutedDim}
                  multiline
                  autoFocus
                />

                <View style={styles.btnRow}>
                  <TouchableOpacity
                    style={[styles.btn, styles.btnCancel]}
                    onPress={() => setOpen(false)}
                    disabled={submitting}
                  >
                    <Text style={styles.btnText}>Cancel</Text>
                  </TouchableOpacity>
                  <TouchableOpacity
                    style={[styles.btn, styles.btnSend]}
                    onPress={submit}
                    disabled={submitting}
                  >
                    {submitting ? (
                      <ActivityIndicator color={theme.fg} />
                    ) : (
                      <Text style={styles.btnText}>Send</Text>
                    )}
                  </TouchableOpacity>
                </View>
              </>
            ) : (
              <ScrollView style={styles.msgList} contentContainerStyle={{ paddingBottom: 8 }}>
                {myReports.length === 0 ? (
                  <Text style={styles.noScreenshot}>
                    No messages yet. Reports you send show up here with any reply.
                  </Text>
                ) : (
                  myReports.map((r) => (
                    <View key={r.id} style={styles.msgItem}>
                      <Text style={styles.msgYou} numberOfLines={4}>
                        {r.message}
                      </Text>
                      {r.response ? (
                        <View style={styles.replyBox}>
                          <Text style={styles.replyLabel}>Reply from the team</Text>
                          <Text style={styles.replyText}>{r.response}</Text>
                        </View>
                      ) : (
                        <Text style={styles.noReply}>No reply yet</Text>
                      )}
                    </View>
                  ))
                )}
                <TouchableOpacity
                  style={[styles.btn, styles.btnCancel, { marginTop: 12 }]}
                  onPress={() => setOpen(false)}
                >
                  <Text style={styles.btnText}>Close</Text>
                </TouchableOpacity>
              </ScrollView>
            )}
          </View>
        </KeyboardAvoidingView>
      </Modal>
    </>
  );
}

const styles = StyleSheet.create({
  fab: {
    position: 'absolute',
    right: 16,
    bottom: 24,
    width: 44,
    height: 44,
    borderRadius: 22,
    backgroundColor: theme.accentDeep,
    alignItems: 'center',
    justifyContent: 'center',
    elevation: 6,
    shadowColor: '#000',
    shadowOpacity: 0.4,
    shadowRadius: 6,
    shadowOffset: { width: 0, height: 3 },
    opacity: 0.85,
  },
  fabIcon: {
    color: theme.fg,
    fontSize: 22,
    fontWeight: '700',
  },
  backdrop: {
    flex: 1,
    backgroundColor: 'rgba(0,0,0,0.6)',
    justifyContent: 'flex-end',
  },
  backdropTap: {
    flex: 1,
  },
  sheet: {
    backgroundColor: theme.bgElevated,
    borderTopLeftRadius: 20,
    borderTopRightRadius: 20,
    padding: 20,
    paddingBottom: 28,
    maxHeight: '90%',
  },
  title: {
    color: theme.fg,
    fontSize: 18,
    fontWeight: '600',
    marginBottom: 12,
  },
  preview: {
    marginBottom: 12,
  },
  previewImg: {
    width: '100%',
    height: 180,
    borderRadius: 8,
    backgroundColor: theme.bg,
  },
  toggleRow: {
    flexDirection: 'row',
    justifyContent: 'space-between',
    alignItems: 'center',
    marginTop: 8,
  },
  toggleLabel: {
    color: theme.muted,
    fontSize: 14,
  },
  noScreenshot: {
    color: theme.warn,
    fontSize: 13,
    marginBottom: 12,
    textAlign: 'center',
  },
  input: {
    backgroundColor: theme.card,
    borderColor: theme.border,
    borderWidth: 1,
    borderRadius: 8,
    color: theme.fg,
    padding: 12,
    minHeight: 100,
    textAlignVertical: 'top',
    marginBottom: 16,
  },
  btnRow: {
    flexDirection: 'row',
    gap: 12,
  },
  btn: {
    flex: 1,
    paddingVertical: 12,
    borderRadius: 8,
    alignItems: 'center',
    justifyContent: 'center',
  },
  btnCancel: {
    backgroundColor: theme.card,
    borderColor: theme.border,
    borderWidth: 1,
  },
  btnSend: {
    backgroundColor: theme.accent,
  },
  btnText: {
    color: theme.fg,
    fontSize: 15,
    fontWeight: '600',
  },
  fabDot: {
    position: 'absolute',
    top: -2,
    right: -2,
    minWidth: 18,
    height: 18,
    paddingHorizontal: 4,
    borderRadius: 9,
    backgroundColor: theme.accent,
    alignItems: 'center',
    justifyContent: 'center',
    borderWidth: 1.5,
    borderColor: theme.bg,
  },
  fabDotText: { color: theme.fg, fontSize: 10, fontWeight: '800' },
  tabRow: { flexDirection: 'row', gap: 8, marginBottom: 14 },
  tab: {
    flex: 1,
    flexDirection: 'row',
    alignItems: 'center',
    justifyContent: 'center',
    gap: 6,
    paddingVertical: 9,
    borderRadius: 8,
    backgroundColor: theme.card,
    borderColor: theme.border,
    borderWidth: 1,
  },
  tabActive: { borderColor: theme.accent, backgroundColor: theme.bg },
  tabText: { color: theme.muted, fontWeight: '600', fontSize: 14 },
  tabTextActive: { color: theme.fg },
  tabDot: { width: 8, height: 8, borderRadius: 4, backgroundColor: theme.accent },
  msgList: { maxHeight: 380 },
  msgItem: {
    backgroundColor: theme.card,
    borderColor: theme.border,
    borderWidth: 1,
    borderRadius: 10,
    padding: 12,
    marginBottom: 10,
  },
  msgYou: { color: theme.fg, fontSize: 14, lineHeight: 19 },
  replyBox: {
    marginTop: 10,
    borderLeftWidth: 3,
    borderLeftColor: theme.accent,
    paddingLeft: 10,
    paddingVertical: 4,
  },
  replyLabel: {
    color: theme.accent,
    fontSize: 11,
    fontWeight: '800',
    textTransform: 'uppercase',
    letterSpacing: 0.5,
    marginBottom: 3,
  },
  replyText: { color: theme.fg, fontSize: 14, lineHeight: 19 },
  noReply: { color: theme.mutedDim, fontSize: 12, marginTop: 8, fontStyle: 'italic' },
});
