/// <reference types="google.maps" />
import {
  type FC,
  useCallback,
  useEffect,
  useMemo,
  useRef,
  useState,
} from 'react';

import {
  AdvancedMarker,
  APIProvider as GoogleMapsProvider,
  Map,
  Pin,
  useMap,
} from '@vis.gl/react-google-maps';
import HomeWorkRoundedIcon from '@material-hu/icons/material/HomeWorkRounded';
import { IconMapPinExclamation } from '@material-hu/icons/tabler';
import Box from '@material-hu/mui/Box';
import Stack from '@material-hu/mui/Stack';
import { useTheme } from '@material-hu/mui/styles';
import Typography from '@material-hu/mui/Typography';

import HuCardContainer from '@material-hu/components/design-system/CardContainer';

import {
  EntrySource,
  type SiteCoordinates,
  type SiteLocation,
  type UserSite,
} from 'src/types/timeTracking';
import { useLokaliseTranslation } from 'src/utils/i18n';

import ConnectionLine from './ConnectionLine';
import ToleranceCircle from './ToleranceCircle';

const GOOGLE_MAPS_API_KEY = import.meta.env.VITE_GOOGLE_MAPS_API_KEY || '';
const MAP_ID = import.meta.env.VITE_GOOGLE_MAPS_CONTROL_MAP_ID || '';
const DEFAULT_ZOOM = 17;
// 1 degree of latitude ≈ 111,320 meters
const METERS_PER_DEGREE = 111320;
const MIN_RADIUS = 200; // 200 meters minimum radius

// Calculate distance between two points in meters using Haversine formula
const getDistanceInMeters = (
  point1: google.maps.LatLngLiteral,
  point2: google.maps.LatLngLiteral,
): number => {
  const R = 6371e3; // Earth's radius in meters
  const lat1 = (point1.lat * Math.PI) / 180;
  const lat2 = (point2.lat * Math.PI) / 180;
  const deltaLat = ((point2.lat - point1.lat) * Math.PI) / 180;
  const deltaLng = ((point2.lng - point1.lng) * Math.PI) / 180;

  const a =
    Math.sin(deltaLat / 2) * Math.sin(deltaLat / 2) +
    Math.cos(lat1) *
      Math.cos(lat2) *
      Math.sin(deltaLng / 2) *
      Math.sin(deltaLng / 2);
  const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));

  return Math.round(R * c);
};

// Calculate point on circle edge in direction of target point
const getCircleEdgePoint = (
  center: google.maps.LatLngLiteral,
  target: google.maps.LatLngLiteral,
  radiusMeters: number,
): google.maps.LatLngLiteral => {
  const latDiff = target.lat - center.lat;
  const lngDiff = target.lng - center.lng;

  // Calculate distance in degrees (approximate)
  const distance = Math.sqrt(latDiff * latDiff + lngDiff * lngDiff);

  if (distance === 0) return center;

  // Normalize direction
  const dirLat = latDiff / distance;
  const dirLng = lngDiff / distance;

  // Convert radius to degrees (approximate)
  const radiusInDegreesLat = radiusMeters / METERS_PER_DEGREE;
  const radiusInDegreesLng =
    radiusMeters / (METERS_PER_DEGREE * Math.cos((center.lat * Math.PI) / 180));

  // Calculate point on circle edge
  return {
    lat: center.lat + dirLat * radiusInDegreesLat,
    lng: center.lng + dirLng * radiusInDegreesLng,
  };
};

type SiteMapProps = {
  selectedSiteId: number;
  location: SiteLocation;
  siteName: string;
  allSites: UserSite[];
  markingLocation: SiteCoordinates | null;
  entrySource?: EntrySource;
};

