/**
 * Tira de fotos de un registro. Muestra las ya sincronizadas (tabla `media`)
 * y las locales en cola (`media_outbox`). Permite añadir nuevas desde cámara;
 * antes de encolarlas les estampa un pie de página georreferenciado.
 *
 * Mejoras:
 * - Acceso directo a cámara.
 * - Layout en cuadrícula (grid).
 * - Selección múltiple para borrado masivo.
 * - Visor de imagen integrado.
 * - Procesado de sello en segundo plano (no bloquea).
 */
import * as Location from 'expo-location';
import React, { useCallback, useEffect, useRef, useState } from 'react';
import {
  Alert,
  Image,
  Modal,
  ScrollView,
  StyleSheet,
  Text,
  TouchableOpacity,
  useWindowDimensions,
  View,
} from 'react-native';
import { captureRef } from 'react-native-view-shot';
import { useNavigation } from '@react-navigation/native';
import { NativeStackNavigationProp } from '@react-navigation/native-stack';
import { Media, MediaParentEntity } from '../api/types';
import { absoluteUrl } from '../config';
import { deleteMediaOutbox, enqueueMedia, getMediaOutboxFor, MediaOutboxRow, updateMediaLocalUri } from '../db/outbox';
import { getActiveProjectId, getMediaFor, getProject } from '../db/repositories';
import { RootStackParamList } from '../navigation/types';
import {
  FooterConfig,
  loadFooterConfig,
  RESOLUTION_WIDTHS,
  resolveFieldValue,
  StampMeta,
} from '../photo/footerConfig';
import { FooterOverlay } from '../photo/FooterOverlay';
import { COLORS } from './components';

// ─── tipos internos ────────────────────────────────────────────────────────

interface ComposingTask {
  uuid: string; // Para saber qué registro actualizar al terminar
  uri: string;
  renderW: number;
  renderH: number;
  meta: StampMeta;
  config: FooterConfig;
}

type Nav = NativeStackNavigationProp<RootStackParamList>;

// ─── helpers ───────────────────────────────────────────────────────────────

async function getCoords(): Promise<{ latitude: number; longitude: number } | null> {
  try {
    const { granted } = await Location.requestForegroundPermissionsAsync();
    if (!granted) return null;
    const loc = await Location.getCurrentPositionAsync({ accuracy: Location.Accuracy.Balanced });
    return loc.coords;
  } catch {
    return null;
  }
}

function formatCoords(lat: number, lng: number): string {
  const la = lat >= 0 ? 'N' : 'S';
  const lo = lng >= 0 ? 'E' : 'O';
  return `${Math.abs(lat).toFixed(5)}°${la}, ${Math.abs(lng).toFixed(5)}°${lo}`;
}

// ─── componente ────────────────────────────────────────────────────────────

