/**
 * Import Areas from CSV -> upsert by (name + city)
 * - City is resolved from CSV's `City` column (normalized lowercase, no spaces).
 * - Only checks SuperAdmin by { firstName: "SuperAdmin" }.
 * - Uses bulkWrite upserts for speed.
 */
/* eslint-disable */
/* eslint-disable no-console */
 
import * as fs from 'fs';
import * as path from 'path';
// adjust path if needed

import mongoose, { AnyBulkWriteOperation, Types } from 'mongoose';

import connectToDatabase from '../config/dbConfig';
import User from '@/modules/user/user.model';
import City from '@/modules/master/location/city/city.model';
import Area from '@/modules/master/location/area/area.model';

type LogLevel = 'info' | 'error' | 'success';

const log = (lvl: LogLevel, msg: string) => {
  const c =
    lvl === 'error' ? '\x1b[31m' : lvl === 'success' ? '\x1b[32m' : '\x1b[36m';
  console.log(
    `${c}[${new Date().toISOString()}] ${lvl.toUpperCase()}: ${msg}\x1b[0m`,
  );
};

const CSV_PATH = './src/shared/seeder/area.csv';

interface CSVRow {
  Area: string;
  Pincode: number | string;
  City: string;
  StateName: string;
  Latitude?: number | string;
  Longitude?: number | string;
}

function escapeRegex(s: string) {
  return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}

/**
 * Normalize strings for consistent matching
 * Lowercase + remove spaces
 * (You can expand if needed, e.g. remove hyphens/dots)
 */
function norm(s: string) {
  return s ? s.toLowerCase().replace(/\s+/g, '') : '';
}

async function getSuperAdmin(): Promise<{ _id: Types.ObjectId }> {
  const superAdmin = await User.findOne(
    { firstName: 'SuperAdmin' },
    { _id: 1 },
  ).lean();
  if (!superAdmin) throw new Error('SuperAdmin user not found.');
  return { _id: superAdmin._id as Types.ObjectId };
}

function parseCSV(csvContent: string): CSVRow[] {
  const lines = csvContent.trim().split('\n');
  if (lines.length < 2) throw new Error('CSV must have header and data rows');

  const headers = lines[0].split(',').map((h) => h.trim());
  const rows: CSVRow[] = [];

  for (let i = 1; i < lines.length; i++) {
    const values = lines[i].split(',').map((v) => v.trim());

    const row: Record<string, string> = {};

    headers.forEach((header, index) => {
      row[header] = values[index] || '';
    });

    rows.push({
      Area: row.Area || '',
      Pincode: row.Pincode || '',
      City: row.City || '',
      StateName: row.StateName || '',
      Latitude: row.Latitude || undefined,
      Longitude: row.Longitude || undefined,
    });
  }

  return rows;
}

function readCSVRows(): CSVRow[] {
  const filePath = path.resolve(CSV_PATH);
  if (!fs.existsSync(filePath))
    throw new Error(`CSV file not found: ${filePath}`);

  const csvContent = fs.readFileSync(filePath, 'utf-8');
  const data = parseCSV(csvContent);

  log('info', `Read ${data.length} rows from CSV`);
  return data;
}

async function buildCityMap() {
  const cities = await City.find({}, { _id: 1, name: 1 }).lean();
  const byNorm = new Map<string, Types.ObjectId>();
  for (const c of cities) {
    const key = norm(c.name); // normalize DB city name
    byNorm.set(key, c._id as Types.ObjectId);
  }
  return byNorm;
}

function validateRow(row: CSVRow, idx: number): string[] {
  const errs: string[] = [];
  if (!row.Area?.toString().trim()) errs.push(`Row ${idx}: Area required`);
  if (!row.Pincode?.toString().trim())
    errs.push(`Row ${idx}: Pincode required`);

  if (!row.City?.toString().trim()) errs.push(`Row ${idx}: City required`);

  const pin = parseInt(String(row.Pincode), 10);
  if (isNaN(pin) || pin < 100000 || pin > 999999)
    errs.push(`Row ${idx}: Invalid Pincode`);

  const lat = row.Latitude != null ? parseFloat(String(row.Latitude)) : null;
  const lng = row.Longitude != null ? parseFloat(String(row.Longitude)) : null;

  if (lat != null && (isNaN(lat) || lat < -90 || lat > 90))
    errs.push(`Row ${idx}: Bad Latitude`);
  if (lng != null && (isNaN(lng) || lng < -180 || lng > 180))
    errs.push(`Row ${idx}: Bad Longitude`);

  return errs;
}