const SiteMapContent: FC<SiteMapProps> = ({
  selectedSiteId,
  location,
  siteName,
  allSites,
  markingLocation,
  entrySource,
}) => {
  const map = useMap();
  const theme = useTheme();
  const { t, i18n } = useLokaliseTranslation('time_tracker');
  const [hoveredSite, setHoveredSite] = useState<UserSite | null>(null);
  const [showMarkingCard, setShowMarkingCard] = useState(false);
  const isInitialMount = useRef(true);

  // Convert SiteCoordinates to google.maps.LatLngLiteral
  // For WEB entries, use site location (from form) instead of original marking location
  const actualMarkingLocation = useMemo(() => {
    if (entrySource === EntrySource.WEB) {
      return {
        lat: location.latitude,
        lng: location.longitude,
      };
    }
    // For other entry sources (APP, KIOSK), use the original marking location
    return markingLocation
      ? { lat: markingLocation.latitude, lng: markingLocation.longitude }
      : null;
  }, [markingLocation, location, entrySource]);

  const sitePosition = useMemo(
    () => ({
      lat: location.latitude,
      lng: location.longitude,
    }),
    [location.latitude, location.longitude],
  );

  // Get selected site object
  const selectedSite = useMemo(
    () => allSites.find(site => site.id === selectedSiteId),
    [allSites, selectedSiteId],
  );

  // Check if this is a case without site (siteId === -1)
  const hasNoSite = selectedSiteId === -1;

  // Fit bounds to show both site and marking location
  // If no site, only use marking location
  const fitBoundsToPoints = useCallback(() => {
    if (!map) return;

    const bounds = new google.maps.LatLngBounds();

    // If no site, only extend marking location (or sitePosition which is the same)
    if (hasNoSite) {
      if (actualMarkingLocation) {
        bounds.extend(actualMarkingLocation);
      } else {
        bounds.extend(sitePosition);
      }
    } else {
      bounds.extend(sitePosition);
      if (actualMarkingLocation) {
        bounds.extend(actualMarkingLocation);
      }
    }

    // If no site, just center on marking location with more padding for better zoom
    if (hasNoSite) {
      // Add a minimum area around the marking location to avoid too much zoom
      const latOffset = MIN_RADIUS / METERS_PER_DEGREE;
      const lngOffset =
        MIN_RADIUS /
        (METERS_PER_DEGREE * Math.cos((sitePosition.lat * Math.PI) / 180));

      const centerPoint = actualMarkingLocation || sitePosition;
      const minBounds = new google.maps.LatLngBounds(
        {
          lat: centerPoint.lat - latOffset,
          lng: centerPoint.lng - lngOffset,
        },
        {
          lat: centerPoint.lat + latOffset,
          lng: centerPoint.lng + lngOffset,
        },
      );

      bounds.union(minBounds);
      const padding = { top: 100, right: 100, bottom: 100, left: 100 };
      map.fitBounds(bounds, padding);
      return;
    }

    // Check if marking is outside tolerance to adjust padding/zoom
    const distanceToSite = actualMarkingLocation
      ? getDistanceInMeters(actualMarkingLocation, sitePosition)
      : 0;
    const hasIncident =
      actualMarkingLocation &&
      location.tolerance > 0 &&
      distanceToSite > location.tolerance;

    // Calculate minimum radius (tolerance + 100m)
    const minRadius = location.tolerance + 100;

    // Convert meters to degrees (approximate)
    const latOffset = minRadius / METERS_PER_DEGREE;
    const lngOffset =
      minRadius /
      (METERS_PER_DEGREE * Math.cos((sitePosition.lat * Math.PI) / 180));

    // Calculate the center point for minimum bounds
    const centerForMinBounds = actualMarkingLocation
      ? {
          lat: (sitePosition.lat + actualMarkingLocation.lat) / 2,
          lng: (sitePosition.lng + actualMarkingLocation.lng) / 2,
        }
      : sitePosition;

    // Expand bounds to ensure minimum visible area centered between both points
    const minBounds = new google.maps.LatLngBounds(
      {
        lat: centerForMinBounds.lat - latOffset,
        lng: centerForMinBounds.lng - lngOffset,
      },
      {
        lat: centerForMinBounds.lat + latOffset,
        lng: centerForMinBounds.lng + lngOffset,
      },
    );

    // Union the calculated bounds with the minimum bounds
    bounds.union(minBounds);

    // Increase padding when there's an incident to reduce zoom and ensure popovers are visible
    // Popovers appear above markers, so we need more space, especially at the top
    const padding = hasIncident
      ? { top: 200, right: 150, bottom: 100, left: 150 } // More padding for popovers
      : { top: 50, right: 50, bottom: 50, left: 50 };

    map.fitBounds(bounds, padding);
  }, [map, sitePosition, actualMarkingLocation, location.tolerance, hasNoSite]);

  // Only adjust bounds when siteId changes, not on hover or popover changes
  useEffect(() => {
    if (isInitialMount.current) {
      isInitialMount.current = false;
      // On initial mount, fit bounds after a short delay to ensure map is ready
      const timer = setTimeout(() => {
        fitBoundsToPoints();
      }, 100);
      return () => clearTimeout(timer);
    }
    // On site change, fit bounds immediately
    fitBoundsToPoints();
  }, [selectedSiteId, fitBoundsToPoints]);

  // Get other sites (not selected)
  const otherSites = useMemo(
    () => allSites.filter(site => site.id !== selectedSiteId && site.location),
    [allSites, selectedSiteId],
  );

  // Calculate center between site and marking location for better map view
  // If no site, center on marking location only
  const mapCenter = useMemo(() => {
    if (hasNoSite) {
      return actualMarkingLocation || sitePosition;
    }
    if (actualMarkingLocation) {
      return {
        lat: (sitePosition.lat + actualMarkingLocation.lat) / 2,
        lng: (sitePosition.lng + actualMarkingLocation.lng) / 2,
      };
    }
    return sitePosition;
  }, [sitePosition, actualMarkingLocation, hasNoSite]);

  // Calculate initial zoom based on whether there's an incident
  // When there's an incident, start with less zoom to ensure popovers are visible
  // If no site, use a lower zoom level (15) to show more area around the marking location
  const initialZoom = useMemo(() => {
    if (hasNoSite) {
      return 17; // Lower zoom to show more area when there's no site
    }
    if (!actualMarkingLocation || location.tolerance <= 0) {
      return DEFAULT_ZOOM;
    }
    const distanceToSite = getDistanceInMeters(
      actualMarkingLocation,
      sitePosition,
    );
    const hasIncident = distanceToSite > location.tolerance;
    return hasIncident ? 15 : DEFAULT_ZOOM;
  }, [actualMarkingLocation, sitePosition, location.tolerance, hasNoSite]);

  // Colors for selected site (blue)

  const pinBackground = theme.palette.newBase?.brand?.[500] || '#2563eb';
  const pinBorder = theme.palette.new.background.elements.default;
  const circleFill = theme.palette.newBase?.brand?.[200] || '#BFC9ED';
  const circleStroke = theme.palette.newBase?.brand?.[400] || '#2563eb';

  const grayPinBackground = theme.palette.new.text.neutral.lighter;

  const handleMouseOver = (site: UserSite) => setHoveredSite(site);
  const handleMouseOut = () => setHoveredSite(null);

  const handleMarkingMouseOver = () => {
    if (isMarkingOutsideTolerance) {
      setShowMarkingCard(true);
    }
  };
  const handleMarkingMouseOut = () => setShowMarkingCard(false);

  // Calculate distance from marking location to site
  const distanceToSite = useMemo(() => {
    if (!actualMarkingLocation) return 0;
    return getDistanceInMeters(actualMarkingLocation, sitePosition);
  }, [actualMarkingLocation, sitePosition]);

  // Display distance in km when >= 1000m, otherwise in meters; formatted with locale separators
  const useKmUnit = distanceToSite >= 1000;
  const formattedDistance = useKmUnit
    ? (distanceToSite / 1000).toLocaleString(i18n.language, {
        maximumFractionDigits: 1,
      })
    : distanceToSite.toLocaleString(i18n.language);

  // Check if marking is outside the tolerance radius
  const isMarkingOutsideTolerance = useMemo(() => {
    if (!actualMarkingLocation || location.tolerance <= 0) return false;
    return distanceToSite > location.tolerance;
  }, [actualMarkingLocation, distanceToSite, location.tolerance]);

  // Path for the connection line (ends at circle edge, not center)
  // Only show if marking is outside tolerance
  const connectionPath = useMemo(() => {
    if (!actualMarkingLocation || !isMarkingOutsideTolerance) return null;
    const circleEdgePoint = getCircleEdgePoint(
      sitePosition,
      actualMarkingLocation,
      location.tolerance,
    );
    return [actualMarkingLocation, circleEdgePoint];
  }, [
    actualMarkingLocation,
    sitePosition,
    location.tolerance,
    isMarkingOutsideTolerance,
  ]);

  return (
    <Map
      mapId={MAP_ID}
      defaultZoom={initialZoom}
      defaultCenter={mapCenter}
      gestureHandling="greedy"
      disableDefaultUI
      reuseMaps
      style={{ width: '100%', height: '100%' }}
    >
      {/* Connection line from marking location to site - only show if there's a site */}
      {!hasNoSite && connectionPath && (
        <ConnectionLine
          path={connectionPath}
          strokeColor={circleFill}
          strokeOpacity={0.5}
          strokeWeight={10}
          onMouseOver={handleMarkingMouseOver}
          onMouseOut={handleMarkingMouseOut}
        />
      )}

      {/* Marking location point (blue dot) */}
      {actualMarkingLocation && (
        <AdvancedMarker
          position={actualMarkingLocation}
          title={t('MARKING_LOCATION')}
          onMouseEnter={handleMarkingMouseOver}
          onMouseLeave={handleMarkingMouseOut}
        >
          <Stack
            sx={{
              width: 16,
              height: 16,
              borderRadius: '50%',
              backgroundColor: pinBackground,
              border: `3px solid ${pinBorder}`,
              boxShadow: `0px 4px 8px ${theme.palette.new.shadows['4dp']}`,
            }}
          />
        </AdvancedMarker>
      )}

      {/* Other sites (gray) - render first so selected site appears on top */}
      {otherSites.map(site => {
        const sitePos = {
          lat: site.location.latitude,
          lng: site.location.longitude,
        };
        return (
          <AdvancedMarker
            key={site.id}
            position={sitePos}
            title={site.name}
            onMouseEnter={() => handleMouseOver(site)}
            onMouseLeave={handleMouseOut}
          >
            <Pin
              background={grayPinBackground}
              borderColor={pinBorder}
              scale={1}
            >
              <HomeWorkRoundedIcon
                sx={{
                  color: pinBorder,
                  height: '16px',
                  width: '16px',
                }}
              />
            </Pin>
          </AdvancedMarker>
        );
      })}

      {/* Selected site tolerance circle (blue) - only show if there's a site */}
      {!hasNoSite && location.tolerance > 0 && (
        <ToleranceCircle
          center={sitePosition}
          radius={location.tolerance}
          strokeColor={circleStroke}
          strokeOpacity={0}
          strokeWeight={0}
          fillColor={circleFill}
          fillOpacity={0.5}
          onMouseOver={
            selectedSite ? () => handleMouseOver(selectedSite) : undefined
          }
          onMouseOut={handleMouseOut}
        />
      )}

      {/* Selected site marker (blue) - only show if there's a site */}
      {!hasNoSite && (
        <AdvancedMarker
          position={sitePosition}
          title={selectedSite?.name}
          onMouseEnter={
            selectedSite ? () => handleMouseOver(selectedSite) : undefined
          }
          onMouseLeave={handleMouseOut}
        >
          <Pin
            background={pinBackground}
            borderColor={pinBorder}
            scale={1.2}
          >
            <HomeWorkRoundedIcon
              sx={{
                color: pinBorder,
                height: '20px',
                width: '20px',
              }}
            />
          </Pin>
        </AdvancedMarker>
      )}

      {/* Card tooltip on hover for sites */}
      {hoveredSite && (
        <AdvancedMarker
          position={{
            lat: hoveredSite.location.latitude,
            lng: hoveredSite.location.longitude,
          }}
          zIndex={1000}
          onMouseEnter={() => handleMouseOver(hoveredSite)}
          onMouseLeave={handleMouseOut}
        >
          <Box
            sx={{
              position: 'relative',
              bottom: 70,
              left: '50%',
              transform: 'translateX(-50%)',
            }}
          >
            <HuCardContainer
              sx={{
                minWidth: 220,
                boxShadow: `0px 4px 8px ${theme.palette.new.shadows['4dp']}`,
                '.MuiCardContent-root': {
                  p: 1.5,
                },
              }}
            >
              <Stack
                sx={{
                  flexDirection: 'row',
                  alignItems: 'center',
                  gap: 1.5,
                }}
              >
                <Box
                  sx={{
                    display: 'flex',
                    alignItems: 'center',
                    justifyContent: 'center',
                    width: 36,
                    height: 36,
                    borderRadius: '50%',
                    backgroundColor:
                      hoveredSite.id === selectedSiteId
                        ? theme.palette.new.background.elements.brand
                        : theme.palette.new.background.elements.grey,
                    flexShrink: 0,
                  }}
                >
                  <HomeWorkRoundedIcon
                    sx={{
                      color:
                        hoveredSite.id === selectedSiteId
                          ? theme.palette.new.text.neutral.brand
                          : theme.palette.new.text.neutral.lighter,
                      width: 20,
                      height: 20,
                    }}
                  />
                </Box>
                <Stack>
                  <Typography
                    variant="globalXS"
                    sx={{
                      color: theme.palette.new.text.neutral.lighter,
                    }}
                  >
                    {t('SITE')}
                  </Typography>
                  <Typography
                    variant="globalS"
                    fontWeight="fontWeightSemiBold"
                  >
                    {hoveredSite.name || hoveredSite.location.address}
                  </Typography>
                </Stack>
              </Stack>
            </HuCardContainer>
          </Box>
        </AdvancedMarker>
      )}

      {/* Card tooltip on hover for marking location - only show if outside tolerance */}
      {showMarkingCard &&
        actualMarkingLocation &&
        isMarkingOutsideTolerance && (
          <AdvancedMarker
            position={actualMarkingLocation}
            zIndex={1000}
            onMouseEnter={handleMarkingMouseOver}
            onMouseLeave={handleMarkingMouseOut}
          >
            <Box
              sx={{
                position: 'relative',
                bottom: 50,
                left: '50%',
                transform: 'translateX(-50%)',
              }}
            >
              <HuCardContainer
                sx={{
                  minWidth: 260,
                  boxShadow: `0px 4px 8px ${theme.palette.new.shadows['4dp']}`,
                  '.MuiCardContent-root': {
                    p: 1.5,
                  },
                }}
              >
                <Stack
                  sx={{
                    flexDirection: 'row',
                    alignItems: 'flex-start',
                    gap: 1.5,
                  }}
                >
                  <Box
                    sx={{
                      display: 'flex',
                      alignItems: 'center',
                      justifyContent: 'center',
                      width: 36,
                      height: 36,
                      borderRadius: '50%',
                      backgroundColor: theme.palette.newBase?.yellow?.[100],
                      flexShrink: 0,
                    }}
                  >
                    <IconMapPinExclamation
                      size={20}
                      color={theme.palette.newBase?.yellow?.[800]}
                    />
                  </Box>
                  <Stack sx={{ gap: 0.5 }}>
                    <Typography
                      variant="globalS"
                      fontWeight="fontWeightSemiBold"
                    >
                      {t('time_tracker:marking_out_of_location')}
                    </Typography>
                    <Typography
                      variant="globalXS"
                      sx={{
                        color: theme.palette.new.text.neutral.lighter,
                      }}
                    >
                      {t('time_tracker:marking_distance_from_site', {
                        distance: formattedDistance,
                        site: siteName,
                        context: useKmUnit ? 'kilometers' : 'meters',
                      })}
                    </Typography>
                  </Stack>
                </Stack>
              </HuCardContainer>
            </Box>
          </AdvancedMarker>
        )}
    </Map>
  );
};

type Props = {
  selectedSiteId: number;
  location: SiteLocation;
  siteName: string;
  allSites: UserSite[];
  markingLocation: SiteCoordinates | null;
  entrySource?: EntrySource;
};

const SiteMap: FC<Props> = ({
  selectedSiteId,
  location,
  siteName,
  allSites,
  markingLocation,
  entrySource,
}) => {
  if (!GOOGLE_MAPS_API_KEY) {
    return null;
  }

  return (
    <GoogleMapsProvider
      apiKey={GOOGLE_MAPS_API_KEY}
      authReferrerPolicy="origin"
    >
      <SiteMapContent
        selectedSiteId={selectedSiteId}
        location={location}
        siteName={siteName}
        allSites={allSites}
        markingLocation={markingLocation}
        entrySource={entrySource}
      />
    </GoogleMapsProvider>
  );
};

export default SiteMap;
