import { useState, useEffect, useCallback, useRef } from 'react';
import {
  Table,
  Button,
  Input,
  Select,
  Tag,
  Space,
  Modal,
  Form,
  Switch,
  Popconfirm,
  message,
  Typography,
  Row,
  Col,
  Card,
  Statistic,
  Progress,
  Radio,
  Upload,
  Drawer,
  InputNumber,
  Tabs,
  Grid,
  Tooltip,
} from 'antd';
import {
  PlusOutlined,
  EditOutlined,
  DeleteOutlined,
  SearchOutlined,
  DownloadOutlined,
  UploadOutlined,
  AimOutlined,
  EnvironmentOutlined,
  SettingOutlined,
  TableOutlined,
  WarningOutlined,
  CheckCircleOutlined,
  InfoCircleOutlined,
  ClockCircleOutlined,
  ScissorOutlined,
  EyeOutlined,
  DatabaseOutlined,
} from '@ant-design/icons';
import type { ColumnsType, TablePaginationConfig } from 'antd/es/table';
import type { UploadFile } from 'antd/es/upload';
import dayjs from 'dayjs';
import { useNavigate, useOutletContext } from 'react-router-dom';
import { api } from '@/lib/api';
import { buildServiceUrl } from '@/lib/service-url';
import { useAuthStore } from '@/stores/auth.store';
import type { AppOutletContext } from '@/components/AppLayout';
import type {
  Location,
  Address,
  BuildingType,
  SupportLevel,
  LocationsListResponse,
  LocationsListParams,
  LocationStats,
  GeocodeResult,
  ReverseGeocodeResult,
  BulkImportResult,
  Cut,
  NarDataset,
  NarDatasetsResponse,
  NarServerImportResult,
  NarImportProgress,
  LocationHistory,
  LocationHistoryResponse,
  ServicesConfig,
} from '@/types/api';
import {
  LOCATION_HISTORY_ACTION_LABELS,
  LOCATION_HISTORY_ACTION_COLORS,
  SUPPORT_LEVEL_LABELS,
  SUPPORT_LEVEL_COLORS,
} from '@/types/api';
import AdminMapView from '@/components/map/AdminMapView';
import { PageTour } from '@/components/tour/PageTour';
import AreaImportWizard from '@/components/map/AreaImportWizard';
import axios from 'axios';

const { Text } = Typography;
const { TextArea } = Input;

const BUILDING_TYPE_LABELS: Record<BuildingType, string> = {
  SINGLE_FAMILY: 'Single Family',
  MULTI_UNIT: 'Multi-Unit',
  MIXED_USE: 'Mixed Use',
  COMMERCIAL: 'Commercial',
};

const BUILDING_TYPE_COLORS: Record<BuildingType, string> = {
  SINGLE_FAMILY: 'default',
  MULTI_UNIT: 'blue',
  MIXED_USE: 'purple',
  COMMERCIAL: 'orange',
};

function formatNarSize(bytes: number): string {
  if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(0)} KB`;
  if (bytes < 1024 * 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(0)} MB`;
  return `${(bytes / (1024 * 1024 * 1024)).toFixed(1)} GB`;
}

