import { PrismaClient } from '@prisma/client';
import fs from 'fs';
import * as turf from '@turf/turf';
import { createClient } from '../../app/_utils/supabase/client';
import axios from 'axios';
import xlsx from 'xlsx';

export interface CreateLocationDto {
  district_id: string;
  name?: string;
  description?: string;
  address?: string;
  type?: string;
  latitude: number;
  longitude: number;
  land_area?: number;
  polygon?: Record<string, any>;
  geometry?: Record<string, any>;
  location?: string;
  year: number; // Added year field
}

interface DistrictAreaData {
  [district: string]: {
    [year: string]: number;
  };
}

export class GeoJSONSeeder {
  private mapboxToken: string;
  private areaData: DistrictAreaData = {};

  constructor(
    private prisma: PrismaClient,
    private supabase = createClient()
  ) {
    this.mapboxToken = process.env.NEXT_PUBLIC_MAPBOX_ACCESS_TOKEN || '';

    // Load area data from Excel
    this.loadAreaData();
  }

  private loadAreaData(): void {
    try {
      // Load the Excel file
      const workbook = xlsx.readFile(
        'prisma/data/excels/administrations/geographics.xlsx'
      );
      const sheetName = workbook.SheetNames[0];
      const worksheet = workbook.Sheets[sheetName];

      // Convert to JSON
      const jsonData = xlsx.utils.sheet_to_json(worksheet);

      // Parse the data into our structure
      jsonData.forEach((row: any) => {
        const districtName = row['Kecamatan'];
        if (districtName) {
          this.areaData[districtName] = {
            '2020': this.parseAreaValue(row['2020']),
            '2021': this.parseAreaValue(row['2021']),
            '2022': this.parseAreaValue(row['2022']),
            '2023': this.parseAreaValue(row['2023']),
            '2024': this.parseAreaValue(row['2024']),
          };
        }
      });

      console.log('Area data loaded successfully');
    } catch (error) {
      console.error('Error loading area data from Excel:', error);
    }
  }

  private parseAreaValue(value: string | number): number {
    if (typeof value === 'number') {
      return value;
    }

    if (typeof value === 'string') {
      // Replace comma with dot for decimal
      return parseFloat(value.replace(',', '.'));
    }

    return 0;
  }

  private getDistrictArea(districtName: string, year: number): number {
    // Normalize district name (remove case sensitivity and extra spaces)
    const normalizedName = districtName.trim().toLowerCase();

    // Try to find the district in our data
    for (const district in this.areaData) {
      if (district.trim().toLowerCase() === normalizedName) {
        return this.areaData[district][year.toString()] || 0;
      }
    }

    console.warn(
      `No area data found for district: ${districtName}, year: ${year}`
    );
    return 0;
  }

  async run(): Promise<void> {
    console.log('Seeding GeoJSON data...');

    await this.prisma.units.deleteMany({});
    await this.prisma.districts.deleteMany({});
    await this.prisma.cities.deleteMany({});
    await this.prisma.geographics.deleteMany({});

    try {
      // Load GeoJSON file
      const regencyGeoJson = JSON.parse(
        fs.readFileSync('prisma/data/geojson/jember/regency.geojson', 'utf-8')
      );
      const districtGeoJson = JSON.parse(
        fs.readFileSync('prisma/data/geojson/jember/districts.geojson', 'utf-8')
      );

      // 1. Insert Kota/Kabupaten: Jember
      let regency; // Declare regency variable outside the loop

      for (const feature of regencyGeoJson.features) {
        const properties = feature.properties;
        const geometry = feature.geometry;

        // Cleanup code
        const regencyCode = properties.kode_kk.replace(/\./g, '').trim();

        // Insert Regency
        regency = await this.prisma.cities.create({
          data: {
            id: regencyCode,
            name: properties.kab_kota,
          },
        });

        // 2. Loop Semua District di GeoJSON
        for (const feature of districtGeoJson.features) {
          const properties = feature.properties;
          const geometry = feature.geometry;

          // Cleanup code
          const districtCode = properties.kode_kec.replace(/\./g, '').trim();

          // Insert District
          const district = await this.prisma.districts.create({
            data: {
              id: districtCode,
              name: properties.kecamatan,
              city_id: regency.id,
            },
          });

          console.log(`Inserted district: ${district.name}`);

          // Buat Location satu kali untuk district (tidak dalam loop)
          const centroid = turf.centroid(feature);
          const [longitude, latitude] = centroid.geometry.coordinates;

          const address = await this.getStreetFromMapbox(longitude, latitude);

          // Insert locations for each year with appropriate land area
          const years = [2020, 2021, 2022, 2023, 2024];

          for (const year of years) {
            const landArea = this.getDistrictArea(district.name, year);

            // Create location data for this district and year
            const locationData: CreateLocationDto = {
              district_id: district.id,
              description: `Location for ${district.name} District in Jember (${year})`,
              address: address,
              type: 'district location',
              year: year,
              latitude,
              longitude,
              land_area: landArea,
              polygon: geometry,
              geometry: geometry,
              location: `POINT(${longitude} ${latitude})`,
            };

            const { error } = await this.supabase
              .from('geographics')
              .insert([locationData])
              .select();

            if (error) {
              console.error(
                `Error inserting into locations for district ${district.name} (${year}):`,
                error
              );
              continue;
            }

            console.log(
              `Inserted geographic data for: ${district.name} (${year}) with area: ${landArea} sq km`
            );
          }
        }
      }

      console.log(
        'GeoJSON data seeded successfully!',
        districtGeoJson.features.length,
        'districts inserted with 5 years of area data.'
      );
    } catch (error) {
      console.error('Error seeding GeoJSON data:', error);
    }
  }

  private async getStreetFromMapbox(lng: number, lat: number): Promise<string> {
    try {
      const response = await axios.get(
        `https://api.mapbox.com/search/geocode/v6/reverse?longitude=${lng}&latitude=${lat}&access_token=${this.mapboxToken}`
      );

      if (
        response.data &&
        response.data.features &&
        response.data.features.length > 0
      ) {
        // Extract full_address from the first feature
        const fullAddress = response.data.features[0].properties.full_address;

        return (
          fullAddress ||
          `Jalan Tidak Diketahui No. ${Math.floor(this.getRandomNumber(1, 100))}`
        );
      }

      // Fallback if no address found
      return `Jalan Tidak Diketahui No. ${Math.floor(this.getRandomNumber(1, 100))}`;
    } catch (error) {
      console.error('Error fetching street from Mapbox:', error);
      return `Jalan Tidak Diketahui No. ${Math.floor(this.getRandomNumber(1, 100))}`;
    }
  }

  private getRandomNumber(min: number, max: number): number {
    return Math.random() * (max - min) + min;
  }
}