/**
 * S5. Place card — MAPMAP-NAV-UI-SPEC.md.
 *
 * Peek sheet with name/address/distance and a full-width Directions CTA,
 * plus a compact Save/Share row; drag up (handled by the shared `Sheet`
 * primitive) reveals coordinates. Category is part of the spec's "if
 * available" detail list but isn't in `SelectedPlace`/`SearchResult` (see
 * MapmapNav.types.ts) — there's no data to show it with, so it's omitted
 * here (see T6 report).
 *
 * The peek's visible height is sized to its own content (measured via
 * onLayout on `peekBlock`, not the fixed detail section revealed on drag-up)
 * rather than a flat fraction of the window — a bare "Dropped pin" (name +
 * coordinates only, no address/distance) and a rich search result (name +
 * address + distance) render very different amounts of content, and a fixed
 * fraction left the Directions button floating over dead space for the
 * sparser case.
 */
import React, { useCallback, useState } from "react";
import {
  LayoutChangeEvent,
  Pressable,
  StyleSheet,
  Text,
  useWindowDimensions,
  View,
} from "react-native";
import Svg, { Line, Path } from "react-native-svg";
import { Button, Chip, Sheet } from "../../components";
import type { SheetSnapPoint } from "../../components";
import { minTouchTarget, radius, spacing, theme, typeScale } from "../../theme";
import type { SelectedPlace, Units } from "../../state/types";
import { formatDistance } from "../mapChrome";

export type PlaceCardProps = {
  place: SelectedPlace;
  /** Peek (content-sized) vs dragged-up detail view. */
  expanded: boolean;
  onExpandedChange: (expanded: boolean) => void;
  onDirections: () => void;
  onDismiss: () => void;
  onShare?: () => void;
  /** Bookmark this place. Optional — the Save button only renders when
   * provided (default no-op keeps callers that don't wire it up unaffected). */
  onSave?: () => void;
  units: Units;
};

// Mirrors Sheet.tsx's `handleArea` (paddingVertical 8 * 2 + the 4px handle)
// plus a couple of px of breathing room — Sheet doesn't expose this as a
// prop, so we account for it here when sizing the peek to content.
const SHEET_HANDLE_HEIGHT = 24;
// Sensible bounds so a very long/short place name (numberOfLines caps it at
// 2 either way) can't produce a peek that's uncomfortably short or tall.
const PEEK_MIN_HEIGHT = 180;
const PEEK_MAX_FRACTION = 0.42;
// Used only until the first onLayout measurement lands, to avoid a jump.
const PEEK_FALLBACK_FRACTION = 0.24;

function formatCoords(position: SelectedPlace["position"]): string {
  return `${position.latitude.toFixed(5)}, ${position.longitude.toFixed(5)}`;
}

function CloseIcon(): React.JSX.Element {
  return (
    <Svg width={14} height={14} viewBox="0 0 24 24">
      <Line x1={5} y1={5} x2={19} y2={19} stroke={theme.textSecondary} strokeWidth={2.4} strokeLinecap="round" />
      <Line x1={19} y1={5} x2={5} y2={19} stroke={theme.textSecondary} strokeWidth={2.4} strokeLinecap="round" />
    </Svg>
  );
}

function ShareIcon(): React.JSX.Element {
  return (
    <Svg width={16} height={16} viewBox="0 0 24 24">
      <Path
        d="M12 3v11M12 3 8 7M12 3l4 4M5 13v6a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2v-6"
        stroke={theme.textPrimary}
        strokeWidth={2}
        strokeLinecap="round"
        strokeLinejoin="round"
        fill="none"
      />
    </Svg>
  );
}

function BookmarkIcon(): React.JSX.Element {
  return (
    <Svg width={16} height={16} viewBox="0 0 24 24">
      <Path
        d="M6 3.5h12a1 1 0 0 1 1 1V21l-7-4-7 4V4.5a1 1 0 0 1 1-1Z"
        stroke={theme.textPrimary}
        strokeWidth={2}
        strokeLinejoin="round"
        fill="none"
      />
    </Svg>
  );
}

function SecondaryActionButton({
  icon,
  label,
  onPress,
  testID,
}: {
  icon: React.ReactNode;
  label: string;
  onPress: () => void;
  testID?: string;
}): React.JSX.Element {
  return (
    <Pressable
      onPress={onPress}
      style={({ pressed }) => [styles.secondaryAction, pressed && styles.secondaryActionPressed]}
      accessibilityRole="button"
      accessibilityLabel={label}
      testID={testID}
    >
      {icon}
      <Text style={styles.secondaryActionText}>{label}</Text>
    </Pressable>
  );
}

