package org.micord.utils;

import java.io.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;

import static org.assertj.core.api.Assertions.assertThat;

/**
 * Utility class for file operations in tests.
 */
public class FileTestUtils {

    /**
     * Count the number of lines in a CSV file
     */
    public static long countCsvLines(File csvFile) throws IOException {
        try (BufferedReader reader = new BufferedReader(
                new InputStreamReader(new FileInputStream(csvFile), StandardCharsets.UTF_8))) {
            return reader.lines().count();
        }
    }

    /**
     * Read all lines from a CSV file
     */
    public static List<String> readCsvLines(File csvFile) throws IOException {
        return Files.readAllLines(csvFile.toPath(), StandardCharsets.UTF_8);
    }

    /**
     * Parse CSV line (simple implementation for testing)
     */
    public static String[] parseCsvLine(String line) {
        List<String> result = new ArrayList<>();
        StringBuilder current = new StringBuilder();
        boolean inQuotes = false;

        for (int i = 0; i < line.length(); i++) {
            char c = line.charAt(i);

            if (c == '"') {
                if (i + 1 < line.length() && line.charAt(i + 1) == '"') {
                    current.append('"');
                    i++; // Skip next quote
                } else {
                    inQuotes = !inQuotes;
                }
            } else if (c == ',' && !inQuotes) {
                result.add(current.toString());
                current = new StringBuilder();
            } else {
                current.append(c);
            }
        }

        result.add(current.toString());
        return result.toArray(new String[0]);
    }

    /**
     * Verify CSV file structure
     */
    public static void verifyCsvStructure(File csvFile, int expectedColumns) throws IOException {
        List<String> lines = readCsvLines(csvFile);
        assertThat(lines).isNotEmpty();

        // Check header
        String[] header = parseCsvLine(lines.get(0));
        assertThat(header).hasSize(expectedColumns);

        // Check data rows have same number of columns
        for (int i = 1; i < Math.min(10, lines.size()); i++) {
            String[] row = parseCsvLine(lines.get(i));
            assertThat(row).hasSize(expectedColumns);
        }
    }

    /**
     * Count total rows across all CSV files in a ZIP
     */
    public static long countTotalRowsInZip(File zipFile) throws IOException {
        long totalRows = 0;

        try (ZipFile zip = new ZipFile(zipFile)) {
            for (ZipEntry entry : java.util.Collections.list(zip.entries())) {
                if (entry.getName().endsWith(".csv")) {
                    try (BufferedReader reader = new BufferedReader(
                            new InputStreamReader(zip.getInputStream(entry), StandardCharsets.UTF_8))) {
                        // Count lines excluding header
                        long lines = reader.lines().count() - 1;
                        totalRows += lines;
                    }
                }
            }
        }

        return totalRows;
    }

    /**
     * Extract all CSV files from a ZIP to a temporary directory
     */
    public static List<File> extractCsvFromZip(File zipFile, Path tempDir) throws IOException {
        List<File> extractedFiles = new ArrayList<>();

        try (ZipFile zip = new ZipFile(zipFile)) {
            for (ZipEntry entry : java.util.Collections.list(zip.entries())) {
                if (entry.getName().endsWith(".csv")) {
                    Path outputPath = tempDir.resolve(entry.getName());

                    try (InputStream is = zip.getInputStream(entry);
                         OutputStream os = Files.newOutputStream(outputPath)) {
                        byte[] buffer = new byte[8192];
                        int len;
                        while ((len = is.read(buffer)) > 0) {
                            os.write(buffer, 0, len);
                        }
                    }

                    extractedFiles.add(outputPath.toFile());
                }
            }
        }

        return extractedFiles;
    }

    /**
     * Verify that each CSV file in ZIP has headers
     */
    public static void verifyZipCsvHeaders(File zipFile, String[] expectedHeaders) throws IOException {
        try (ZipFile zip = new ZipFile(zipFile)) {
            for (ZipEntry entry : java.util.Collections.list(zip.entries())) {
                if (entry.getName().endsWith(".csv")) {
                    try (BufferedReader reader = new BufferedReader(
                            new InputStreamReader(zip.getInputStream(entry), StandardCharsets.UTF_8))) {
                        String firstLine = reader.readLine();
                        assertThat(firstLine).isNotNull();

                        String[] headers = parseCsvLine(firstLine);
                        assertThat(headers).containsExactly(expectedHeaders);
                    }
                }
            }
        }
    }

    /**
     * Create a temporary CSV file with test data
     */
    public static File createTempCsvFile(List<String[]> data, Path tempDir) throws IOException {
        File csvFile = Files.createTempFile(tempDir, "test-", ".csv").toFile();

        try (PrintWriter writer = new PrintWriter(
                new OutputStreamWriter(new FileOutputStream(csvFile), StandardCharsets.UTF_8))) {
            for (String[] row : data) {
                writer.println(formatCsvRow(row));
            }
        }

        return csvFile;
    }

    /**
     * Format a row for CSV output
     */
    private static String formatCsvRow(String[] row) {
        StringBuilder result = new StringBuilder();
        for (int i = 0; i < row.length; i++) {
            if (i > 0) {
                result.append(",");
            }
            result.append("\"");
            result.append(row[i] != null ? row[i].replace("\"", "\"\"") : "");
            result.append("\"");
        }
        return result.toString();
    }

    /**
     * Get file size in MB
     */
    public static double getFileSizeInMB(File file) {
        return file.length() / (1024.0 * 1024.0);
    }

    /**
     * Verify file is within size limits
     */
    public static void verifyFileSize(File file, double maxSizeMB) {
        double sizeMB = getFileSizeInMB(file);
        assertThat(sizeMB).isLessThanOrEqualTo(maxSizeMB);
    }
}