export function MediaStrip({
  parentEntity,
  parentId,
  canUpload,
}: {
  parentEntity: MediaParentEntity;
  parentId: number;
  canUpload: boolean;
}) {
  const navigation = useNavigation<Nav>();
  const { width: winW } = useWindowDimensions();

  const [synced, setSynced]   = useState<Media[]>([]);
  const [pending, setPending] = useState<MediaOutboxRow[]>([]);
  const [composing, setComposing] = useState<ComposingTask | null>(null);

  // Estados para selección y visor
  const [selectedUuids, setSelectedUuids] = useState<Set<string>>(new Set());
  const [viewerUri, setViewerUri] = useState<string | null>(null);

  const captureViewRef = useRef<View>(null);

  // Cola de tareas de procesado para no perder ninguna
  const queueRef = useRef<ComposingTask[]>([]);

  // ── cargar datos ──────────────────────────────────────────────────────────

  const refresh = useCallback(async () => {
    const [s, p] = await Promise.all([
      getMediaFor(parentEntity, parentId),
      getMediaOutboxFor(parentEntity, parentId),
    ]);
    setSynced(s);
    setPending(p);
  }, [parentEntity, parentId]);

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

  // ── compositing (Procesado en 2º plano) ───────────────────────────────────

  /** Inicia el siguiente procesado de la cola si no hay uno en curso. */
  const processNext = useCallback(() => {
    if (composing || queueRef.current.length === 0) return;
    const next = queueRef.current.shift();
    if (next) setComposing(next);
  }, [composing]);

  useEffect(() => {
    processNext();
  }, [composing, processNext]);

  const onCaptureImageLoaded = useCallback(() => {
    if (!composing) return;

    setTimeout(async () => {
      if (!captureViewRef.current) {
        setComposing(null);
        return;
      }
      try {
        const stampedUri = await captureRef(captureViewRef, {
          format: 'jpg',
          quality: composing.config.quality
        });
        // Actualizar el registro local con la imagen ya sellada
        await updateMediaLocalUri(composing.uuid, stampedUri);
        await refresh();
      } catch (e) {
        console.error('Stamping failed:', e);
      } finally {
        setComposing(null); // Esto disparará el siguiente en la cola vía useEffect
      }
    }, 100);
  }, [composing, refresh]);

  /** Prepara una tarea de estampado y la mete en la cola. */
  async function scheduleStamp(uuid: string, rawUri: string, origW: number, origH: number) {
    const [config, projectId] = await Promise.all([
      loadFooterConfig(),
      getActiveProjectId(),
    ]);

    if (!config.enabled) return;

    let projectName: string | null = null;
    if (projectId != null) {
      const proj = await getProject(projectId);
      projectName = proj?.name ?? null;
    }

    const coordsRaw = await getCoords();
    const meta: StampMeta = {
      projectName,
      date: new Date().toLocaleString('es-ES', {
        year: 'numeric', month: '2-digit', day: '2-digit',
        hour: '2-digit', minute: '2-digit',
      }),
      coordinates: coordsRaw ? formatCoords(coordsRaw.latitude, coordsRaw.longitude) : null,
    };

    const targetW = RESOLUTION_WIDTHS[config.resolution] || 1920;
    const scale   = Math.min(1, targetW / origW);

    queueRef.current.push({
      uuid,
      uri: rawUri,
      renderW: Math.round(origW * scale),
      renderH: Math.round(origH * scale),
      meta,
      config,
    });
    processNext();
  }

  // ── acciones ──────────────────────────────────────────────────────────────

  const handleCapture = async (uri: string, width: number, height: number) => {
    // 1. Guardar la imagen original inmediatamente para respuesta instantánea
    const uuid = await enqueueMedia({
      parentEntity,
      parentId,
      localUri: uri,
      mimeType: 'image/jpeg',
      category: 'image',
    });

    await refresh(); // Mostrar en la galería ya

    // 2. Programar el estampado en segundo plano
    void scheduleStamp(uuid, uri, width, height);
  };

  const onAdd = useCallback(() => {
    navigation.navigate('Camera', { onCapture: handleCapture });
  }, [navigation, handleCapture]);

  const toggleSelect = (uuid: string) => {
    setSelectedUuids((prev) => {
      const next = new Set(prev);
      if (next.has(uuid)) next.delete(uuid);
      else next.add(uuid);
      return next;
    });
  };

  const deleteSelected = async () => {
    const count = selectedUuids.size;
    if (count === 0) return;

    Alert.alert('Eliminar fotos', `¿Deseas eliminar las ${count} fotos seleccionadas?`, [
      { text: 'Cancelar', style: 'cancel' },
      {
        text: 'Eliminar',
        style: 'destructive',
        onPress: async () => {
          for (const uuid of selectedUuids) {
            await deleteMediaOutbox(uuid);
          }
          setSelectedUuids(new Set());
          await refresh();
        },
      },
    ]);
  };

  // ─── render ───────────────────────────────────────────────────────────────

  const isSelectionMode = selectedUuids.size > 0;
  const thumbSize = (winW - 32 - 16) / 3;

  if (synced.length === 0 && pending.length === 0 && !canUpload) return null;

  return (
    <View style={styles.wrapper}>
      <View style={styles.header}>
        <Text style={styles.headerLabel}>
          {isSelectionMode ? `${selectedUuids.size} seleccionadas` : `Fotos (${synced.length + pending.length})`}
        </Text>
        <View style={styles.headerActions}>
          {isSelectionMode ? (
            <TouchableOpacity onPress={deleteSelected} style={styles.deleteAction}>
              <Text style={styles.deleteActionTxt}>Borrar</Text>
            </TouchableOpacity>
          ) : (
            <TouchableOpacity onPress={() => navigation.navigate('PhotoSettings')}>
              <Text style={styles.gear}>⚙</Text>
            </TouchableOpacity>
          )}
        </View>
      </View>

      <View style={styles.grid}>
        {canUpload && (
          <TouchableOpacity
            style={[styles.addBtn, { width: thumbSize, height: thumbSize }]}
            onPress={onAdd}
            disabled={isSelectionMode}
          >
            <Text style={styles.addPlus}>＋</Text>
            <Text style={styles.addText}>Añadir</Text>
          </TouchableOpacity>
        )}

        {pending.map((m) => {
          const isSelected = selectedUuids.has(m.uuid);
          const isProcessing = composing?.uuid === m.uuid || queueRef.current.some(q => q.uuid === m.uuid);

          return (
            <TouchableOpacity
              key={m.uuid}
              style={[styles.thumbWrap, { width: thumbSize, height: thumbSize }, isSelected && styles.selectedThumb]}
              onPress={() => isSelectionMode ? toggleSelect(m.uuid) : setViewerUri(m.local_uri)}
              onLongPress={() => toggleSelect(m.uuid)}
              activeOpacity={0.8}
            >
              <Image source={{ uri: m.local_uri }} style={styles.thumb} />

              {isProcessing && (
                <View style={styles.processingOverlay}>
                  <Text style={styles.processingTxt}>Sellando…</Text>
                </View>
              )}

              {isSelected && (
                <View style={styles.checkOverlay}>
                  <Text style={styles.checkIcon}>✓</Text>
                </View>
              )}
              {m.status === 'error' && (
                <View style={[styles.tag, { backgroundColor: COLORS.danger }]}>
                  <Text style={styles.tagText}>error</Text>
                </View>
              )}
            </TouchableOpacity>
          );
        })}

        {synced.map((m) => (
          <TouchableOpacity
            key={`s${m.id}`}
            style={[styles.thumbWrap, { width: thumbSize, height: thumbSize }]}
            onPress={() => setViewerUri(absoluteUrl(m.url))}
          >
            <Image source={{ uri: absoluteUrl(m.url) }} style={styles.thumb} />
          </TouchableOpacity>
        ))}
      </View>

      <Modal visible={!!viewerUri} transparent={false} animationType="fade">
        <View style={styles.viewerContainer}>
          <Image source={{ uri: viewerUri ?? undefined }} style={styles.viewerImg} resizeMode="contain" />
          <TouchableOpacity style={styles.viewerClose} onPress={() => setViewerUri(null)}>
            <Text style={styles.viewerCloseTxt}>Cerrar</Text>
          </TouchableOpacity>
        </View>
      </Modal>

      {/* COMPOSER OCULTO: Solo renderiza si hay una tarea en la cola */}
      {composing && (
        <View
          ref={captureViewRef}
          collapsable={false}
          style={{
            position: 'absolute',
            top: 0,
            left: -(composing.renderW + 500),
            width: composing.renderW,
            height: composing.renderH,
            overflow: 'hidden',
          }}
        >
          <Image
            source={{ uri: composing.uri }}
            style={{ width: composing.renderW, height: composing.renderH }}
            resizeMode="stretch"
            onLoad={onCaptureImageLoaded}
          />
          <FooterOverlay config={composing.config} meta={composing.meta} imageWidth={composing.renderW} />
        </View>
      )}
    </View>
  );
}