export default function LocationsPage() {
  const navigate = useNavigate();
  const { user } = useAuthStore();
  const isSuperAdmin = user?.role === 'SUPER_ADMIN';
  const [nocodbUrl, setNocodbUrl] = useState<string | null>(null);
  const [locations, setLocations] = useState<Location[]>([]);
  const screens = Grid.useBreakpoint();
  const isMobile = !screens.md;
  const [pagination, setPagination] = useState({ page: 1, limit: 20, total: 0, totalPages: 0 });
  const [loading, setLoading] = useState(false);
  const [stats, setStats] = useState<LocationStats | null>(null);
  const [search, setSearch] = useState('');
  const [debouncedSearch, setDebouncedSearch] = useState('');
  const searchTimerRef = useRef<ReturnType<typeof setTimeout>>(undefined);
  const [confidenceFilter, setConfidenceFilter] = useState<'high' | 'medium' | 'low' | 'none' | undefined>();

  // Drawers
  const [createDrawerOpen, setCreateDrawerOpen] = useState(false);
  const [editDrawerOpen, setEditDrawerOpen] = useState(false);
  const [editingLocation, setEditingLocation] = useState<Location | null>(null);
  const [locationHistory, setLocationHistory] = useState<LocationHistory[]>([]);
  const [historyLoading, setHistoryLoading] = useState(false);
  const [historyPagination, setHistoryPagination] = useState({ page: 1, limit: 20, total: 0, totalPages: 0 });
  const [importDrawerOpen, setImportDrawerOpen] = useState(false);
  const [importing, setImporting] = useState(false);
  const [geocodingMissing, setGeocodingMissing] = useState(false);
  const [importFormat, setImportFormat] = useState<'standard' | 'nar' | 'server' | 'area'>('standard');
  const [importFilterType, setImportFilterType] = useState<'none' | 'cut' | 'mapArea' | 'city' | 'province'>('none');
  const [importCutId, setImportCutId] = useState<string | undefined>();
  const [importCity, setImportCity] = useState('');
  const [importProvince, setImportProvince] = useState<string | undefined>();
  const [importResidentialOnly, setImportResidentialOnly] = useState(true);
  const [bulkImportResult, setBulkImportResult] = useState<BulkImportResult | null>(null);
  const [cuts, setCuts] = useState<Cut[]>([]);

  // NAR Server Import state
  const [narDatasets, setNarDatasets] = useState<NarDataset[]>([]);
  const [narDatasetsLoading, setNarDatasetsLoading] = useState(false);
  const [narDir, setNarDir] = useState<string | null>(null);
  const [narSelectedProvince, setNarSelectedProvince] = useState<string | undefined>();
  const [narFilterType, setNarFilterType] = useState<'none' | 'city' | 'postalPrefix' | 'cut'>('none');
  const [narFilterCity, setNarFilterCity] = useState('');
  const [narFilterPostalPrefix, setNarFilterPostalPrefix] = useState('');
  const [narFilterCutId, setNarFilterCutId] = useState<string | undefined>();
  const [narResidentialOnly, setNarResidentialOnly] = useState(true);
  const [narImportResult, setNarImportResult] = useState<NarServerImportResult | null>(null);
  const [narProgress, setNarProgress] = useState<NarImportProgress | null>(null);
  const narPollRef = useRef<ReturnType<typeof setInterval>>(undefined);

  // Bulk Re-Geocoding state
  const [bulkGeocodeDrawerOpen, setBulkGeocodeDrawerOpen] = useState(false);
  const [bulkGeocoding, setBulkGeocoding] = useState(false);
  const [bulkGeocodeStatus, setBulkGeocodeStatus] = useState<any>(null);
  const bulkGeocodePollRef = useRef<ReturnType<typeof setInterval>>(undefined);
  const [bulkGeocodeForm] = Form.useForm();

  // Tabs + map
  const [activeTab, setActiveTab] = useState('table');
  const [allLocations, setAllLocations] = useState<Location[]>([]);
  const [mapLoading, setMapLoading] = useState(false);
  const fetchTimerRef = useRef<ReturnType<typeof setTimeout>>(undefined);
  const abortControllerRef = useRef<AbortController | null>(null);
  const currentBoundsRef = useRef<{ minLat: number; maxLat: number; minLng: number; maxLng: number } | null>(null);

  // Selection for bulk ops
  const [selectedRowKeys, setSelectedRowKeys] = useState<string[]>([]);

  const [createForm] = Form.useForm();
  const [editForm] = Form.useForm();
  const [geocoding, setGeocoding] = useState(false);

  const handleSearchChange = (value: string) => {
    setSearch(value);
    clearTimeout(searchTimerRef.current);
    searchTimerRef.current = setTimeout(() => setDebouncedSearch(value), 300);
  };

  const stopNarPolling = useCallback(() => {
    if (narPollRef.current) {
      clearInterval(narPollRef.current);
      narPollRef.current = undefined;
    }
  }, []);

  useEffect(() => {
    return () => {
      clearTimeout(searchTimerRef.current);
      stopNarPolling();
    };
  }, [stopNarPolling]);

  const fetchLocations = useCallback(async (params?: LocationsListParams) => {
    setLoading(true);
    try {
      const { data } = await api.get<LocationsListResponse>('/map/locations', {
        params: {
          page: params?.page ?? pagination.page,
          limit: params?.limit ?? pagination.limit,
          search: params?.search ?? (debouncedSearch || undefined),
          confidenceLevel: params?.confidenceLevel ?? confidenceFilter,
        },
      });
      setLocations(data.locations);
      setPagination(data.pagination);
    } catch {
      message.error('Failed to load locations');
    } finally {
      setLoading(false);
    }
  }, [pagination.page, pagination.limit, debouncedSearch, confidenceFilter]);

  const fetchStats = useCallback(async () => {
    try {
      const { data } = await api.get<LocationStats>('/map/locations/stats');
      setStats(data);
    } catch (error) {
      console.error('Failed to fetch location stats:', error);
      message.error('Failed to load location statistics. Please refresh the page.');
      // Keep stats as null to prevent rendering broken UI
    }
  }, []);

  const fetchLocationHistory = useCallback(async (locationId: string, page: number = 1) => {
    setHistoryLoading(true);
    try {
      const { data } = await api.get<LocationHistoryResponse>(`/map/locations/${locationId}/history`, {
        params: { page, limit: 20 },
      });
      setLocationHistory(data.history);
      setHistoryPagination(data.pagination);
    } catch {
      message.error('Failed to load location history');
    } finally {
      setHistoryLoading(false);
    }
  }, []);

  const fetchAllLocations = useCallback(async (bounds?: {
    minLat: number;
    maxLat: number;
    minLng: number;
    maxLng: number;
  }, isBackgroundFetch = false, zoom?: number) => {
    if (abortControllerRef.current) {
      abortControllerRef.current.abort();
    }

    // Only show loading spinner for initial load, not for viewport changes
    if (!isBackgroundFetch) {
      setMapLoading(true);
    }
    const controller = new AbortController();
    abortControllerRef.current = controller;

    try {
      let url = '/map/locations/all';
      if (bounds) {
        // Zoom-aware limits (500/1000/2000)
        const limit = zoom && zoom >= 15 ? 2000 : zoom && zoom >= 12 ? 1000 : 500;

        const params = new URLSearchParams({
          minLat: bounds.minLat.toString(),
          maxLat: bounds.maxLat.toString(),
          minLng: bounds.minLng.toString(),
          maxLng: bounds.maxLng.toString(),
          limit: limit.toString(),
        });
        url += `?${params}`;
      }
      const { data } = await api.get<Location[]>(url, {
        signal: controller.signal,
      });

      // Check if we hit the limit
      const expectedLimit = zoom && zoom >= 15 ? 2000 : zoom && zoom >= 12 ? 1000 : 500;
      if (data.length === expectedLimit) {
        message.warning('Too many locations in view. Zoom in for more detail.', 3);
      }

      setAllLocations(data);
    } catch (err: unknown) {
      if (!axios.isCancel(err) && !(err instanceof DOMException && err.name === 'AbortError')) {
        message.error('Failed to load map locations');
      }
    } finally {
      if (!isBackgroundFetch) {
        setMapLoading(false);
      }
      abortControllerRef.current = null;
    }
  }, []);

  const handleMapMove = useCallback((map: any) => {
    // Skip if map is animating (prevents disrupting zoom transitions)
    if (map._animatingZoom || map._moving) {
      return;
    }

    const bounds = map.getBounds();
    const paddedBounds = bounds.pad(0.1); // 10% padding for smoother panning
    const zoom = map.getZoom();

    const newBounds = {
      minLat: paddedBounds.getSouth(),
      maxLat: paddedBounds.getNorth(),
      minLng: paddedBounds.getWest(),
      maxLng: paddedBounds.getEast(),
    };

    // Store current bounds for auto-refresh
    currentBoundsRef.current = newBounds;

    clearTimeout(fetchTimerRef.current);
    fetchTimerRef.current = setTimeout(() => {
      // Mark as background fetch to prevent loading state during viewport changes
      fetchAllLocations(newBounds, true, zoom);
    }, 800); // Increased debounce to 800ms to allow zoom animations to complete
  }, [fetchAllLocations]);

  // Refresh handler that uses current viewport bounds
  const handleRefresh = useCallback(() => {
    if (currentBoundsRef.current) {
      fetchAllLocations(currentBoundsRef.current, true);
    } else {
      fetchAllLocations(undefined, false);
    }
  }, [fetchAllLocations]);

  const fetchCuts = useCallback(async () => {
    try {
      const { data } = await api.get('/map/cuts', { params: { limit: 100 } });
      setCuts(data.cuts ?? data);
    } catch {
      // silent — cuts dropdown is optional
    }
  }, []);

  useEffect(() => {
    fetchLocations({ page: 1 });
    fetchStats();
    fetchCuts();
  }, [debouncedSearch, confidenceFilter]); // eslint-disable-line react-hooks/exhaustive-deps

  // Fetch all locations when switching to map tab
  useEffect(() => {
    if (activeTab === 'map') {
      fetchAllLocations();
    }
  }, [activeTab, fetchAllLocations]);

  const handleTableChange = (pag: TablePaginationConfig) => {
    fetchLocations({ page: pag.current ?? 1, limit: pag.pageSize ?? 20 });
  };

  const handleGeocode = async (form: ReturnType<typeof Form.useForm>[0]) => {
    const address = form.getFieldValue('address');
    if (!address) {
      message.warning('Enter an address first');
      return;
    }
    setGeocoding(true);
    try {
      const { data } = await api.post<GeocodeResult>('/map/locations/geocode', { address });
      form.setFieldsValue({ latitude: data.latitude, longitude: data.longitude });
      message.success(`Geocoded (${data.provider}, ${data.confidence}% confidence)`);
    } catch {
      message.error('Could not geocode address');
    } finally {
      setGeocoding(false);
    }
  };

  const handleCreate = async (values: Record<string, unknown>) => {
    try {
      // Clean up empty strings
      const cleaned = Object.fromEntries(
        Object.entries(values).filter(([, v]) => v !== '' && v !== undefined)
      );
      await api.post('/map/locations', cleaned);
      message.success('Location created');
      setCreateDrawerOpen(false);
      createForm.resetFields();
      fetchLocations({ page: 1 });
      fetchStats();
      if (activeTab === 'map') fetchAllLocations();
    } catch (err: unknown) {
      const msg =
        (err as { response?: { data?: { error?: { message?: string } } } })
          ?.response?.data?.error?.message || 'Failed to create location';
      message.error(msg);
    }
  };

  const handleEdit = async (values: Record<string, unknown>) => {
    if (!editingLocation) return;
    try {
      await api.put(`/map/locations/${editingLocation.id}`, values);
      message.success('Location updated');
      setEditDrawerOpen(false);
      setEditingLocation(null);
      editForm.resetFields();
      fetchLocations();
      fetchStats();
    } catch (err: unknown) {
      const msg =
        (err as { response?: { data?: { error?: { message?: string } } } })
          ?.response?.data?.error?.message || 'Failed to update location';
      message.error(msg);
    }
  };

  const handleDelete = async (id: string) => {
    try {
      await api.delete(`/map/locations/${id}`);
      message.success('Location deleted');
      fetchLocations();
      fetchStats();
    } catch {
      message.error('Failed to delete location');
    }
  };

  const handleBulkDelete = async () => {
    if (!selectedRowKeys.length) return;
    try {
      await api.post('/map/locations/bulk-delete', { ids: selectedRowKeys });
      message.success(`Deleted ${selectedRowKeys.length} locations`);
      setSelectedRowKeys([]);
      fetchLocations();
      fetchStats();
    } catch {
      message.error('Failed to delete locations');
    }
  };

  const handleGeocodeMissing = useCallback(async () => {
    setGeocodingMissing(true);
    try {
      const { data } = await api.post<{ total: number; geocoded: number; failed: number }>('/map/locations/geocode-missing');
      message.success(`Geocoded ${data.geocoded} of ${data.total} locations (${data.failed} failed)`);
      fetchLocations();
      fetchStats();
    } catch {
      message.error('Failed to geocode locations');
    } finally {
      setGeocodingMissing(false);
    }
  }, [fetchLocations, fetchStats]);

  const handleExportCsv = useCallback(async () => {
    try {
      const { data } = await api.get('/map/locations/export-csv', { responseType: 'blob' });
      const url = window.URL.createObjectURL(new Blob([data as BlobPart]));
      const a = document.createElement('a');
      a.href = url;
      a.download = `locations-${dayjs().format('YYYY-MM-DD')}.csv`;
      a.click();
      window.URL.revokeObjectURL(url);
    } catch {
      message.error('Failed to export CSV');
    }
  }, []);

  const handleImportCsv = async (file: UploadFile) => {
    if (!file.originFileObj) return;
    setImporting(true);
    try {
      const formData = new FormData();
      formData.append('file', file.originFileObj);
      const { data } = await api.post<{ total: number; success: number; warnings: number; failed: number; errors: string[] }>(
        '/map/locations/import-csv',
        formData,
        { headers: { 'Content-Type': 'multipart/form-data' } }
      );
      message.success(`Imported ${data.success} of ${data.total} locations (${data.warnings} warnings, ${data.failed} failed)`);
      if (data.errors.length > 0) {
        Modal.warning({
          title: 'Import Warnings',
          content: (
            <div style={{ maxHeight: 300, overflow: 'auto' }}>
              {data.errors.map((e, i) => (
                <div key={i} style={{ fontSize: 12, marginBottom: 4 }}>{e}</div>
              ))}
            </div>
          ),
        });
      }
      setImportDrawerOpen(false);
      fetchLocations({ page: 1 });
      fetchStats();
    } catch (err: unknown) {
      const msg =
        (err as { response?: { data?: { error?: { message?: string } } } })
          ?.response?.data?.error?.message || 'Failed to import CSV';
      message.error(msg);
    } finally {
      setImporting(false);
    }
  };

  const handleBulkImport = async (file: UploadFile) => {
    if (!file.originFileObj) return;
    setImporting(true);
    setBulkImportResult(null);
    try {
      const formData = new FormData();
      formData.append('file', file.originFileObj);
      formData.append('format', importFormat);
      formData.append('filterType', importFilterType);
      if (importFilterType === 'cut' && importCutId) formData.append('cutId', importCutId);
      if (importFilterType === 'city' && importCity) formData.append('filterCity', importCity);
      if (importFilterType === 'province' && importProvince) formData.append('filterProvince', importProvince);
      formData.append('residentialOnly', String(importResidentialOnly));
      formData.append('deduplicateRadius', '5');
      formData.append('skipGeocoding', 'true');
      formData.append('batchSize', '1000');
      const { data } = await api.post<BulkImportResult>(
        '/map/locations/import-bulk',
        formData,
        {
          headers: { 'Content-Type': 'multipart/form-data' },
          timeout: 300000, // 5 minutes
        }
      );
      setBulkImportResult(data);
      message.success(`Created ${data.created} of ${data.total} locations`);
      fetchLocations({ page: 1 });
      fetchStats();
    } catch (err: unknown) {
      const msg =
        (err as { response?: { data?: { error?: { message?: string } } } })
          ?.response?.data?.error?.message || 'Bulk import failed';
      message.error(msg);
    } finally {
      setImporting(false);
    }
  };

  const fetchNarDatasets = async () => {
    setNarDatasetsLoading(true);
    try {
      const { data } = await api.get<NarDatasetsResponse>('/map/nar-import/datasets');
      setNarDatasets(data.datasets);
      setNarDir(data.narDir);
    } catch {
      message.error('Failed to scan NAR data directory');
    } finally {
      setNarDatasetsLoading(false);
    }
  };

  const handleNarServerImport = async () => {
    if (!narSelectedProvince) {
      message.warning('Select a province to import');
      return;
    }
    setImporting(true);
    setNarImportResult(null);
    setNarProgress(null);
    stopNarPolling();

    try {
      const body: Record<string, unknown> = {
        provinceCode: narSelectedProvince,
        filterType: narFilterType,
        residentialOnly: narResidentialOnly,
        deduplicateRadius: 5,
        batchSize: 1000,
      };
      if (narFilterType === 'city' && narFilterCity) body.filterCity = narFilterCity;
      if (narFilterType === 'postalPrefix' && narFilterPostalPrefix) body.filterPostalPrefix = narFilterPostalPrefix;
      if (narFilterType === 'cut' && narFilterCutId) body.cutId = narFilterCutId;

      const { data } = await api.post<{ importId: string }>('/map/nar-import', body);
      const importId = data.importId;

      // Start polling
      narPollRef.current = setInterval(async () => {
        try {
          const { data: progress } = await api.get<NarImportProgress>(`/map/nar-import/status/${importId}`);
          setNarProgress(progress);

          if (progress.status === 'complete') {
            stopNarPolling();
            setImporting(false);
            if (progress.result) {
              setNarImportResult(progress.result);
              message.success(
                `Imported ${progress.result.created} locations from ${progress.result.provinceName} in ${(progress.result.durationMs / 1000).toFixed(1)}s`
              );
            }
            fetchLocations({ page: 1 });
            fetchStats();
          } else if (progress.status === 'failed') {
            stopNarPolling();
            setImporting(false);
            message.error(progress.error || 'NAR import failed');
          }
        } catch {
          // Polling error — don't stop, might be transient
        }
      }, 2000);
    } catch (err: unknown) {
      const msg =
        (err as { response?: { data?: { error?: { message?: string } } } })
          ?.response?.data?.error?.message || 'NAR import failed';
      message.error(msg);
      setImporting(false);
    }
  };

  // Bulk Re-Geocoding Functions
  const stopBulkGeocodePolling = useCallback(() => {
    if (bulkGeocodePollRef.current) {
      clearInterval(bulkGeocodePollRef.current);
      bulkGeocodePollRef.current = undefined;
    }
  }, []);

  const handleBulkGeocode = async (values: { confidenceThreshold?: number; buildingType?: string; limit?: number }) => {
    setBulkGeocoding(true);
    try {
      const { data } = await api.post<{ jobId: string; message: string }>('/map/locations/bulk-geocode', values);
      message.success(data.message);

      // Start polling for job status
      bulkGeocodePollRef.current = setInterval(async () => {
        try {
          const { data: status } = await api.get(`/map/locations/bulk-geocode/${data.jobId}`);
          setBulkGeocodeStatus(status);

          if (status.state === 'completed') {
            stopBulkGeocodePolling();
            setBulkGeocoding(false);
            if (status.result) {
              message.success(
                `Bulk geocoding complete: ${status.result.improved} locations improved, ${status.result.unchanged} unchanged, ${status.result.failed} failed`
              );
            }
            fetchLocations({ page: 1 });
            fetchStats();
          } else if (status.state === 'failed') {
            stopBulkGeocodePolling();
            setBulkGeocoding(false);
            message.error(status.failedReason || 'Bulk geocoding failed');
          }
        } catch {
          // Polling error — don't stop
        }
      }, 2000);
    } catch (err: unknown) {
      const msg =
        (err as { response?: { data?: { error?: { message?: string } } } })
          ?.response?.data?.error?.message || 'Failed to start bulk geocoding';
      message.error(msg);
      setBulkGeocoding(false);
    }
  };

  const handleAddFromMap = async (lat: number, lng: number) => {
    // Reverse geocode to get address, then pre-fill create form
    try {
      const { data } = await api.post<ReverseGeocodeResult>('/map/locations/reverse-geocode', {
        latitude: lat,
        longitude: lng,
      });
      createForm.setFieldsValue({
        latitude: Math.round(lat * 100000) / 100000,
        longitude: Math.round(lng * 100000) / 100000,
        address: data.address,
      });
    } catch {
      // Still open modal with just coordinates
      createForm.setFieldsValue({
        latitude: Math.round(lat * 100000) / 100000,
        longitude: Math.round(lng * 100000) / 100000,
      });
    }
    setCreateDrawerOpen(true);
  };

  const handleMoveLocation = async (id: string, lat: number, lng: number) => {
    try {
      await api.put(`/map/locations/${id}`, {
        latitude: Math.round(lat * 100000) / 100000,
        longitude: Math.round(lng * 100000) / 100000,
      });
      message.success('Location moved');
      fetchAllLocations();
      fetchLocations();
    } catch {
      message.error('Failed to move location');
    }
  };

  const openEdit = (location: Location) => {
    setEditingLocation(location);
    editForm.setFieldsValue({
      address: location.address,
      buildingType: location.buildingType,
      buildingNotes: location.buildingNotes,
      latitude: location.latitude ? parseFloat(location.latitude) : undefined,
      longitude: location.longitude ? parseFloat(location.longitude) : undefined,
    });
    setEditDrawerOpen(true);
    // Fetch history for this location
    fetchLocationHistory(location.id, 1);
  };

  // Expandable row renderer to show addresses (units) for multi-unit buildings
  const expandedRowRender = (location: Location) => {
    if (!location.addresses || location.addresses.length === 0) {
      return (
        <div style={{ padding: '12px 24px', textAlign: 'center' }}>
          <Text type="secondary">
            No units/addresses defined for this location yet.
          </Text>
          <div style={{ marginTop: 8, fontSize: 12 }}>
            <Text type="secondary">
              Units can be added during canvassing or imported via NAR data.
            </Text>
          </div>
        </div>
      );
    }

    const addressColumns: ColumnsType<Address> = [
      {
        title: 'Unit',
        dataIndex: 'unitNumber',
        key: 'unitNumber',
        width: 100,
        render: (unit: string | null) => unit || '--',
      },
      {
        title: 'Name',
        key: 'name',
        render: (_: unknown, addr: Address) => {
          const name = [addr.firstName, addr.lastName].filter(Boolean).join(' ');
          return name || '--';
        },
      },
      {
        title: 'Contact',
        key: 'contact',
        render: (_: unknown, addr: Address) => {
          const parts: string[] = [];
          if (addr.email) parts.push(addr.email);
          if (addr.phone) parts.push(addr.phone);
          return parts.length > 0 ? parts.join(' • ') : '--';
        },
        responsive: ['md'],
      },
      {
        title: 'Support Level',
        dataIndex: 'supportLevel',
        key: 'supportLevel',
        width: 120,
        render: (level: SupportLevel | null) => {
          if (!level) return <Tag>None</Tag>;
          return <Tag color={SUPPORT_LEVEL_COLORS[level]}>{SUPPORT_LEVEL_LABELS[level]}</Tag>;
        },
        responsive: ['md'],
      },
      {
        title: 'Sign',
        key: 'sign',
        width: 100,
        render: (_: unknown, addr: Address) => {
          if (!addr.sign) return 'No';
          return addr.signSize ? `Yes (${addr.signSize})` : 'Yes';
        },
        responsive: ['lg'],
      },
      {
        title: 'Notes',
        dataIndex: 'notes',
        key: 'notes',
        ellipsis: true,
        render: (notes: string | null) => notes || '--',
        responsive: ['lg'],
      },
    ];

    return (
      <div style={{ padding: '0 24px 12px' }}>
        <Table<Address>
          columns={addressColumns}
          dataSource={location.addresses}
          rowKey="id"
          pagination={false}
          size="small"
          bordered
        />
      </div>
    );
  };

  const columns: ColumnsType<Location> = [
    {
      title: 'Address',
      dataIndex: 'address',
      key: 'address',
      render: (addr: string) => (
        <span style={{ fontWeight: 500 }}>{addr || '--'}</span>
      ),
    },
    {
      title: 'Building Type',
      dataIndex: 'buildingType',
      key: 'buildingType',
      render: (type: BuildingType) => (
        <Tag color={BUILDING_TYPE_COLORS[type]}>
          {BUILDING_TYPE_LABELS[type]}
        </Tag>
      ),
      responsive: ['md'],
    },
    {
      title: 'Total Units',
      dataIndex: 'totalUnits',
      key: 'totalUnits',
      align: 'center',
      width: 120,
      responsive: ['md'],
    },
    {
      title: 'Coordinates',
      key: 'coords',
      render: (_: unknown, record: Location) =>
        record.latitude && record.longitude
          ? `${Number(record.latitude).toFixed(5)}, ${Number(record.longitude).toFixed(5)}`
          : '--',
      responsive: ['lg'],
    },
    {
      title: 'Geocode',
      key: 'geocode',
      render: (_: unknown, record: Location) => {
        if (record.geocodeConfidence != null && record.geocodeConfidence > 0) {
          const confidence = record.geocodeConfidence;
          let color: string;
          let icon: React.ReactNode;
          let label: string;

          if (confidence >= 85) {
            color = 'success';
            icon = <CheckCircleOutlined />;
            label = `High (${confidence}%)`;
          } else if (confidence >= 60) {
            color = 'warning';
            icon = <InfoCircleOutlined />;
            label = `Medium (${confidence}%)`;
          } else {
            color = 'error';
            icon = <WarningOutlined />;
            label = `Low (${confidence}%)`;
          }

          return (
            <Space direction="vertical" size={0}>
              <Tag color={color} icon={icon}>
                {label}
              </Tag>
              {record.geocodeProvider && (
                <Text type="secondary" style={{ fontSize: 11 }}>
                  {record.geocodeProvider.toLowerCase()}
                </Text>
              )}
            </Space>
          );
        }

        if (record.latitude && record.longitude) {
          return <Tag color="blue">Manual</Tag>;
        }

        return <Tag>None</Tag>;
      },
      responsive: ['lg'],
    },
    {
      title: 'Created',
      dataIndex: 'createdAt',
      key: 'createdAt',
      render: (date: string) => dayjs(date).format('YYYY-MM-DD'),
      responsive: ['xl'],
    },
    {
      title: 'Actions',
      key: 'actions',
      width: 120,
      render: (_: unknown, record: Location) => (
        <Space>
          <Button
            type="link"
            size="small"
            icon={<EditOutlined />}
            onClick={(e) => {
              e.stopPropagation();
              openEdit(record);
            }}
            title="Edit"
          />
          <Popconfirm
            title="Delete this location?"
            onConfirm={() => handleDelete(record.id)}
          >
            <Button
              type="link"
              size="small"
              danger
              icon={<DeleteOutlined />}
              title="Delete"
              onClick={(e) => e.stopPropagation()}
            />
          </Popconfirm>
        </Space>
      ),
    },
  ];

  const locationFormFields = (form: ReturnType<typeof Form.useForm>[0]) => (
    <>
      <Form.Item
        name="address"
        label="Street Address"
        rules={[{ required: true, message: 'Address is required' }]}
        help="Base building address (without unit number)"
      >
        <Input
          placeholder="123 Main St, City, Province"
          addonAfter={
            <Button
              type="link"
              size="small"
              icon={<AimOutlined />}
              loading={geocoding}
              onClick={() => handleGeocode(form)}
              style={{ padding: 0, height: 'auto' }}
              aria-label="Geocode address to get coordinates"
            >
              Geocode
            </Button>
          }
        />
      </Form.Item>
      <Row gutter={12}>
        <Col span={12}>
          <Form.Item
            name="latitude"
            label="Latitude"
            rules={[{ required: true, message: 'Latitude is required' }]}
          >
            <InputNumber style={{ width: '100%' }} step={0.00001} precision={5} />
          </Form.Item>
        </Col>
        <Col span={12}>
          <Form.Item
            name="longitude"
            label="Longitude"
            rules={[{ required: true, message: 'Longitude is required' }]}
          >
            <InputNumber style={{ width: '100%' }} step={0.00001} precision={5} />
          </Form.Item>
        </Col>
      </Row>
      <Form.Item name="buildingType" label="Building Type" initialValue="SINGLE_FAMILY">
        <Radio.Group buttonStyle="solid">
          {(Object.keys(BUILDING_TYPE_LABELS) as BuildingType[]).map((type) => (
            <Radio.Button key={type} value={type}>
              {BUILDING_TYPE_LABELS[type]}
            </Radio.Button>
          ))}
        </Radio.Group>
      </Form.Item>
      <Form.Item name="buildingNotes" label="Building Notes" help="Access codes, manager contact, etc.">
        <TextArea rows={3} placeholder="e.g., Access code: 1234, Ring buzzer for manager" />
      </Form.Item>
    </>
  );

  const { setPageHeader } = useOutletContext<AppOutletContext>();

  useEffect(() => {
    if (isSuperAdmin) {
      api.get<ServicesConfig>('/services/config')
        .then(({ data }) => setNocodbUrl(buildServiceUrl(data.nocodbSubdomain, data.domain, data.nocodbPort)))
        .catch(() => {});
    }
  }, [isSuperAdmin]);

  useEffect(() => {
    setPageHeader({
      title: 'Map Locations',
      actions: isSuperAdmin && nocodbUrl ? (
        <Tooltip title="Browse locations in NocoDB for advanced filtering & export">
          <Button icon={<DatabaseOutlined />} href={nocodbUrl} target="_blank" size="small">
            Browse in NocoDB
          </Button>
        </Tooltip>
      ) : undefined,
    });
    return () => setPageHeader(null);
  }, [setPageHeader, isSuperAdmin, nocodbUrl]);

  const anyDrawerOpen = createDrawerOpen || editDrawerOpen || importDrawerOpen || bulkGeocodeDrawerOpen;
  const activeDrawerWidth = isMobile ? 0 : (createDrawerOpen ? 600 : editDrawerOpen ? 700 : importDrawerOpen ? 700 : bulkGeocodeDrawerOpen ? 600 : 0);

  return (
    <>
      <div style={{ marginRight: anyDrawerOpen ? activeDrawerWidth : 0, transition: 'margin-right 0.15s cubic-bezier(0.2, 0, 0, 1)' }}>
      {/* Stats cards */}
      {stats && (
        <Row gutter={[12, 12]} style={{ marginBottom: 16 }}>
          <Col xs={12} sm={8} md={4}>
            <Card size="small">
              <Statistic title="Total" value={stats.total} />
            </Card>
          </Col>
          <Col xs={12} sm={8} md={4}>
            <Card size="small">
              <Statistic
                title="Single Family"
                value={stats.buildingTypes.SINGLE_FAMILY}
                valueStyle={{ color: '#1890ff' }}
              />
            </Card>
          </Col>
          <Col xs={12} sm={8} md={4}>
            <Card size="small">
              <Statistic
                title="Multi-Unit"
                value={stats.buildingTypes.MULTI_UNIT}
                valueStyle={{ color: '#52c41a' }}
              />
            </Card>
          </Col>
          <Col xs={12} sm={8} md={4}>
            <Card size="small">
              <Statistic
                title="Mixed Use"
                value={stats.buildingTypes.MIXED_USE}
                valueStyle={{ color: '#faad14' }}
              />
            </Card>
          </Col>
          <Col xs={12} sm={8} md={4}>
            <Card size="small">
              <Statistic
                title="Commercial"
                value={stats.buildingTypes.COMMERCIAL}
                valueStyle={{ color: '#722ed1' }}
              />
            </Card>
          </Col>
          <Col xs={12} sm={8} md={4}>
            <Card size="small">
              <Statistic
                title="Geocoded"
                value={stats.total > 0 ? Math.round((stats.geocoded / stats.total) * 100) : 0}
                suffix="%"
                valueStyle={{ color: '#52c41a' }}
              />
            </Card>
          </Col>
        </Row>
      )}

      {/* Confidence Distribution Stats */}
      {stats && stats.confidence && (
        <Row gutter={[12, 12]} style={{ marginBottom: 16 }}>
          <Col xs={12} sm={6} md={4}>
            <Card size="small">
              <Statistic
                title="High Confidence"
                value={stats.confidence.high}
                prefix={<CheckCircleOutlined />}
                valueStyle={{ color: '#52c41a' }}
                suffix={<Text type="secondary" style={{ fontSize: 12 }}>≥85%</Text>}
              />
            </Card>
          </Col>
          <Col xs={12} sm={6} md={4}>
            <Card size="small">
              <Statistic
                title="Medium Confidence"
                value={stats.confidence.medium}
                prefix={<InfoCircleOutlined />}
                valueStyle={{ color: '#faad14' }}
                suffix={<Text type="secondary" style={{ fontSize: 12 }}>60-84%</Text>}
              />
            </Card>
          </Col>
          <Col xs={12} sm={6} md={4}>
            <Card size="small">
              <Statistic
                title="Low Confidence"
                value={stats.confidence.low}
                prefix={<WarningOutlined />}
                valueStyle={{ color: '#ff4d4f' }}
                suffix={<Text type="secondary" style={{ fontSize: 12 }}>&lt;60%</Text>}
              />
            </Card>
          </Col>
          <Col xs={12} sm={6} md={4}>
            <Card size="small">
              <Statistic
                title="Manual/None"
                value={stats.confidence.none}
                valueStyle={{ color: '#8c8c8c' }}
              />
            </Card>
          </Col>
          {stats.confidence.average !== null && (
            <Col xs={12} sm={6} md={4}>
              <Card size="small">
                <Statistic
                  title="Avg Confidence"
                  value={stats.confidence.average}
                  suffix="%"
                  valueStyle={{ color: '#1890ff' }}
                />
              </Card>
            </Col>
          )}
        </Row>
      )}

      {/* Action bar */}
      <Row justify="space-between" align="middle" style={{ marginBottom: 16 }}>
        <Space wrap>
          <Button icon={<ScissorOutlined />} onClick={() => navigate('/app/map/cuts')}>Cuts</Button>
          <Button icon={<EyeOutlined />} onClick={() => navigate('/map')}>Public Map</Button>
          <Button icon={<SettingOutlined />} onClick={() => navigate('/app/map/settings')}>Settings</Button>
        </Space>
        <Space wrap>
          <Button icon={<DownloadOutlined />} onClick={handleExportCsv}>Export CSV</Button>
          <Button icon={<UploadOutlined />} onClick={() => setImportDrawerOpen(true)}>Import CSV</Button>
          <Button icon={<AimOutlined />} onClick={handleGeocodeMissing} loading={geocodingMissing}>Geocode Missing</Button>
          <Button icon={<TableOutlined />} onClick={() => setBulkGeocodeDrawerOpen(true)}>Bulk Re-Geocode</Button>
          <Button type="primary" icon={<PlusOutlined />} onClick={() => setCreateDrawerOpen(true)} data-tour-map-add>Add Location</Button>
        </Space>
      </Row>

      <Tabs
        activeKey={activeTab}
        onChange={setActiveTab}
        items={[
          {
            key: 'table',
            label: <span><TableOutlined /> Table</span>,
            children: (
              <>
                {/* Filters */}
                <Row gutter={[12, 12]} style={{ marginBottom: 16 }} data-tour-map-filters>
                  <Col xs={24} sm={14} md={12}>
                    <Input
                      placeholder="Search address, postal code"
                      prefix={<SearchOutlined />}
                      value={search}
                      onChange={(e) => handleSearchChange(e.target.value)}
                      allowClear
                    />
                  </Col>
                  <Col xs={12} sm={10} md={6}>
                    <Select
                      placeholder="Confidence Level"
                      options={[
                        { value: 'high', label: 'High (≥85%)' },
                        { value: 'medium', label: 'Medium (60-84%)' },
                        { value: 'low', label: 'Low (<60%)' },
                        { value: 'none', label: 'Manual/None' },
                      ]}
                      value={confidenceFilter}
                      onChange={setConfidenceFilter}
                      allowClear
                      style={{ width: '100%' }}
                    />
                  </Col>
                  {selectedRowKeys.length > 0 && (
                    <Col>
                      <Popconfirm
                        title={`Delete ${selectedRowKeys.length} locations?`}
                        onConfirm={handleBulkDelete}
                      >
                        <Button danger>
                          Delete Selected ({selectedRowKeys.length})
                        </Button>
                      </Popconfirm>
                    </Col>
                  )}
                </Row>

                <div data-tour-map-table>
                  <Table<Location>
                    columns={columns}
                    dataSource={locations}
                    rowKey="id"
                    loading={loading}
                    scroll={{ x: 'max-content' }}
                    rowSelection={{
                      selectedRowKeys,
                      onChange: (keys) => setSelectedRowKeys(keys as string[]),
                    }}
                    expandable={{
                      expandedRowRender,
                      rowExpandable: (record) => (record.totalUnits ?? 0) > 1 || (record.addresses && record.addresses.length > 0) || false,
                    }}
                    pagination={{
                      current: pagination.page,
                      pageSize: pagination.limit,
                      total: pagination.total,
                      showSizeChanger: true,
                      showTotal: (total) => `${total} locations`,
                    }}
                    onChange={handleTableChange}
                    locale={{ emptyText: (debouncedSearch || confidenceFilter)
                      ? 'No locations match your filters.'
                      : <div style={{ padding: 16 }}>
                          <div style={{ marginBottom: 8, color: 'rgba(255,255,255,0.45)' }}>No locations yet.</div>
                          <Button type="primary" icon={<UploadOutlined />} onClick={() => setImportDrawerOpen(true)}>Import CSV</Button>
                        </div>
                    }}
                  />
                </div>
              </>
            ),
          },
          {
            key: 'map',
            label: <span><EnvironmentOutlined /> Map</span>,
            children: (
              <AdminMapView
                locations={allLocations}
                loading={mapLoading}
                onEditLocation={openEdit}
                onAddLocationAtPoint={isMobile ? undefined : handleAddFromMap}
                onMoveLocation={isMobile ? undefined : handleMoveLocation}
                onRefresh={handleRefresh}
                onMapMove={handleMapMove}
                visible={activeTab === 'map'}
              />
            ),
          },
        ]}
      />
      </div>

      {/* Create Drawer */}
      <Drawer
        title="Add Location"
        open={createDrawerOpen}
        destroyOnHidden
        mask={false}
        width={isMobile ? '100%' : 600}
        rootStyle={{ position: 'absolute', top: 64, height: 'calc(100vh - 64px)' }}
        onClose={() => {
          setCreateDrawerOpen(false);
          createForm.resetFields();
        }}
        extra={
          <Space>
            <Button onClick={() => {
              setCreateDrawerOpen(false);
              createForm.resetFields();
            }}>
              Cancel
            </Button>
            <Button type="primary" onClick={() => createForm.submit()}>
              Create
            </Button>
          </Space>
        }
      >
        <Form form={createForm} onFinish={handleCreate} layout="vertical">
          {locationFormFields(createForm)}
        </Form>
      </Drawer>

      {/* Edit Drawer */}
      <Drawer
        title="Edit Location"
        open={editDrawerOpen}
        mask={false}
        destroyOnHidden
        width={isMobile ? '100%' : 700}
        rootStyle={{ position: 'absolute', top: 64, height: 'calc(100vh - 64px)' }}
        onClose={() => {
          setEditDrawerOpen(false);
          setEditingLocation(null);
          setLocationHistory([]);
          setHistoryPagination({ page: 1, limit: 20, total: 0, totalPages: 0 });
          editForm.resetFields();
        }}
        extra={
          <Space>
            <Button onClick={() => {
              setEditDrawerOpen(false);
              setEditingLocation(null);
              editForm.resetFields();
            }}>
              Cancel
            </Button>
            <Button type="primary" onClick={() => editForm.submit()}>
              Save
            </Button>
          </Space>
        }
      >
        <Tabs
          defaultActiveKey="details"
          items={[
            {
              key: 'details',
              label: 'Details',
              children: (
                <Form form={editForm} onFinish={handleEdit} layout="vertical">
                  {locationFormFields(editForm)}
                </Form>
              ),
            },
            {
              key: 'history',
              label: (
                <span>
                  <ClockCircleOutlined /> History
                </span>
              ),
              children: (
                <Table
                  dataSource={locationHistory}
                  loading={historyLoading}
                  rowKey="id"
                  size="small"
                  pagination={{
                    current: historyPagination.page,
                    pageSize: historyPagination.limit,
                    total: historyPagination.total,
                    onChange: (page) => {
                      if (editingLocation) {
                        fetchLocationHistory(editingLocation.id, page);
                      }
                    },
                  }}
                  columns={[
                    {
                      title: 'Action',
                      dataIndex: 'action',
                      key: 'action',
                      width: 150,
                      render: (action: string) => (
                        <Tag color={LOCATION_HISTORY_ACTION_COLORS[action as keyof typeof LOCATION_HISTORY_ACTION_COLORS]}>
                          {LOCATION_HISTORY_ACTION_LABELS[action as keyof typeof LOCATION_HISTORY_ACTION_LABELS]}
                        </Tag>
                      ),
                    },
                    {
                      title: 'Field',
                      dataIndex: 'field',
                      key: 'field',
                      width: 120,
                      render: (field: string | null) => field ? <code>{field}</code> : '--',
                    },
                    {
                      title: 'Change',
                      key: 'change',
                      render: (_, record) => {
                        if (!record.field) return '--';
                        return (
                          <span style={{ fontSize: 12 }}>
                            <Text delete type="secondary">{record.oldValue || '(empty)'}</Text>
                            {' → '}
                            <Text strong>{record.newValue || '(empty)'}</Text>
                          </span>
                        );
                      },
                    },
                    {
                      title: 'User',
                      dataIndex: ['user', 'email'],
                      key: 'user',
                      width: 180,
                      render: (email: string | null) => email || <Text type="secondary">System</Text>,
                    },
                    {
                      title: 'When',
                      dataIndex: 'createdAt',
                      key: 'createdAt',
                      width: 180,
                      render: (date: string) => dayjs(date).format('MMM D, YYYY h:mm A'),
                    },
                  ]}
                />
              ),
            },
          ]}
        />
      </Drawer>

      {/* Import CSV / Bulk Import Drawer */}
      <Drawer
        title="Import Locations"
        open={importDrawerOpen}
        destroyOnHidden
        mask={false}
        width={isMobile ? '100%' : 700}
        rootStyle={{ position: 'absolute', top: 64, height: 'calc(100vh - 64px)' }}
        onClose={() => {
          setImportDrawerOpen(false);
          setBulkImportResult(null);
          setNarImportResult(null);
          setNarProgress(null);
          stopNarPolling();
          setImporting(false);
          setImportFormat('standard');
          setImportFilterType('none');
          setImportCutId(undefined);
          setImportCity('');
          setImportProvince(undefined);
          setNarSelectedProvince(undefined);
          setNarFilterType('none');
          setNarFilterCity('');
          setNarFilterPostalPrefix('');
          setNarFilterCutId(undefined);
        }}
        extra={
          <Button onClick={() => {
            setImportDrawerOpen(false);
            setImportFormat('standard');
          }}>
            Close
          </Button>
        }
      >
        <Radio.Group
          value={importFormat}
          onChange={(e) => {
            setImportFormat(e.target.value);
            setBulkImportResult(null);
            setNarImportResult(null);
            if (e.target.value === 'server' && narDatasets.length === 0) {
              fetchNarDatasets();
            }
          }}
          style={{ marginBottom: 16 }}
          optionType="button"
          buttonStyle="solid"
        >
          <Radio.Button value="standard">Standard CSV</Radio.Button>
          <Radio.Button value="nar">NAR Upload</Radio.Button>
          <Radio.Button value="server">NAR Server</Radio.Button>
          <Radio.Button value="area">Area Import</Radio.Button>
        </Radio.Group>

        {importFormat === 'area' && (
          <AreaImportWizard
            cuts={cuts}
            onComplete={() => {
              setImportDrawerOpen(false);
              setImportFormat('standard');
              fetchLocations();
              fetchStats();
            }}
          />
        )}

        {importFormat === 'standard' && (
          <>
            <Typography.Paragraph type="secondary" style={{ fontSize: 12 }}>
              Columns: address, first name, last name, email, phone, unit number,
              support level (1-4), sign (yes/no), sign size, notes, latitude, longitude.
              Column names are matched flexibly.
            </Typography.Paragraph>
            <Upload.Dragger
              accept=".csv"
              maxCount={1}
              showUploadList={false}
              customRequest={() => {}}
              onChange={(info) => {
                if (info.file.status !== 'uploading' && info.file) {
                  handleImportCsv(info.file);
                }
              }}
              disabled={importing}
            >
              <p className="ant-upload-drag-icon">
                <UploadOutlined style={{ fontSize: 32, color: '#9d4edd' }} />
              </p>
              <p className="ant-upload-text">
                {importing ? 'Importing...' : 'Click or drag CSV file to upload'}
              </p>
            </Upload.Dragger>
          </>
        )}

        {importFormat === 'nar' && (
          <>
            <Typography.Paragraph type="secondary" style={{ fontSize: 12 }}>
              Statistics Canada NAR Address CSV. Supports both 2025 format (CIVIC_NO,
              OFFICIAL_STREET_NAME, BG_X/BG_Y) and legacy format (STR_NBR, STR_NME, LAT/LNG).
              Columns detected automatically.
            </Typography.Paragraph>

            <div style={{ marginBottom: 12 }}>
              <Text type="secondary" style={{ fontSize: 11, display: 'block', marginBottom: 4 }}>
                Geographic Filter
              </Text>
              <Select
                value={importFilterType}
                onChange={(val) => {
                  setImportFilterType(val);
                  setImportCutId(undefined);
                  setImportCity('');
                  setImportProvince(undefined);
                }}
                style={{ width: '100%', marginBottom: 8 }}
                options={[
                  { value: 'none', label: 'No filter — import all rows' },
                  { value: 'mapArea', label: 'Map settings area (configured center + zoom)' },
                  { value: 'city', label: 'City name' },
                  { value: 'province', label: 'Province / Territory' },
                  { value: 'cut', label: 'Cut boundary' },
                ]}
              />

              {importFilterType === 'cut' && (
                <Select
                  placeholder="Select a cut..."
                  allowClear
                  value={importCutId}
                  onChange={setImportCutId}
                  style={{ width: '100%' }}
                  options={cuts.map((c) => ({ value: c.id, label: c.name }))}
                />
              )}

              {importFilterType === 'city' && (
                <Input
                  placeholder="Enter city name (e.g. Ottawa, Edmonton)"
                  value={importCity}
                  onChange={(e) => setImportCity(e.target.value)}
                />
              )}

              {importFilterType === 'province' && (
                <Select
                  placeholder="Select province..."
                  allowClear
                  value={importProvince}
                  onChange={setImportProvince}
                  style={{ width: '100%' }}
                  options={[
                    { value: 'AB', label: 'AB — Alberta' },
                    { value: 'BC', label: 'BC — British Columbia' },
                    { value: 'MB', label: 'MB — Manitoba' },
                    { value: 'NB', label: 'NB — New Brunswick' },
                    { value: 'NL', label: 'NL — Newfoundland and Labrador' },
                    { value: 'NS', label: 'NS — Nova Scotia' },
                    { value: 'NT', label: 'NT — Northwest Territories' },
                    { value: 'NU', label: 'NU — Nunavut' },
                    { value: 'ON', label: 'ON — Ontario' },
                    { value: 'PE', label: 'PE — Prince Edward Island' },
                    { value: 'QC', label: 'QC — Quebec' },
                    { value: 'SK', label: 'SK — Saskatchewan' },
                    { value: 'YT', label: 'YT — Yukon' },
                  ]}
                />
              )}

              {importFilterType === 'mapArea' && (
                <Text type="secondary" style={{ fontSize: 11 }}>
                  Filters to the approximate visible area from your Map Settings (center point + zoom level).
                </Text>
              )}
            </div>

            <div style={{ marginBottom: 12 }}>
              <Switch
                size="small"
                checked={importResidentialOnly}
                onChange={setImportResidentialOnly}
              />{' '}
              <Text type="secondary" style={{ fontSize: 11 }}>
                Residential only (skip commercial/industrial addresses)
              </Text>
            </div>

            <Upload.Dragger
              accept=".csv"
              maxCount={1}
              showUploadList={false}
              customRequest={() => {}}
              onChange={(info) => {
                if (info.file.status !== 'uploading' && info.file) {
                  handleBulkImport(info.file);
                }
              }}
              disabled={importing}
            >
              <p className="ant-upload-drag-icon">
                <UploadOutlined style={{ fontSize: 32, color: '#9d4edd' }} />
              </p>
              <p className="ant-upload-text">
                {importing ? 'Importing...' : 'Click or drag CSV file to upload'}
              </p>
              <p className="ant-upload-hint" style={{ fontSize: 11 }}>
                Max 100MB per file. For larger datasets use NAR Server mode.
              </p>
            </Upload.Dragger>

            {bulkImportResult && (
              <div style={{ marginTop: 16 }}>
                <Row gutter={[8, 8]}>
                  <Col span={8}>
                    <Card size="small">
                      <Statistic title="Total Rows" value={bulkImportResult.total} valueStyle={{ fontSize: 18 }} />
                    </Card>
                  </Col>
                  <Col span={8}>
                    <Card size="small">
                      <Statistic title="Created" value={bulkImportResult.created} valueStyle={{ fontSize: 18, color: '#52c41a' }} />
                    </Card>
                  </Col>
                  <Col span={8}>
                    <Card size="small">
                      <Statistic title="Duplicates" value={bulkImportResult.skippedDuplicate} valueStyle={{ fontSize: 18, color: '#faad14' }} />
                    </Card>
                  </Col>
                  <Col span={8}>
                    <Card size="small">
                      <Statistic title="Out of Bounds" value={bulkImportResult.skippedOutOfBounds} valueStyle={{ fontSize: 18, color: '#ff4d4f' }} />
                    </Card>
                  </Col>
                  <Col span={8}>
                    <Card size="small">
                      <Statistic title="Invalid" value={bulkImportResult.skippedInvalid} valueStyle={{ fontSize: 18, color: '#ff4d4f' }} />
                    </Card>
                  </Col>
                  <Col span={8}>
                    <Card size="small">
                      <Statistic title="Errors" value={bulkImportResult.errors.length} valueStyle={{ fontSize: 18, color: '#ff4d4f' }} />
                    </Card>
                  </Col>
                </Row>
                {bulkImportResult.errors.length > 0 && (
                  <div style={{ marginTop: 8, maxHeight: 150, overflow: 'auto', background: '#fafafa', borderRadius: 4, padding: 8 }}>
                    {bulkImportResult.errors.map((e, i) => (
                      <div key={i} style={{ fontSize: 11, color: '#ff4d4f', marginBottom: 2 }}>{e}</div>
                    ))}
                  </div>
                )}
              </div>
            )}
          </>
        )}

        {importFormat === 'server' && (
          <>
            <Typography.Paragraph type="secondary" style={{ fontSize: 12 }}>
              Import from NAR data files on the server. Download the NAR package from{' '}
              <a href="https://www150.statcan.gc.ca/n1/pub/46-26-0002/462600022022001-eng.htm" target="_blank" rel="noopener noreferrer">
                Statistics Canada
              </a>, extract to the server data directory, then select a province below.
            </Typography.Paragraph>

            {!narDir && !narDatasetsLoading && (
              <div style={{ textAlign: 'center', padding: 24 }}>
                <Button onClick={fetchNarDatasets} loading={narDatasetsLoading}>
                  Scan Server Directory
                </Button>
              </div>
            )}

            {narDatasetsLoading && (
              <div style={{ textAlign: 'center', padding: 24 }}>
                <Text type="secondary">Scanning data directory...</Text>
              </div>
            )}

            {narDir && narDatasets.length === 0 && !narDatasetsLoading && (
              <div style={{ textAlign: 'center', padding: 24 }}>
                <Text type="warning">No NAR datasets found in {narDir}. Extract the NAR zip and ensure Addresses/ and Locations/ directories exist.</Text>
              </div>
            )}

            {narDatasets.length > 0 && (
              <>
                <Text type="secondary" style={{ fontSize: 11, display: 'block', marginBottom: 8 }}>
                  {narDatasets.length} provinces available in {narDir}
                </Text>

                <Select
                  placeholder="Select province to import..."
                  value={narSelectedProvince}
                  onChange={setNarSelectedProvince}
                  style={{ width: '100%', marginBottom: 12 }}
                  options={narDatasets.map((d) => ({
                    value: d.provinceCode,
                    label: `${d.provinceAbbr} — ${d.provinceName} (${d.addressFiles.length} files, ${formatNarSize(d.totalAddressSize)})`,
                  }))}
                />

                <div style={{ marginBottom: 12 }}>
                  <Text type="secondary" style={{ fontSize: 11, display: 'block', marginBottom: 4 }}>
                    Geographic Filter
                  </Text>
                  <Select
                    value={narFilterType}
                    onChange={(val) => {
                      setNarFilterType(val);
                      setNarFilterCity('');
                      setNarFilterPostalPrefix('');
                      setNarFilterCutId(undefined);
                    }}
                    style={{ width: '100%', marginBottom: 8 }}
                    options={[
                      { value: 'none', label: 'No filter — import all addresses' },
                      { value: 'city', label: 'City name' },
                      { value: 'postalPrefix', label: 'Postal code prefix (FSA, e.g. K1A)' },
                      { value: 'cut', label: 'Cut boundary' },
                    ]}
                  />

                  {narFilterType === 'city' && (
                    <Input
                      placeholder="City name (e.g. Fredericton, Ottawa)"
                      value={narFilterCity}
                      onChange={(e) => setNarFilterCity(e.target.value)}
                    />
                  )}

                  {narFilterType === 'postalPrefix' && (
                    <Input
                      placeholder="Postal prefix, 3 chars (e.g. K1A, E3B)"
                      value={narFilterPostalPrefix}
                      onChange={(e) => setNarFilterPostalPrefix(e.target.value.toUpperCase().slice(0, 3))}
                      maxLength={3}
                    />
                  )}

                  {narFilterType === 'cut' && (
                    <Select
                      placeholder="Select a cut..."
                      allowClear
                      value={narFilterCutId}
                      onChange={setNarFilterCutId}
                      style={{ width: '100%' }}
                      options={cuts.map((c) => ({ value: c.id, label: c.name }))}
                    />
                  )}
                </div>

                <div style={{ marginBottom: 16 }}>
                  <Switch
                    size="small"
                    checked={narResidentialOnly}
                    onChange={setNarResidentialOnly}
                  />{' '}
                  <Text type="secondary" style={{ fontSize: 11 }}>
                    Residential only (skip non-residential addresses)
                  </Text>
                </div>

                <Button
                  type="primary"
                  onClick={handleNarServerImport}
                  loading={importing && !narProgress}
                  disabled={!narSelectedProvince || importing}
                  block
                >
                  {importing
                    ? 'Importing...'
                    : `Import ${narSelectedProvince ? narDatasets.find((d) => d.provinceCode === narSelectedProvince)?.provinceAbbr : ''} Addresses`}
                </Button>

                {/* Live progress display */}
                {importing && narProgress &&
                 typeof narProgress.locationsCreated === 'number' &&
                 typeof narProgress.totalRows === 'number' && (
                  <div style={{ marginTop: 16 }}>
                    <Progress
                      status="active"
                      showInfo={false}
                      strokeColor={{ from: '#108ee9', to: '#87d068' }}
                      percent={99.9}
                      style={{ marginBottom: 8 }}
                    />
                    <Text type="secondary" style={{ fontSize: 12, display: 'block', marginBottom: 8 }}>
                      {narProgress.status === 'loading-locations'
                        ? 'Loading location coordinates...'
                        : narProgress.currentFile
                          ? `Processing ${narProgress.currentFile}...`
                          : 'Processing...'}
                    </Text>
                    <Row gutter={[8, 8]}>
                      <Col span={8}>
                        <Statistic
                          title="Rows Processed"
                          value={narProgress.totalRows?.toLocaleString() ?? '0'}
                          valueStyle={{ fontSize: 16 }}
                        />
                      </Col>
                      <Col span={8}>
                        <Statistic
                          title="Locations Created"
                          value={narProgress.locationsCreated?.toLocaleString() ?? '0'}
                          valueStyle={{ fontSize: 16, color: '#52c41a' }}
                        />
                      </Col>
                      <Col span={8}>
                        <Statistic
                          title="Skipped"
                          value={
                            (
                              (narProgress.skippedDuplicate ?? 0) +
                              (narProgress.skippedOutOfBounds ?? 0) +
                              (narProgress.skippedNonResidential ?? 0) +
                              (narProgress.skippedInvalid ?? 0)
                            ).toLocaleString()
                          }
                          valueStyle={{ fontSize: 16, color: '#faad14' }}
                        />
                      </Col>
                    </Row>
                  </div>
                )}
              </>
            )}

            {narImportResult && (
              <div style={{ marginTop: 16 }}>
                <Text strong style={{ display: 'block', marginBottom: 8 }}>
                  {narImportResult.provinceName} — {(narImportResult.durationMs / 1000).toFixed(1)}s
                </Text>
                <Row gutter={[8, 8]}>
                  <Col span={8}>
                    <Card size="small">
                      <Statistic title="Total Rows" value={narImportResult.totalRows} valueStyle={{ fontSize: 18 }} />
                    </Card>
                  </Col>
                  <Col span={8}>
                    <Card size="small">
                      <Statistic title="Created" value={narImportResult.created} valueStyle={{ fontSize: 18, color: '#52c41a' }} />
                    </Card>
                  </Col>
                  <Col span={8}>
                    <Card size="small">
                      <Statistic title="Duplicates" value={narImportResult.skippedDuplicate} valueStyle={{ fontSize: 18, color: '#faad14' }} />
                    </Card>
                  </Col>
                  <Col span={8}>
                    <Card size="small">
                      <Statistic title="Out of Bounds" value={narImportResult.skippedOutOfBounds} valueStyle={{ fontSize: 18, color: '#ff4d4f' }} />
                    </Card>
                  </Col>
                  <Col span={8}>
                    <Card size="small">
                      <Statistic title="Non-Residential" value={narImportResult.skippedNonResidential} valueStyle={{ fontSize: 18, color: '#faad14' }} />
                    </Card>
                  </Col>
                  <Col span={8}>
                    <Card size="small">
                      <Statistic title="Invalid" value={narImportResult.skippedInvalid} valueStyle={{ fontSize: 18, color: '#ff4d4f' }} />
                    </Card>
                  </Col>
                </Row>
                {narImportResult.errors.length > 0 && (
                  <div style={{ marginTop: 8, maxHeight: 150, overflow: 'auto', background: '#fafafa', borderRadius: 4, padding: 8 }}>
                    {narImportResult.errors.map((e, i) => (
                      <div key={i} style={{ fontSize: 11, color: '#ff4d4f', marginBottom: 2 }}>{e}</div>
                    ))}
                  </div>
                )}
              </div>
            )}
          </>
        )}
      </Drawer>

      {/* Bulk Re-Geocode Drawer */}
      <Drawer
        title="Bulk Re-Geocode Locations"
        open={bulkGeocodeDrawerOpen}
        destroyOnHidden
        mask={false}
        width={isMobile ? '100%' : 600}
        rootStyle={{ position: 'absolute', top: 64, height: 'calc(100vh - 64px)' }}
        onClose={() => {
          setBulkGeocodeDrawerOpen(false);
          setBulkGeocoding(false);
          setBulkGeocodeStatus(null);
          stopBulkGeocodePolling();
          bulkGeocodeForm.resetFields();
        }}
        extra={
          <Button onClick={() => {
            setBulkGeocodeDrawerOpen(false);
            setBulkGeocoding(false);
            setBulkGeocodeStatus(null);
            stopBulkGeocodePolling();
            bulkGeocodeForm.resetFields();
          }}>
            Close
          </Button>
        }
      >
        {!bulkGeocoding && !bulkGeocodeStatus ? (
          <Form
            form={bulkGeocodeForm}
            onFinish={handleBulkGeocode}
            layout="vertical"
            initialValues={{ confidenceThreshold: 60, limit: 1000 }}
          >
            <Typography.Paragraph type="secondary">
              Re-geocode multiple locations in the background to improve their confidence scores.
              Only locations with improved results will be updated.
            </Typography.Paragraph>

            <Form.Item
              label="Confidence Threshold (%)"
              name="confidenceThreshold"
              help="Only geocode locations with confidence below this threshold"
            >
              <InputNumber min={0} max={100} style={{ width: '100%' }} />
            </Form.Item>

            <Form.Item label="Building Type Filter" name="buildingType">
              <Select placeholder="All types" allowClear>
                <Select.Option value="SINGLE_FAMILY">Single Family</Select.Option>
                <Select.Option value="MULTI_UNIT">Multi-Unit</Select.Option>
                <Select.Option value="MIXED_USE">Mixed Use</Select.Option>
                <Select.Option value="COMMERCIAL">Commercial</Select.Option>
              </Select>
            </Form.Item>

            <Form.Item
              label="Maximum Locations"
              name="limit"
              help="Process up to this many locations (max 5000)"
            >
              <InputNumber min={1} max={5000} style={{ width: '100%' }} />
            </Form.Item>

            <Form.Item>
              <Button type="primary" htmlType="submit" block size="large">
                Start Bulk Re-Geocode
              </Button>
            </Form.Item>
          </Form>
        ) : bulkGeocoding && bulkGeocodeStatus ? (
          <>
            <Typography.Title level={5}>Processing...</Typography.Title>
            {bulkGeocodeStatus.progress && (
              <>
                <Progress
                  percent={Math.round((bulkGeocodeStatus.progress.processed / bulkGeocodeStatus.progress.total) * 100)}
                  status="active"
                />
                <Typography.Paragraph type="secondary" style={{ marginTop: 16, fontSize: 12 }}>
                  {bulkGeocodeStatus.progress.currentAddress && (
                    <>Current: {bulkGeocodeStatus.progress.currentAddress}</>
                  )}
                </Typography.Paragraph>

                <Row gutter={16} style={{ marginTop: 16 }}>
                  <Col span={12}>
                    <Card size="small">
                      <Statistic title="Processed" value={bulkGeocodeStatus.progress.processed} suffix={`/ ${bulkGeocodeStatus.progress.total}`} />
                    </Card>
                  </Col>
                  <Col span={12}>
                    <Card size="small">
                      <Statistic title="Improved" value={bulkGeocodeStatus.progress.improved} valueStyle={{ color: '#52c41a' }} />
                    </Card>
                  </Col>
                </Row>

                <Row gutter={16} style={{ marginTop: 8 }}>
                  <Col span={12}>
                    <Card size="small">
                      <Statistic title="Unchanged" value={bulkGeocodeStatus.progress.unchanged} />
                    </Card>
                  </Col>
                  <Col span={12}>
                    <Card size="small">
                      <Statistic title="Failed" value={bulkGeocodeStatus.progress.failed} valueStyle={{ color: '#ff4d4f' }} />
                    </Card>
                  </Col>
                </Row>
              </>
            )}
          </>
        ) : bulkGeocodeStatus?.state === 'completed' && bulkGeocodeStatus.result ? (
          <>
            <Typography.Title level={5}>Complete!</Typography.Title>
            <Row gutter={16}>
              <Col span={8}>
                <Card size="small">
                  <Statistic title="Total" value={bulkGeocodeStatus.result.total} />
                </Card>
              </Col>
              <Col span={8}>
                <Card size="small">
                  <Statistic title="Improved" value={bulkGeocodeStatus.result.improved} valueStyle={{ color: '#52c41a' }} />
                </Card>
              </Col>
              <Col span={8}>
                <Card size="small">
                  <Statistic title="Duration" value={bulkGeocodeStatus.result.duration} suffix="s" />
                </Card>
              </Col>
            </Row>
            <Row gutter={16} style={{ marginTop: 8 }}>
              <Col span={12}>
                <Card size="small">
                  <Statistic title="Unchanged" value={bulkGeocodeStatus.result.unchanged} />
                </Card>
              </Col>
              <Col span={12}>
                <Card size="small">
                  <Statistic title="Failed" value={bulkGeocodeStatus.result.failed} valueStyle={{ color: '#ff4d4f' }} />
                </Card>
              </Col>
            </Row>
            <Button
              type="primary"
              block
              size="large"
              style={{ marginTop: 16 }}
              onClick={() => {
                setBulkGeocodeDrawerOpen(false);
                setBulkGeocoding(false);
                setBulkGeocodeStatus(null);
                stopBulkGeocodePolling();
                bulkGeocodeForm.resetFields();
              }}
            >
              Close
            </Button>
          </>
        ) : null}
      </Drawer>
      <PageTour sectionId="map-locations" />
    </>
  );
}
