import React, { useRef, useState, useCallback, useMemo } from 'react';
import {
  View,
  Text,
  TouchableOpacity,
  TextInput,
  FlatList,
  ActivityIndicator,
  Keyboard,
  Platform,
} from 'react-native';
import { OSMView, LocationButton } from 'expo-osm-sdk';
import type { OSMViewRef, MarkerConfig } from 'expo-osm-sdk';
import * as Location from 'expo-location';
import { getDigiPin, getLatLngFromDigiPin, formatDigiPin, isWithinIndia } from '../core/digipin';
import { autocompleteAddress, looksLikeDigiPin, couldBeDigiPinInput } from '../core/api';
import type { AutocompleteSuggestion } from '../core/api';
import type { MapPinSelectorProps } from '../core/types';
import { styles, COLORS } from '../styles/checkout.native';

// ─── Constants ────────────────────────────────────────────────────────────────

const INDIA_CENTER = { latitude: 20.5937, longitude: 78.9629 };
const OVERVIEW_ZOOM = 5;
const STREET_ZOOM = 16;

/**
 * Carto Positron vector basemap — same source as the web version.
 * Free, no API key required. Attribution required (shown by OSMView natively).
 */
const CARTO_STYLE_URL =
  'https://basemaps.cartocdn.com/gl/positron-gl-style/style.json';

const DEFAULT_BRAND = '#0ea5e9';

/**
 * Custom pin SVG as a data URI — teardrop with white inner circle and brand-colour dot.
 */
function makePinIconDataUri(hex: string): string {
  const c = hex.startsWith('#') ? hex : `#${hex}`;
  return (
    'data:image/svg+xml;utf8,' +
    encodeURIComponent(
      '<svg width="40" height="52" viewBox="0 0 40 52" fill="none" xmlns="http://www.w3.org/2000/svg">' +
        `<path d="M20 0C9.402 0 0 9.402 0 20C0 34 20 52 20 52S40 34 40 20C40 9.402 30.598 0 20 0Z" fill="${c}"/>` +
        '<circle cx="20" cy="20" r="9" fill="white"/>' +
        `<circle cx="20" cy="20" r="5" fill="${c}"/>` +
        '</svg>'
    )
  );
}

// ─── Helper ───────────────────────────────────────────────────────────────────

function computeDigiPinSafe(lat: number, lng: number): string | null {
  if (!isWithinIndia(lat, lng)) return null;
  try {
    return getDigiPin(lat, lng);
  } catch {
    return null;
  }
}

// ─── Component ────────────────────────────────────────────────────────────────

/**
 * Native (iOS/Android) map pin selector.
 * Uses expo-osm-sdk's OSMView with a draggable marker over Carto Positron tiles.
 * Metro resolves this file on native; MapPinSelector.tsx is used on web.
 */
