package org.micord.utils;

import java.time.LocalDate;
import java.util.*;
import java.util.concurrent.ThreadLocalRandom;

/**
 * Utility class for generating test data for download service tests.
 */
public class TestDataGenerator {

    private static final String[] FIRST_NAMES = {
        "John", "Jane", "Michael", "Sarah", "David", "Emma", "Robert", "Lisa", "William", "Mary"
    };

    private static final String[] LAST_NAMES = {
        "Smith", "Johnson", "Williams", "Brown", "Jones", "Garcia", "Miller", "Davis", "Rodriguez", "Martinez"
    };

    private static final String[] CITIES = {
        "New York", "Los Angeles", "Chicago", "Houston", "Phoenix", "Philadelphia", "San Antonio", "San Diego"
    };

    /**
     * Generate a list of maps representing CSV data rows
     */
    public static List<Map<String, Object>> generateMapData(int rows) {
        List<Map<String, Object>> data = new ArrayList<>();
        Random random = new Random();

        for (int i = 0; i < rows; i++) {
            Map<String, Object> row = new HashMap<>();
            row.put("id", UUID.randomUUID().toString());
            row.put("firstName", FIRST_NAMES[random.nextInt(FIRST_NAMES.length)]);
            row.put("lastName", LAST_NAMES[random.nextInt(LAST_NAMES.length)]);
            row.put("age", 18 + random.nextInt(60));
            row.put("city", CITIES[random.nextInt(CITIES.length)]);
            row.put("email", "user" + i + "@example.com");
            row.put("phone", generatePhoneNumber());
            row.put("registrationDate", generateRandomDate());
            row.put("isActive", random.nextBoolean());
            row.put("balance", String.format("%.2f", random.nextDouble() * 10000));

            data.add(row);
        }

        return data;
    }

    /**
     * Generate SQL-style array data (including headers)
     */
    public static List<String[]> generateSqlData(int rows, boolean includeHeaders) {
        List<String[]> data = new ArrayList<>();
        Random random = new Random();

        if (includeHeaders) {
            data.add(new String[]{
                "id", "firstName", "lastName", "age", "city",
                "email", "phone", "registrationDate", "isActive", "balance"
            });
        }

        for (int i = 0; i < rows; i++) {
            String[] row = new String[]{
                UUID.randomUUID().toString(),
                FIRST_NAMES[random.nextInt(FIRST_NAMES.length)],
                LAST_NAMES[random.nextInt(LAST_NAMES.length)],
                String.valueOf(18 + random.nextInt(60)),
                CITIES[random.nextInt(CITIES.length)],
                "user" + i + "@example.com",
                generatePhoneNumber(),
                generateRandomDate().toString(),
                String.valueOf(random.nextBoolean()),
                String.format("%.2f", random.nextDouble() * 10000)
            };
            data.add(row);
        }

        return data;
    }

    /**
     * Generate data with special characters that need CSV escaping
     */
    public static List<Map<String, Object>> generateProblematicData() {
        List<Map<String, Object>> data = new ArrayList<>();

        // Row with quotes
        Map<String, Object> row1 = new HashMap<>();
        row1.put("id", "1");
        row1.put("description", "This has \"quotes\" in it");
        row1.put("name", "John \"The Boss\" Smith");
        data.add(row1);

        // Row with commas
        Map<String, Object> row2 = new HashMap<>();
        row2.put("id", "2");
        row2.put("description", "Values, with, many, commas");
        row2.put("name", "Smith, John");
        data.add(row2);

        // Row with newlines
        Map<String, Object> row3 = new HashMap<>();
        row3.put("id", "3");
        row3.put("description", "First line\nSecond line\nThird line");
        row3.put("name", "Multi\nLine\nName");
        data.add(row3);

        // Row with null values
        Map<String, Object> row4 = new HashMap<>();
        row4.put("id", "4");
        row4.put("description", null);
        row4.put("name", null);
        data.add(row4);

        // Row with special characters
        Map<String, Object> row5 = new HashMap<>();
        row5.put("id", "5");
        row5.put("description", "Special chars: @#$%^&*()_+-=[]{}|;':\",./<>?");
        row5.put("name", "Ñoño José");
        data.add(row5);

        // Row with tabs
        Map<String, Object> row6 = new HashMap<>();
        row6.put("id", "6");
        row6.put("description", "Tab\tseparated\tvalues");
        row6.put("name", "Tab\tUser");
        data.add(row6);

        return data;
    }

    /**
     * Generate edge case datasets for boundary testing
     */
    public static class EdgeCaseDatasets {
        public static final int JUST_UNDER_LIMIT = 599999;
        public static final int EXACTLY_AT_LIMIT = 600000;
        public static final int JUST_OVER_LIMIT = 600001;
        public static final int ONE_AND_HALF_CHUNKS = 900000;
        public static final int EXACTLY_TWO_CHUNKS = 1200000;
        public static final int MULTIPLE_CHUNKS = 2400000;

        public static List<Map<String, Object>> getDataset(int size) {
            return generateMapData(size);
        }
    }

    /**
     * Generate large dataset in chunks to avoid memory issues
     */
    public static Iterator<Map<String, Object>> generateLargeDataIterator(int totalRows) {
        return new Iterator<Map<String, Object>>() {
            private int currentRow = 0;
            private final Random random = new Random();

            @Override
            public boolean hasNext() {
                return currentRow < totalRows;
            }

            @Override
            public Map<String, Object> next() {
                if (!hasNext()) {
                    throw new NoSuchElementException();
                }

                Map<String, Object> row = new HashMap<>();
                row.put("id", UUID.randomUUID().toString());
                row.put("rowNumber", currentRow);
                row.put("timestamp", System.currentTimeMillis());
                row.put("data", "Row " + currentRow + " of " + totalRows);
                row.put("random", random.nextDouble());

                currentRow++;
                return row;
            }
        };
    }

    private static String generatePhoneNumber() {
        Random random = new Random();
        return String.format("(%03d) %03d-%04d",
            random.nextInt(900) + 100,
            random.nextInt(900) + 100,
            random.nextInt(9000) + 1000);
    }

    private static LocalDate generateRandomDate() {
        long minDay = LocalDate.of(2020, 1, 1).toEpochDay();
        long maxDay = LocalDate.of(2024, 12, 31).toEpochDay();
        long randomDay = ThreadLocalRandom.current().nextLong(minDay, maxDay);
        return LocalDate.ofEpochDay(randomDay);
    }
}