package org.raddatz.familienarchiv.importing;

import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.api.io.TempDir;
import org.mockito.junit.jupiter.MockitoExtension;
import org.raddatz.familienarchiv.tag.Tag;
import org.raddatz.familienarchiv.tag.TagService;

import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
import java.util.UUID;

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

@ExtendWith(MockitoExtension.class)
class TagTreeImporterTest {

    @Test
    void load_upsertsRootTagWithNullParent(@TempDir Path tempDir) throws Exception {
        TagService tagService = mock(TagService.class);
        when(tagService.upsertBySourceRef(any(), any(), any()))
                .thenAnswer(inv -> tagOf(inv.getArgument(0), inv.getArgument(1), inv.getArgument(2)));
        Path xlsx = writeTagTree(tempDir, List.<String[]>of(
                new String[]{"Themen", "", "Themen"}));

        new TagTreeImporter(tagService).load(xlsx.toFile());

        verify(tagService).upsertBySourceRef("Themen", "Themen", null);
    }

    @Test
    void load_resolvesParentByPath_forChildTag(@TempDir Path tempDir) throws Exception {
        TagService tagService = mock(TagService.class);
        UUID rootId = UUID.randomUUID();
        when(tagService.upsertBySourceRef(eq("Themen"), eq("Themen"), isNull()))
                .thenReturn(tagOf("Themen", "Themen", null, rootId));
        when(tagService.upsertBySourceRef(eq("Themen/Brautbriefe"), eq("Brautbriefe"), eq(rootId)))
                .thenReturn(tagOf("Themen/Brautbriefe", "Brautbriefe", rootId));
        Path xlsx = writeTagTree(tempDir, List.<String[]>of(
                new String[]{"Themen", "", "Themen"},
                new String[]{"Themen/Brautbriefe", "Themen", "Brautbriefe"}));

        new TagTreeImporter(tagService).load(xlsx.toFile());

        verify(tagService).upsertBySourceRef("Themen/Brautbriefe", "Brautbriefe", rootId);
    }

    @Test
    void load_returnsCountOfProcessedRows(@TempDir Path tempDir) throws Exception {
        TagService tagService = mock(TagService.class);
        when(tagService.upsertBySourceRef(any(), any(), any()))
                .thenAnswer(inv -> tagOf(inv.getArgument(0), inv.getArgument(1), inv.getArgument(2)));
        Path xlsx = writeTagTree(tempDir, List.<String[]>of(
                new String[]{"Themen", "", "Themen"},
                new String[]{"Themen/Brautbriefe", "Themen", "Brautbriefe"}));

        int processed = new TagTreeImporter(tagService).load(xlsx.toFile());

        assertThat(processed).isEqualTo(2);
    }

    private static Tag tagOf(String sourceRef, String name, UUID parentId) {
        return tagOf(sourceRef, name, parentId, UUID.randomUUID());
    }

    private static Tag tagOf(String sourceRef, String name, UUID parentId, UUID id) {
        return Tag.builder().id(id).sourceRef(sourceRef).name(name).parentId(parentId).build();
    }

    private Path writeTagTree(Path dir, List<String[]> rows) throws Exception {
        Path xlsx = dir.resolve("canonical-tag-tree.xlsx");
        try (XSSFWorkbook wb = new XSSFWorkbook()) {
            Sheet sheet = wb.createSheet("Sheet1");
            Row header = sheet.createRow(0);
            header.createCell(0).setCellValue("tag_path");
            header.createCell(1).setCellValue("parent_name");
            header.createCell(2).setCellValue("tag_name");
            for (int r = 0; r < rows.size(); r++) {
                Row row = sheet.createRow(r + 1);
                String[] values = rows.get(r);
                for (int c = 0; c < values.length; c++) {
                    row.createCell(c).setCellValue(values[c]);
                }
            }
            try (OutputStream out = Files.newOutputStream(xlsx)) {
                wb.write(out);
            }
        }
        return xlsx;
    }
}
