import { $Enums } from '@prisma/client';
import { CRIME_RATE_COLORS } from '@/app/_utils/const/map';
import type { ICrimes } from '@/app/_utils/types/crimes';
import { IDistrictFeature } from './types/map';

// Process crime data by district
export const processCrimeDataByDistrict = (crimes: ICrimes[]) => {
  return crimes.reduce(
    (acc, crime) => {
      const districtId = crime.district_id;

      acc[districtId] = {
        number_of_crime: crime.number_of_crime,
        level: crime.level,
      };
      return acc;
    },
    {} as Record<
      string,
      { number_of_crime?: number; level?: $Enums.crime_rates }
    >
  );
};

// Get color for crime rate level
export const getCrimeRateColor = (level?: $Enums.crime_rates) => {
  if (!level) return CRIME_RATE_COLORS.default;

  switch (level) {
    case 'low':
      return CRIME_RATE_COLORS.low;
    case 'medium':
      return CRIME_RATE_COLORS.medium;
    case 'high':
      return CRIME_RATE_COLORS.high;
    default:
      return CRIME_RATE_COLORS.default;
  }
};

// Create fill color expression for district layer
export const createFillColorExpression = (
  focusedDistrictId: string | null,
  crimeDataByDistrict: Record<
    string,
    { number_of_crime?: number; level?: $Enums.crime_rates }
  >
) => {
  const colorEntries = focusedDistrictId
    ? [
        [
          focusedDistrictId,
          getCrimeRateColor(crimeDataByDistrict[focusedDistrictId]?.level),
        ],
        'rgba(0,0,0,0.05)',
      ]
    : Object.entries(crimeDataByDistrict).flatMap(([districtId, data]) => {
        return [districtId, getCrimeRateColor(data.level)];
      });

  return [
    'case',
    ['has', 'kode_kec'],
    [
      'match',
      ['get', 'kode_kec'],
      ...colorEntries,
      focusedDistrictId ? 'rgba(0,0,0,0.05)' : CRIME_RATE_COLORS.default,
    ],
    CRIME_RATE_COLORS.default,
  ];
};

// Extract crime incidents for GeoJSON
export const extractCrimeIncidents = (
  crimes: ICrimes[],
  filterCategory: string | 'all'
) => {
  return crimes.flatMap((crime) => {
    if (!crime.crime_incidents) return [];

    let filteredIncidents = crime.crime_incidents;

    if (filterCategory !== 'all') {
      filteredIncidents = crime.crime_incidents.filter(
        (incident) =>
          incident.crime_categories &&
          incident.crime_categories.name === filterCategory
      );
    }

    return filteredIncidents
      .map((incident) => {
        if (!incident.locations) {
          console.warn('Missing location for incident:', incident.id);
          return null;
        }

        return {
          type: 'Feature' as const,
          properties: {
            id: incident.id,
            district: crime.districts?.name || 'Unknown',
            category: incident.crime_categories?.name || 'Unknown',
            incidentType: incident.crime_categories?.type || 'Unknown',
            level: crime.level || 'low',
            description: incident.description || '',
            status: incident.status || '',
          },
          geometry: {
            type: 'Point' as const,
            coordinates: [
              incident.locations.longitude || 0,
              incident.locations.latitude || 0,
            ],
          },
        };
      })
      .filter(Boolean);
  });
};

// Process district feature from map click
export const processDistrictFeature = (
  feature: any,
  e: any,
  districtId: string,
  crimeDataByDistrict: Record<
    string,
    { number_of_crime?: number; level?: $Enums.crime_rates }
  >,
  crimes: ICrimes[],
  year: string,
  month: string
): IDistrictFeature | null => {
  const crimeData = crimeDataByDistrict[districtId] || {};
  let crime_incidents: Array<{
    id: string;
    timestamp: Date;
    description: string;
    status: string;
    category: string;
    type: string;
    address: string;
    latitude: number;
    longitude: number;
  }> = [];

  const districtCrimes = crimes.filter(
    (crime) => crime.district_id === districtId
  );

  districtCrimes.forEach((crimeRecord) => {
    if (crimeRecord && crimeRecord.crime_incidents) {
      const incidents = crimeRecord.crime_incidents.map((incident) => ({
        id: incident.id,
        timestamp: incident.timestamp,
        description: incident.description || '',
        status: incident.status || '',
        category: incident.crime_categories?.name || '',
        type: incident.crime_categories?.type || '',
        address: incident.locations?.address || '',
        latitude: incident.locations?.latitude || 0,
        longitude: incident.locations?.longitude || 0,
      }));

      crime_incidents = [...crime_incidents, ...incidents];
    }
  });

  const firstDistrictCrime =
    districtCrimes.length > 0 ? districtCrimes[0] : null;
  if (!firstDistrictCrime) return null;

  const selectedYearNum = year
    ? Number.parseInt(year)
    : new Date().getFullYear();

  let demographics = firstDistrictCrime?.districts.demographics?.find(
    (d) => d.year === selectedYearNum
  );

  if (!demographics && firstDistrictCrime?.districts.demographics?.length) {
    demographics = firstDistrictCrime.districts.demographics.sort(
      (a, b) => b.year - a.year
    )[0];
    console.log(
      `Tidak ada data demografis untuk tahun ${selectedYearNum}, menggunakan data tahun ${demographics.year}`
    );
  }

  let geographics = firstDistrictCrime?.districts.geographics?.find(
    (g) => g.year === selectedYearNum
  );

  if (!geographics && firstDistrictCrime?.districts.geographics?.length) {
    const validGeographics = firstDistrictCrime.districts.geographics
      .filter((g) => g.year !== null)
      .sort((a, b) => (b.year || 0) - (a.year || 0));

    geographics =
      validGeographics.length > 0
        ? validGeographics[0]
        : firstDistrictCrime.districts.geographics[0];

    console.log(
      `Tidak ada data geografis untuk tahun ${selectedYearNum}, menggunakan data ${geographics.year ? `tahun ${geographics.year}` : 'tanpa tahun'}`
    );
  }

  const clickLng = e.lngLat ? e.lngLat.lng : null;
  const clickLat = e.lngLat ? e.lngLat.lat : null;

  if (!geographics) {
    console.error('Missing geographics data for district:', districtId);
    return null;
  }

  if (!demographics) {
    console.error('Missing demographics data for district:', districtId);
    return null;
  }

  return {
    id: districtId,
    name:
      feature.properties.nama ||
      feature.properties.kecamatan ||
      'Unknown District',
    longitude: geographics.longitude || clickLng || 0,
    latitude: geographics.latitude || clickLat || 0,
    number_of_crime: crimeData.number_of_crime || 0,
    level: crimeData.level || $Enums.crime_rates.low,
    demographics: {
      number_of_unemployed: demographics.number_of_unemployed,
      population: demographics.population,
      population_density: demographics.population_density,
      year: demographics.year,
    },
    geographics: {
      address: geographics.address || '',
      land_area: geographics.land_area || 0,
      year: geographics.year || 0,
      latitude: geographics.latitude,
      longitude: geographics.longitude,
    },
    crime_incidents: crime_incidents || [],
    selectedYear: year,
    selectedMonth: month,
    isFocused: true,
  };
};

/**
 * Format distance in a human-readable way
 * @param meters Distance in meters
 * @returns Formatted distance string
 */
export function formatDistance(meters: number): string {
  if (meters < 1000) {
    return `${Math.round(meters)} m`;
  } else {
    return `${(meters / 1000).toFixed(1)} km`;
  }
}