const styles = StyleSheet.create({
  wrapper: { marginVertical: 8 },
  header: {
    flexDirection: 'row',
    justifyContent: 'space-between',
    alignItems: 'center',
    paddingHorizontal: 4,
    marginBottom: 10,
  },
  headerLabel: { fontSize: 14, fontWeight: '700', color: COLORS.muted },
  headerActions: { flexDirection: 'row', alignItems: 'center' },
  gear: { fontSize: 18, color: COLORS.muted, paddingHorizontal: 8 },
  deleteAction: { backgroundColor: COLORS.danger, paddingHorizontal: 12, paddingVertical: 4, borderRadius: 6 },
  deleteActionTxt: { color: '#fff', fontSize: 12, fontWeight: 'bold' },

  grid: {
    flexDirection: 'row',
    flexWrap: 'wrap',
    gap: 8,
  },
  addBtn: {
    borderRadius: 8,
    borderWidth: 1,
    borderColor: COLORS.primary,
    borderStyle: 'dashed',
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: '#fff',
  },
  addPlus: { color: COLORS.primary, fontSize: 24, fontWeight: '700' },
  addText: { color: COLORS.primary, fontSize: 11, marginTop: 2 },

  thumbWrap: {
    borderRadius: 8,
    overflow: 'hidden',
    backgroundColor: COLORS.bg,
    borderWidth: 2,
    borderColor: 'transparent',
  },
  selectedThumb: { borderColor: COLORS.primary },
  thumb: { width: '100%', height: '100%' },

  checkOverlay: {
    ...StyleSheet.absoluteFillObject,
    backgroundColor: 'rgba(31, 111, 67, 0.4)',
    justifyContent: 'center',
    alignItems: 'center',
  },
  checkIcon: { color: '#fff', fontSize: 32, fontWeight: 'bold' },

  processingOverlay: {
    ...StyleSheet.absoluteFillObject,
    backgroundColor: 'rgba(0,0,0,0.5)',
    justifyContent: 'center',
    alignItems: 'center',
  },
  processingTxt: { color: '#fff', fontSize: 10, fontWeight: 'bold' },

  tag: {
    position: 'absolute',
    bottom: 0,
    left: 0,
    right: 0,
    paddingVertical: 1,
    alignItems: 'center',
  },
  tagText: { color: '#fff', fontSize: 9, fontWeight: '700' },

  /* Visor */
  viewerContainer: { flex: 1, backgroundColor: '#000', justifyContent: 'center' },
  viewerImg: { width: '100%', height: '80%' },
  viewerClose: {
    position: 'absolute',
    bottom: 40,
    alignSelf: 'center',
    paddingHorizontal: 30,
    paddingVertical: 12,
    backgroundColor: 'rgba(255,255,255,0.2)',
    borderRadius: 25,
  },
  viewerCloseTxt: { color: '#fff', fontWeight: 'bold' },
});