export function PlaceCard({
  place,
  expanded,
  onExpandedChange,
  onDirections,
  onDismiss,
  onShare,
  onSave,
  units,
}: PlaceCardProps): React.JSX.Element {
  const { height: windowHeight } = useWindowDimensions();
  const snapPoint: SheetSnapPoint = expanded ? "expanded" : "peek";
  const [measuredContentHeight, setMeasuredContentHeight] = useState<number | null>(null);

  const handlePeekLayout = useCallback((event: LayoutChangeEvent) => {
    setMeasuredContentHeight(event.nativeEvent.layout.height);
  }, []);

  const fallbackPeekHeight = Math.round(windowHeight * PEEK_FALLBACK_FRACTION);
  const maxPeekHeight = Math.round(windowHeight * PEEK_MAX_FRACTION);
  const peekHeight = Math.max(
    PEEK_MIN_HEIGHT,
    Math.min(
      maxPeekHeight,
      SHEET_HANDLE_HEIGHT + (measuredContentHeight ?? fallbackPeekHeight)
    )
  );

  const showCoordsInPeek = !place.address;
  const hasSecondaryActions = Boolean(onSave || onShare);

  return (
    <Sheet
      visible
      snapPoint={snapPoint}
      onSnapPointChange={(point) => onExpandedChange(point === "expanded")}
      onClose={onDismiss}
      peekHeight={peekHeight}
      testID="place-card"
    >
      <View style={styles.content}>
        <View onLayout={handlePeekLayout} style={styles.peekBlock}>
          <View style={styles.header}>
            <View style={styles.headerText}>
              <Text style={styles.name} numberOfLines={2}>
                {place.name}
              </Text>
              {place.address ? (
                <Text style={styles.address} numberOfLines={1}>
                  {place.address}
                </Text>
              ) : null}
              {showCoordsInPeek && (
                <Text style={styles.address} numberOfLines={1}>
                  {formatCoords(place.position)}
                </Text>
              )}
            </View>
            <Pressable
              onPress={onDismiss}
              hitSlop={8}
              accessibilityRole="button"
              accessibilityLabel="Dismiss"
              style={styles.dismissButton}
            >
              <CloseIcon />
            </Pressable>
          </View>

          {typeof place.distanceMeters === "number" && (
            <Chip label={formatDistance(place.distanceMeters, units)} tone="neutral" />
          )}

          <Button
            label="Directions"
            variant="primary"
            fullWidth
            onPress={onDirections}
            testID="place-card-directions"
          />

          {hasSecondaryActions && (
            <View style={styles.secondaryActionsRow}>
              {onSave && (
                <SecondaryActionButton
                  icon={<BookmarkIcon />}
                  label="Save"
                  onPress={onSave}
                  testID="place-card-save"
                />
              )}
              {onShare && (
                <SecondaryActionButton
                  icon={<ShareIcon />}
                  label="Share"
                  onPress={onShare}
                  testID="place-card-share"
                />
              )}
            </View>
          )}
        </View>

        {expanded && place.address && (
          <View style={styles.detailSection}>
            <View style={styles.detailRow}>
              <Text style={styles.detailLabel}>Coordinates</Text>
              <Text style={styles.detailValue}>{formatCoords(place.position)}</Text>
            </View>
          </View>
        )}
      </View>
    </Sheet>
  );
}

const styles = StyleSheet.create({
  content: {
    flex: 1,
    paddingHorizontal: spacing[4],
  },
  peekBlock: {
    paddingBottom: spacing[4],
    gap: spacing[3],
  },
  header: {
    flexDirection: "row",
    alignItems: "flex-start",
    gap: spacing[3],
  },
  headerText: {
    flex: 1,
  },
  name: {
    fontSize: typeScale.headline.fontSize,
    lineHeight: typeScale.headline.lineHeight,
    fontWeight: typeScale.headline.fontWeight,
    color: theme.textPrimary,
  },
  address: {
    marginTop: 2,
    fontSize: typeScale.body.fontSize,
    color: theme.textSecondary,
  },
  dismissButton: {
    width: minTouchTarget - 12,
    height: minTouchTarget - 12,
    borderRadius: radius.pill,
    backgroundColor: theme.surfaceAlt,
    alignItems: "center",
    justifyContent: "center",
  },
  secondaryActionsRow: {
    flexDirection: "row",
    gap: spacing[2],
  },
  secondaryAction: {
    flex: 1,
    flexDirection: "row",
    alignItems: "center",
    justifyContent: "center",
    gap: spacing[1],
    minHeight: minTouchTarget - 4,
    borderRadius: radius.md,
    backgroundColor: theme.surfaceAlt,
  },
  secondaryActionPressed: {
    backgroundColor: theme.border,
  },
  secondaryActionText: {
    fontSize: typeScale.body.fontSize,
    fontWeight: "500",
    color: theme.textPrimary,
  },
  detailSection: {
    marginTop: spacing[2],
    paddingTop: spacing[3],
    paddingBottom: spacing[4],
    borderTopWidth: 1,
    borderTopColor: theme.border,
    gap: spacing[3],
  },
  detailRow: {
    flexDirection: "row",
    justifyContent: "space-between",
    alignItems: "center",
  },
  detailLabel: {
    fontSize: typeScale.caption.fontSize,
    color: theme.textSecondary,
  },
  detailValue: {
    fontSize: typeScale.body.fontSize,
    color: theme.textPrimary,
  },
});