const MapPinSelector: React.FC<MapPinSelectorProps> = ({
  onLocationConfirm,
  defaultLat,
  defaultLng,
  mapHeight = 380,
  theme = 'light',
  enableSearch = true,
  apiKey,
  apiBaseUrl,
  brandColor = DEFAULT_BRAND,
}) => {
  const mapRef = useRef<OSMViewRef>(null);

  const initLat = defaultLat ?? INDIA_CENTER.latitude;
  const initLng = defaultLng ?? INDIA_CENTER.longitude;
  const hasDefault = defaultLat !== undefined && defaultLng !== undefined;

  const [coord, setCoord] = useState({ latitude: initLat, longitude: initLng });
  const [digipin, setDigipin] = useState<string | null>(() =>
    computeDigiPinSafe(initLat, initLng)
  );
  const [mapReady, setMapReady] = useState(false);
  const [locateError, setLocateError] = useState<string | null>(null);

  // ── Search state ──────────────────────────────────────────────────────────
  const [searchQuery, setSearchQuery] = useState('');
  const [suggestions, setSuggestions] = useState<AutocompleteSuggestion[]>([]);
  const [searchLoading, setSearchLoading] = useState(false);
  const [searchError, setSearchError] = useState<string | null>(null);
  const debounceRef = useRef<ReturnType<typeof setTimeout> | null>(null);

  const isDark = theme === 'dark';
  const accentColor = brandColor ?? DEFAULT_BRAND;
  const pinIconUri = useMemo(() => makePinIconDataUri(accentColor), [accentColor]);

  // Resolve numeric height: web passes '380px', native should pass 380 or a number
  const mapHeightNum =
    typeof mapHeight === 'string' ? parseInt(mapHeight, 10) || 380 : mapHeight;

  const handleCoordChange = useCallback(
    (latitude: number, longitude: number) => {
      setCoord({ latitude, longitude });
      setDigipin(computeDigiPinSafe(latitude, longitude));
    },
    []
  );

  // ── Fly map + move pin ────────────────────────────────────────────────────
  const flyToCoord = useCallback((latitude: number, longitude: number) => {
    handleCoordChange(latitude, longitude);
    void mapRef.current?.animateToLocation(latitude, longitude, STREET_ZOOM);
  }, [handleCoordChange]);

  // ── Search input change ───────────────────────────────────────────────────
  const handleSearchChange = useCallback((value: string) => {
    setSearchQuery(value);
    setSearchError(null);
    if (debounceRef.current) clearTimeout(debounceRef.current);

    const trimmed = value.trim();

    if (!trimmed) {
      setSuggestions([]);
      setSearchLoading(false);
      return;
    }

    // DigiPin — offline, instant
    // Keep the text visible in the input; just close the keyboard and dropdown.
    if (looksLikeDigiPin(trimmed)) {
      try {
        const coords = getLatLngFromDigiPin(trimmed);
        flyToCoord(parseFloat(coords.latitude), parseFloat(coords.longitude));
        setSuggestions([]);
        setSearchQuery(formatDigiPin(trimmed));
        Keyboard.dismiss();
      } catch {
        setSearchError('Invalid DigiPin code');
      }
      return;
    }

    // Partial DigiPin — wait for full code, skip autocomplete
    if (couldBeDigiPinInput(trimmed)) {
      setSuggestions([]);
      setSearchLoading(false);
      return;
    }

    if (trimmed.length < 3) {
      setSuggestions([]);
      return;
    }

    setSearchLoading(true);
    debounceRef.current = setTimeout(async () => {
      try {
        // Forward current pin position so the API can bias/bound results nearby
        const results = await autocompleteAddress(
          trimmed, apiKey ?? '', apiBaseUrl, 5,
          coord.latitude, coord.longitude,
        );
        setSuggestions(results);
      } catch (err) {
        setSearchError(err instanceof Error ? err.message : 'Search failed');
        setSuggestions([]);
      } finally {
        setSearchLoading(false);
      }
    }, 400);
  }, [apiKey, apiBaseUrl, flyToCoord]);

  const handleSuggestionSelect = useCallback((s: AutocompleteSuggestion) => {
    flyToCoord(s.coordinates.latitude, s.coordinates.longitude);
    setSearchQuery('');
    setSuggestions([]);
    Keyboard.dismiss();
  }, [flyToCoord]);

  // ── Marker config (draggable checkout pin) ──────────────────────────────────
  const pinMarkers: MarkerConfig[] = useMemo(
    () => [
      {
        id: 'checkout-pin',
        coordinate: coord,
        draggable: true,
        icon: {
          uri: pinIconUri,
          size: 40,
          anchor: { x: 0.5, y: 1.0 }, // tip of the teardrop
        },
      },
    ],
    [coord, pinIconUri]
  );

  // ── Locate-me: delegate to expo-osm-sdk's LocationButton via getCurrentLocation ──
  const getCurrentLocation = useCallback(async () => {
    setLocateError(null);
    const { status } = await Location.requestForegroundPermissionsAsync();
    if (status !== Location.PermissionStatus.GRANTED) {
      throw new Error('Location permission denied');
    }
    const loc = await Location.getCurrentPositionAsync({
      accuracy: Location.Accuracy.High,
    });
    return { latitude: loc.coords.latitude, longitude: loc.coords.longitude };
  }, []);

  // ── Confirm handler ──────────────────────────────────────────────────────────
  const handleConfirm = useCallback(() => {
    if (digipin) {
      onLocationConfirm(coord.latitude, coord.longitude, digipin);
    }
  }, [coord, digipin, onLocationConfirm]);

  const isInIndia = digipin !== null;
  const canConfirm = isInIndia && mapReady;

  return (
    <View style={[styles.mapWrapper, isDark && { backgroundColor: COLORS.bgDark }]}>

      {/* ── Step header ── */}
      <View style={[styles.stepHeader, isDark && styles.stepHeaderDark]}>
        <View style={styles.stepBadge}>
          <Text style={styles.stepBadgeText}>1</Text>
        </View>
        <View style={styles.stepText}>
          <Text style={[styles.stepTitle, isDark && styles.textLight]}>
            Pin Your Location
          </Text>
          <Text style={[styles.stepSub, isDark && styles.textMutedStyle]}>
            Tap the map or drag the pin to your exact location
          </Text>
        </View>
      </View>

      {/* ── Search bar ── */}
      {enableSearch && (
        <View style={[styles.searchBar, isDark && styles.searchBarDark]}>
          <View style={[styles.searchInputWrap, isDark && styles.searchInputWrapDark]}>
            <TextInput
              style={[styles.searchInput, isDark && styles.searchInputDark]}
              placeholder="Search address, 'pharmacy near me', or DigiPin…"
              placeholderTextColor={isDark ? COLORS.textFaint : COLORS.textMuted}
              value={searchQuery}
              onChangeText={handleSearchChange}
              autoCapitalize="characters"
              autoCorrect={false}
              returnKeyType="search"
              clearButtonMode={Platform.OS === 'ios' ? 'while-editing' : 'never'}
            />
            {searchLoading && (
              <ActivityIndicator
                size="small"
                color={accentColor}
                style={styles.searchSpinner}
              />
            )}
          </View>

          {searchError ? (
            <Text style={styles.searchError}>{searchError}</Text>
          ) : null}

          {suggestions.length > 0 ? (
            <FlatList
              data={suggestions}
              keyExtractor={(_, i) => String(i)}
              style={[styles.searchDropdown, isDark && styles.searchDropdownDark]}
              keyboardShouldPersistTaps="handled"
              renderItem={({ item }) => (
                <TouchableOpacity
                  style={[styles.searchSuggestion, isDark && styles.searchSuggestionDark]}
                  onPress={() => handleSuggestionSelect(item)}
                  activeOpacity={0.7}
                >
                  <Text
                    style={[styles.searchSuggestionName, isDark && styles.textLight]}
                    numberOfLines={1}
                  >
                    {item.displayName}
                  </Text>
                  {item.address && item.address !== item.displayName ? (
                    <Text
                      style={[styles.searchSuggestionSub, isDark && styles.textMutedStyle]}
                      numberOfLines={1}
                    >
                      {item.address}
                    </Text>
                  ) : null}
                </TouchableOpacity>
              )}
            />
          ) : null}

          <Text style={[styles.searchHint, isDark && styles.textMutedStyle]}>
            <Text style={styles.searchHintChip}>DigiPin </Text>
            Type your DigiPin for instant offline lookup
          </Text>
        </View>
      )}

      {/* ── Map area ── */}
      <View style={{ height: mapHeightNum, position: 'relative' }}>
        <OSMView
          ref={mapRef}
          style={{ flex: 1 }}
          initialCenter={{ latitude: initLat, longitude: initLng }}
          initialZoom={hasDefault ? STREET_ZOOM : OVERVIEW_ZOOM}
          styleUrl={CARTO_STYLE_URL}
          markers={pinMarkers}
          showUserLocation={false}
          scrollEnabled
          zoomEnabled
          rotateEnabled
          onMapReady={() => setMapReady(true)}
          onPress={(coordinate) => {
            handleCoordChange(coordinate.latitude, coordinate.longitude);
          }}
          onMarkerDragEnd={(_markerId, coordinate) => {
            handleCoordChange(coordinate.latitude, coordinate.longitude);
          }}
        />

        {/* DigiPin badge overlay */}
        {digipin ? (
          <View
            style={[styles.digipinBadge, isDark && styles.digipinBadgeDark]}
            pointerEvents="none"
          >
            <Text style={[styles.digipinLabel, isDark && styles.digipinLabelDark]}>
              DigiPin
            </Text>
            <Text style={styles.digipinCode}>{digipin}</Text>
          </View>
        ) : null}

        {/* Out-of-India notice */}
        {!isInIndia && mapReady ? (
          <View style={styles.mapNotice} pointerEvents="none">
            <Text style={styles.mapNoticeText}>Move map to India to get a DigiPin</Text>
          </View>
        ) : null}

        {/* Locate-me button — expo-osm-sdk's built-in LocationButton */}
        <LocationButton
          style={styles.locateBtn}
          color={accentColor}
          size={44}
          getCurrentLocation={getCurrentLocation}
          onLocationFound={({ latitude, longitude }) => {
            handleCoordChange(latitude, longitude);
            void mapRef.current?.animateToLocation(latitude, longitude, STREET_ZOOM);
          }}
          onLocationError={(err) => setLocateError(err)}
        />

        {/* Geo error toast */}
        {locateError ? (
          <View style={styles.geoError}>
            <Text style={styles.geoErrorText}>{locateError}</Text>
            <TouchableOpacity
              style={styles.geoErrorDismiss}
              onPress={() => setLocateError(null)}
              accessibilityLabel="Dismiss error"
            >
              <Text style={styles.geoErrorDismissText}>×</Text>
            </TouchableOpacity>
          </View>
        ) : null}
      </View>

      {/* ── Coordinates strip ── */}
      {mapReady ? (
        <View style={[styles.coordsStrip, isDark && styles.coordsStripDark]}>
          <Text style={[styles.coordsText, isDark && styles.coordsTextDark]}>
            {coord.latitude.toFixed(5)}° N · {coord.longitude.toFixed(5)}° E
          </Text>
        </View>
      ) : null}

      {/* ── Confirm action bar ── */}
      <View style={[styles.mapActions, isDark && styles.mapActionsDark]}>
        <TouchableOpacity
          style={[styles.btnPrimary, !canConfirm && styles.btnDisabled]}
          onPress={handleConfirm}
          disabled={!canConfirm}
          activeOpacity={0.8}
          accessibilityRole="button"
          accessibilityLabel="Confirm location"
        >
          <Text style={styles.btnPrimaryText}>Confirm Location  →</Text>
        </TouchableOpacity>

        {!isInIndia && mapReady ? (
          <Text style={[styles.mapHint, isDark && styles.mapHintDark]}>
            Move the pin to a location in India to continue
          </Text>
        ) : null}
      </View>
    </View>
  );
};

export default MapPinSelector;