export async function importAreas({
  superAdminId,
}: {
  superAdminId: Types.ObjectId;
}) {
  log('info', 'Connecting DB...');

  // await connectToDatabase();
  // const coll = mongoose.connection.db.collection('areas');

  const isDataSeeded = await Area.countDocuments();
  if (isDataSeeded) {
    log('info', 'Data already seeded');
    return;
  }

  // (optional) show before
  // console.log(
  //   'Before:',
  //   (await coll.indexes()).map((i) => i.name),
  // );

  // drop all non-_id indexes
  // await coll.dropIndexes();

  // (optional) show after
  // console.log(
  //   'After:',
  //   (await coll.indexes()).map((i) => i.name),
  // );

  log('info', 'Fetching SuperAdmin...');
  const superAdmin = await getSuperAdmin();

  log('info', 'Reading CSV...');
  const rows = readCSVRows();

  log('info', 'Building city map...');
  const cityByNorm = await buildCityMap();

  const ops: AnyBulkWriteOperation<typeof Area>[] = [];

  let processed = 0,
    inserted = 0,
    updated = 0;
  const errors: string[] = [];

  for (let i = 0; i < rows.length; i++) {
    const r = rows[i];
    const idx = i + 2;

    const errs = validateRow(r, idx);
    if (errs.length) {
      errors.push(...errs);
      continue;
    }

    const areaName = r.Area.toString().trim();
    const cityNameRaw = r.City.toString().trim();
    const cityNameNorm = norm(cityNameRaw); // normalize CSV city
    const pin = parseInt(String(r.Pincode), 10);

    let cityId = cityByNorm.get(cityNameNorm);
    if (!cityId) {
      // fallback: case-insensitive DB search
      const rx = new RegExp(`^${escapeRegex(cityNameRaw)}$`, 'i');
      const found = await City.findOne({ name: rx }, { _id: 1 }).lean();
      if (found) {
        cityId = found._id as Types.ObjectId;
        cityByNorm.set(cityNameNorm, cityId); // cache normalized form
      }
    }

    if (!cityId) {
      errors.push(`Row ${idx}: City not found for "${cityNameRaw}"`);
      continue;
    }

    const lat = r.Latitude != null ? parseFloat(String(r.Latitude)) : undefined;
    const lng =
      r.Longitude != null ? parseFloat(String(r.Longitude)) : undefined;

    const hasCoords =
      typeof lat === 'number' &&
      !isNaN(lat) &&
      typeof lng === 'number' &&
      !isNaN(lng);

    const filter = {
      name: new RegExp(`^${escapeRegex(areaName)}$`, 'i'),
      city: cityId,
    };

    const update: Record<string, unknown> = {
      $set: {
        name: areaName,
        pinCode: [pin],
        city: cityId,
        updatedBy: superAdminId,
        updatedAt: new Date(),
      },
      $setOnInsert: { createdBy: superAdminId, createdAt: new Date() },
    };

    if (hasCoords) {
      (update.$set as Record<string, unknown>).loc = {
        type: 'Point',
        coordinates: [lng!, lat!],
      };
    }

    ops.push({ updateOne: { filter, update, upsert: true } });

    if (ops.length >= 1000) {
      const res = await Area.bulkWrite(ops, { ordered: false });
      inserted += res.upsertedCount || 0;
      updated += res.modifiedCount || 0;
      processed += ops.length;
      log('info', `Progress: ${processed}/${rows.length}`);
      ops.length = 0;
    }
  }

  if (ops.length) {
    const res = await Area.bulkWrite(ops, { ordered: false });
    inserted += res.upsertedCount || 0;
    updated += res.modifiedCount || 0;
    processed += ops.length;
  }

  log('success', 'Import finished.');
  console.log('='.repeat(50));
  console.log(
    `Rows: ${rows.length}, Inserted: ${inserted}, Updated: ${updated}, Errors: ${errors.length}`,
  );

  if (errors.length) {
    console.log('-'.repeat(50));
    errors.slice(0, 20).forEach((e) => console.log('•', e));
    if (errors.length > 20) console.log(`... and ${errors.length - 20} more`);
  }
}

// importAreas()
//   .then(() => process.exit(0))
//   .catch((err) => {
//     log('error', err.message);
//     process.exit(1);
//   });

// export { importAreas };
