package com.dua3.meja;

import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Locale;
import java.util.function.BiFunction;

import org.junit.Assert;

import com.dua3.meja.model.Cell;
import com.dua3.meja.model.RefOption;
import com.dua3.meja.model.Workbook;
import com.dua3.meja.util.MejaHelper;
import com.dua3.utility.io.FileSystemView;

public class WorkbookTestHelper {

    public static Workbook loadWorkbook() throws Exception {
        Class<? extends WorkbookTestHelper> clazz = WorkbookTestHelper.class;
        String fileName = clazz.getSimpleName()+".xlsx";

        Workbook workbook;

        // the FileSystemView is needed in case the test is run from a jar file
        try (FileSystemView fsv = FileSystemView.create(clazz)) {
            Path wbPath = fsv.resolve(fileName);
            workbook = MejaHelper.openWorkbook(wbPath);
        } catch (IOException e) {
            System.out.format("--- WORKAROUND --- caught IOException, trying to fix path%n");
            // WORKAROUND - If anyone knows a less hackish solution for this, please send a pull request!

            // When tests are run from within gradle, resources are placed in another location (outside of classpath).
            // In that case, we try to guess the correct location of the resource files to be able to perform the tests.
            System.err.println("Resource not found! "+e.getMessage());
            String pathStr = clazz.getResource(".").getPath();

            System.out.format("--- WORKAROUND --- current path is %s%n", pathStr);

            // When started from within Bash on windows, a slash is prepended to the path returned by getResource.
            // We have to remove it again.
            if (pathStr.matches("^/[a-zA-Z]:/.*")) {
                System.out.format("FIXING PATH%n");
                pathStr = pathStr.replaceFirst("^/", "");
                System.out.format("--- WORKAROUND --- fix windows bash path to %s%n", pathStr);
            }

            // Change the path so that it points to the probable resource dir.
            String s = pathStr.replaceAll("/build/classes/java/test/", "/build/resources/test/");
            System.out.format("--- WORKAROUND --- path to resources is now %s%n", pathStr);

            // Then try to load the workbook from there.
            Path path = Paths.get(s);
            try (FileSystemView fsv = FileSystemView.create(path)) {
                Path wbPath = fsv.resolve(fileName);
                workbook = MejaHelper.openWorkbook(wbPath);
            }
        }

        return workbook;
    }

    /**
     * Test formatting applied when calling Cell.getAsText().
     * <p>
     * The workboook 'FormatTest.xlsx' is read from the classpath. Each sheet contains for columns used for testing:
     * <ul>
     * <li> description of what is being tested in the current row
     * <li> value with an applied format to be tested
     * <li> the expected result as a {@code String}
     * <li> an optional remark - if it contains the text {@literal #IGNORE#}, the row is skipped
     * </ul>
     * </p>
     * @param workbook the workbook under test
     */
    public static void testFormat_getAsText(Workbook workbook) {
        testFormatHelper(workbook, (cell,locale) -> cell.getAsText(locale).toString());
    }

    /**
     * Test formatting applied when calling Cell.toString().
     * <p>
     * The workboook 'FormatTest.xlsx' is read from the classpath. Each sheet contains for columns used for testing:
     * <ul>
     * <li> description of what is being tested in the current row
     * <li> value with an applied format to be tested
     * <li> the expected result as a {@code String}
     * <li> an optional remark - if it contains the text {@literal #IGNORE#}, the row is skipped
     * </ul>
     * </p>
     * @param workbook the workbook under test
     */
    static public void testFormat_toString(Workbook workbook) {
        testFormatHelper(workbook, (cell,locale) -> cell.toString(locale));
    }

    /**
     * Test formatting.
     * <p>
     * The workboook 'FormatTest.xlsx' is read from the classpath. Each sheet contains for columns used for testing:
     * <ul>
     * <li> A Flag to indicate ignored test cases
     * <li> description of what is being tested in the current row
     * <li> the locale to use
     * <li> value with an applied format to be tested
     * <li> the expected result as a {@code String}
     * <li> an optional remark
     * </ul>
     * </p>
     * @param extract a lambda expression maps (Cell, Locale) -> (formatted cell content)
     */
    public static void testFormatHelper(Workbook workbook, BiFunction<Cell,Locale,String> extract) {
        workbook.sheets()
            .peek(s -> System.out.format("Processing sheet '%s'%n", s.getSheetName()))
            .forEach(s -> {
                s.rows()
                    .skip(1)
                    .forEach(r -> {
                        boolean ignored = r.getCell(0).toString().equalsIgnoreCase("x");
                        if (ignored) {
                            System.out.format("line %d ignored%n", r.getRowNumber()+1);
                        } else {
                            String description = r.getCell(1).toString();

                            Cell languageCell = r.getCell(2);
                            String languageTag = languageCell.toString();
                            Locale locale = Locale.forLanguageTag(languageTag);
                            if (!languageTag.equals(locale.toLanguageTag())) {
                                throw new IllegalStateException("Check language tag in cell "+languageCell.getCellRef(RefOption.WITH_SHEET));
                            }

                            Cell actualCell = r.getCell(3);
                            Cell expectedCell = r.getCell(4);
                            Cell alternativeCell = r.getCell(4);

                            String styleName = actualCell.getCellStyle().getName();

                            String actual = extract.apply(actualCell, locale);
                            String expected = expectedCell.toString();
                            String alternative = alternativeCell.toString();

                            // We have an expected result, and an accepted alternative form. JUnit should report
                            // - SUCCESS: if actual.equals(expected)
                            // - IGNORED: if actual.equals(alternative)
                            // - FAILED:  for any other result
                            if (!alternative.isEmpty() && !actual.equals(expected)) {
                                Assert.assertEquals(String.format("in line %d [style=%s]: %s - alternative '%s', actual '%s'",
                                        r.getRowNumber()+1, styleName, description,
                                        expected, alternative),
                                        expected, alternative);
                                // let the test be ignored
                                org.junit.Assume.assumeTrue(false);
                            } else {
                                Assert.assertEquals(String.format("in line %d [style=%s]: %s - expected '%s', actual '%s'",
                                        r.getRowNumber()+1, styleName, description,
                                        expected, actual),
                                        expected, actual);
                            }
                        }
                    });
            });
    }
}

@@ -6427,296 +6427,25 @@
  // 
-We have an expected result, and an accepted alternative form. JUnit should report%0D%0A                            // - SUCCESS: if actual.equals(expected)%0D%0A                            // - IGNORED: if actual.equals(alternative)%0D%0A                            // - FAILED:  for any other result
+accept both forms
 %0D%0A  
@@ -6908,101 +6908,115 @@
     
-// let the test be ignored%0D%0A                                org.junit.Assume.assumeTrue(false
+System.err.format(%22Cell %25s matches alternative result!%25n%22, alternativeCell.getCellRef(RefOption.WITH_SHEET)
 );%0D%0A

fix handling of alternative cells