import {
  PrismaClient,
  crime_rates,
  events,
  session_status,
  users,
} from '@prisma/client';
import fs from 'fs';
import path from 'path';
import { parse } from 'csv-parse/sync';
import { generateId, generateIdWithDbCounter } from '../../app/_utils/common';

export class CrimesSeeder {
  constructor(private prisma: PrismaClient) {}

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

    // Clear existing data

    try {
      // Create test user
      const user = await this.createUsers();

      // Create 5 events
      const events = await this.createEvents(user);

      // Create 5 sessions with completed status
      await this.createSessions(user, events);

      // Import monthly crime data
      await this.importMonthlyCrimeData();

      // Import yearly crime data from CSV file
      await this.importYearlyCrimeData();

      // Import all-year crime summaries (2020-2024)
      await this.importAllYearSummaries();

      console.log('✅ Crime seeding completed successfully.');
    } catch (error) {
      console.error('❌ Error seeding crimes:', error);
      throw error;
    }
  }

  private async createUsers() {
    // Check if test users already exist
    const existingUser = await this.prisma.users.findFirst({
      where: { email: 'admin@sigap.id' },
    });

    if (existingUser) {
      console.log('Users already exist, skipping creation.');
      return existingUser;
    }

    // Get admin role ID
    let roleId = await this.prisma.roles.findFirst({
      where: { name: 'admin' },
    });

    if (!roleId) {
      roleId = await this.prisma.roles.create({
        data: {
          name: 'admin',
          description: 'Administrator role',
        },
      });
    }

    // Create test users
    const user = await this.prisma.users.create({
      data: {
        email: `admin@gmail.com`,
        roles_id: roleId.id,
        confirmed_at: new Date(),
        email_confirmed_at: new Date(),
        is_anonymous: false,
        profile: {
          create: {
            first_name: 'Admin',
            last_name: 'Sigap',
            username: 'admin',
          },
        },
      },
    });

    return user;
  }

  private async createEvents(user: users) {
    // Check if events already exist
    const existingEvent = await this.prisma.events.findFirst({
      where: {
        user_id: user.id,
      },
    });

    if (existingEvent) {
      console.log('Events already exist, skipping creation.');
      return existingEvent;
    }

    const event = await this.prisma.events.create({
      data: {
        name: `Crime Inserting Event For Crime 2020 - 2024`,
        description: `Event for inserting crimes in region Jember`,
        user_id: user.id,
      },
    });

    return event;
  }

  private async createSessions(user: users, events: events) {
    // Check if sessions already exist
    const existingSession = await this.prisma.sessions.findFirst();

    if (existingSession) {
      console.log('Sessions already exist, skipping creation.');
      return;
    }

    const newSession = await this.prisma.sessions.create({
      data: {
        user_id: user.id,
        event_id: events.id,
        status: session_status.completed,
      },
    });

    return newSession;
  }

  private async importMonthlyCrimeData() {
    console.log('Importing monthly crime data...');

    // Check if crimes already exist
    const existingCrimes = await this.prisma.crimes.findFirst();
    if (existingCrimes) {
      console.log('Crimes data already exists, skipping import.');
      return;
    }

    // Read CSV file
    const csvFilePath = path.resolve(
      __dirname,
      '../data/excels/crimes/crime_monthly.csv'
    );
    const fileContent = fs.readFileSync(csvFilePath, { encoding: 'utf-8' });

    // Parse CSV
    const records = parse(fileContent, {
      columns: true,
      skip_empty_lines: true,
    });

    // Store unique district IDs to avoid duplicates
    const processedDistricts = new Set<string>();

    // Import records
    for (const record of records) {
      const crimeRate = record.level.toLowerCase() as crime_rates;

      const city = await this.prisma.cities.findFirst({
        where: {
          name: 'Jember',
        },
      });

      if (!city) {
        console.error('City not found: Jember');
        return;
      }

      const year = parseInt(record.year);
      // Create a unique ID for monthly crime data
      const crimeId = await generateIdWithDbCounter(
        'crimes',
        {
          prefix: 'CR',
          segments: {
            codes: [city.id],
            sequentialDigits: 4,
            year,
          },
          format: '{prefix}-{codes}-{sequence}-{year}',
          separator: '-',
          uniquenessStrategy: 'counter',
        },
        /(\d{4})(?=-\d{4}$)/ // Pattern to extract the 4-digit counter
      );

      console.log('Creating crime ID:', crimeId);

      await this.prisma.crimes.create({
        data: {
          id: crimeId,
          district_id: record.district_id,
          level: crimeRate,
          method: record.method || null,
          month: parseInt(record.month_num),
          year: parseInt(record.year),
          number_of_crime: parseInt(record.number_of_crime),
          score: parseFloat(record.score),
        },
      });

      // Keep track of unique districts for later creation of crime incidents
      processedDistricts.add(record.district_id);
    }

    console.log(`Imported ${records.length} monthly crime records.`);
  }

  private async importYearlyCrimeData() {
    console.log('Importing yearly crime data...');

    // Check if yearly summaries already exist (records with null month)
    const existingYearlySummary = await this.prisma.crimes.findFirst({
      where: { month: null },
    });

    if (existingYearlySummary) {
      console.log('Yearly crime data already exists, skipping import.');
      return;
    }

    // Read CSV file
    const csvFilePath = path.resolve(
      __dirname,
      '../data/excels/crimes/crime_yearly.csv'
    );
    const fileContent = fs.readFileSync(csvFilePath, { encoding: 'utf-8' });

    // Parse CSV
    const records = parse(fileContent, {
      columns: true,
      skip_empty_lines: true,
    });

    // Import records
    for (const record of records) {
      const crimeRate = record.level.toLowerCase() as crime_rates;
      const year = parseInt(record.year);

      const city = await this.prisma.cities.findFirst({
        where: {
          districts: {
            some: {
              id: record.district_id,
            },
          },
        },
      });

      if (!city) {
        console.error(`City not found for district ${record.district_id}`);
        continue;
      }

      // Create a unique ID for yearly crime data
      // const crimeId = await generateId({
      //   prefix: 'CR',
      //   segments: {
      //     codes: [city.id],
      //     sequentialDigits: 4,
      //     year,
      //   },
      //   format: '{prefix}-{codes}-{sequence}-{year}',
      //   separator: '-',
      //   randomSequence: false,
      //   uniquenessStrategy: 'counter',
      //   storage: 'database',
      //   tableName: 'crimes',
      // });

      const crimeId = await generateIdWithDbCounter(
        'crimes',
        {
          prefix: 'CR',
          segments: {
            codes: [city.id],
            sequentialDigits: 4,
            year,
          },
          format: '{prefix}-{codes}-{sequence}-{year}',
          separator: '-',
          uniquenessStrategy: 'counter',
        },
        /(\d{4})(?=-\d{4}$)/ // Pattern to extract the 4-digit counter
      );

      await this.prisma.crimes.create({
        data: {
          id: crimeId,
          district_id: record.district_id,
          level: crimeRate,
          method: record.method || 'kmeans',
          month: null,
          year: year,
          number_of_crime: parseInt(record.number_of_crime),
          score: parseInt(record.score),
        },
      });
    }

    console.log(`Imported ${records.length} yearly crime records.`);
  }

  private async importAllYearSummaries() {
    console.log('Importing all-year (2020-2024) crime summaries...');

    // Check if all-year summaries already exist (records with null month and null year)
    const existingAllYearSummaries = await this.prisma.crimes.findFirst({
      where: { month: null, year: null },
    });

    if (existingAllYearSummaries) {
      console.log('All-year crime summaries already exist, skipping import.');
      return;
    }

    // Read CSV file
    const csvFilePath = path.resolve(
      __dirname,
      '../data/excels/crimes/district_summary_2020_2024.csv'
    );
    const fileContent = fs.readFileSync(csvFilePath, { encoding: 'utf-8' });

    // Parse CSV
    const records = parse(fileContent, {
      columns: true,
      skip_empty_lines: true,
    });

    for (const record of records) {
      const crimeRate = record.level.toLowerCase() as crime_rates;
      const districtId = record.district_id;

      const city = await this.prisma.cities.findFirst({
        where: {
          districts: {
            some: {
              id: districtId,
            },
          },
        },
      });

      if (!city) {
        console.error(`City not found for district ${districtId}`);
        continue;
      }

      // Create a unique ID for all-year summary data
      const crimeId = await generateIdWithDbCounter(
        'crimes',
        {
          prefix: 'CR',
          segments: {
            codes: [city.id],
            sequentialDigits: 4,
          },
          format: '{prefix}-{codes}-{sequence}',
          separator: '-',
          uniquenessStrategy: 'counter',
        },
        /(\d{4})$/ // Pattern to extract the 4-digit counter at the end
      );

      await this.prisma.crimes.create({
        data: {
          id: crimeId,
          district_id: districtId,
          level: crimeRate,
          method: 'kmeans',
          month: null,
          year: null,
          number_of_crime: parseInt(record.crime_total),
          score: parseFloat(record.avg_score),
        },
      });
    }

    console.log(`Imported ${records.length} all-year crime summaries.`);
  }
}

// This allows the file to be executed standalone for testing
if (require.main === module) {
  const testSeeder = async () => {
    const prisma = new PrismaClient();
    const seeder = new CrimesSeeder(prisma);
    try {
      await seeder.run();
    } catch (e) {
      console.error('Error during seeding:', e);
      process.exit(1);
    } finally {
      await prisma.$disconnect();
    }
  };

  testSeeder();
}
