/*
 * Copyright 2014-2019 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
 * either express or implied. See the License for the specific language
 * governing permissions and limitations under the License.
 */
package org.dbflute.intro.app.logic.document;

import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;

import javax.annotation.Resource;

import org.apache.commons.io.IOUtils;
import org.dbflute.intro.app.logic.intro.IntroPhysicalLogic;
import org.dbflute.intro.dbflute.allcommon.CDef;
import org.dbflute.optional.OptionalThing;

/**
 * @author deco
 * @author jflute
 * @author cabos
 * @author subaru
 */
public class DocumentPhysicalLogic {

    // ===================================================================================
    //                                                                           Attribute
    //                                                                           =========
    @Resource
    private IntroPhysicalLogic introPhysicalLogic;

    // ===================================================================================
    //                                                                         Find/Exists
    //                                                                         ===========
    public boolean existsSchemaHtml(String clientProject) {
        return findSchemaHtml(clientProject).exists();
    }

    public boolean existsHistoryHtml(String clientProject) {
        return findHistoryHtml(clientProject).exists();
    }

    public boolean existsSyncCheckResultHtml(String clientProject) {
        return findSyncCheckResultHtml(clientProject).exists();
    }

    public boolean existsAlterCheckResultHtml(String clientProject) {
        return findAlterCheckResultHtml(clientProject).exists();
    }

    public File findSchemaHtml(String clientProject) {
        return toProjectNamedDocumentFile(clientProject, "schema");
    }

    public File findHistoryHtml(String clientProject) {
        return toProjectNamedDocumentFile(clientProject, "history");
    }

    public File findPropertiesHtml(String clientProject) {
        return toProjectNamedDocumentFile(clientProject, "properties");
    }

    public File findSyncCheckResultHtml(String clientProject) {
        return toFixedNamedDocumentFile(clientProject, "sync-check-result.html");
    }

    public File findAlterCheckResultHtml(String clientProject) {
        return toProjectNamedMigrationFile(clientProject, "schema");
    }

    public File findLastaDocHtml(String clientProject, String moduleName) {
        return toProjectNamedDocumentFile(clientProject, moduleName, "lastadoc");
    }

    public OptionalThing<File> findAlterDir(String clientProject) {
        File file = new File(buildMigrationPath(clientProject, "", "alter"));
        if (file.exists()) {
            return OptionalThing.of(file);
        }
        return OptionalThing.empty();
    }

    public CDef.NgMark findAlterCheckNgMark(String clientProject) {
        for (CDef.NgMark ngMark : CDef.NgMark.listAll()) {
            if (new File(buildMigrationPath(clientProject, ngMark.code() + ".dfmark")).exists()) {
                return ngMark;
            }
        }
        return null;
    }

    public List<AlterSqlBean> findStackedAlterSqls(String clientProject) {
        final String zipPath = buildCheckedAlterZipPath(clientProject);
        if (zipPath != null) {
            try {
                return findAlterSqlsFromZip(zipPath);
            } catch (IOException e) {
                throw new IllegalStateException("Failed to read zip file: " + zipPath, e);
            }
        }
        return Collections.emptyList();
    }

    private List<AlterSqlBean> findAlterSqlsFromZip(String zipPath) throws IOException {
        final List<AlterSqlBean> alterSqls = new ArrayList<>();
        final ZipFile zipFile = new ZipFile(zipPath);
        final Enumeration<? extends ZipEntry> entries = zipFile.entries();
        while (entries.hasMoreElements()) {
            final ZipEntry entry = entries.nextElement();
            final String fileName = entry.getName();
            final String content = IOUtils.toString(zipFile.getInputStream(entry), StandardCharsets.UTF_8);
            alterSqls.add(new AlterSqlBean(fileName, content));
        }
        return alterSqls;
    }

    // ===================================================================================
    //                                                                                Path
    //                                                                                ====
    private File toProjectNamedDocumentFile(String clientProject, String module, String type) {
        String pureName = type + "-" + module + ".html";
        return toFixedNamedDocumentFile(clientProject, pureName);
    }

    private File toProjectNamedDocumentFile(String clientProject, String type) { // e.g. SchemaHtml
        final String pureName = type + "-" + clientProject + ".html";
        return toFixedNamedDocumentFile(clientProject, pureName);
    }

    private File toFixedNamedDocumentFile(String clientProject, String pureName) { // e.g. SchemaSyncCheck
        return new File(buildDocumentPath(clientProject, pureName));
    }

    private File toProjectNamedMigrationFile(String clientProject, String type) { // e.g. AlterCheck
        final String pureName;

        switch (type) {
        case "schema":
            pureName = "alter-check-result.html";
            break;
        default:
            return null;
        }
        return toFixedNamedMigrationFile(clientProject, type, pureName);
    }

    private File toFixedNamedMigrationFile(String clientProject, String type, String pureName) { // e.g. SchemaSyncCheck
        return new File(buildMigrationPath(clientProject, type, pureName));
    }

    private String buildDocumentPath(String clientProject, String pureName) {
        return introPhysicalLogic.buildClientPath(clientProject, "output", "doc", pureName);
    }

    private String buildMigrationPath(String clientProject, String pureName) {
        return introPhysicalLogic.buildClientPath(clientProject, "playsql", "migration", pureName);
    }

    private String buildMigrationPath(String clientProject, String type, String pureName) {
        return introPhysicalLogic.buildClientPath(clientProject, "playsql", "migration", type, pureName);
    }

    private String buildCheckedAlterZipPath(String clientProject) {
        final String historyPath = introPhysicalLogic.buildClientPath(clientProject, "playsql", "migration", "history");
        try {
            return Files.walk(Paths.get(historyPath))
                    .filter(Files::isRegularFile)
                    .filter(path -> path.getFileName().toString().startsWith("checked"))
                    .map(path -> path.toString())
                    .findFirst()
                    .orElse(null);
        } catch (IOException e) {
            throw new IllegalStateException("Failed to get checked alter schema zip file under the directory: " + historyPath, e);
        }
    }
}

@@ -779,16 +779,43 @@
 .Files;%0A
+import java.nio.file.Path;%0A
 import j
@@ -7193,22 +7193,20 @@
   final 
-String
+Path
  history
@@ -7212,16 +7212,26 @@
 yPath = 
+Paths.get(
 introPhy
@@ -7302,26 +7302,103 @@
 , %22history%22)
+)
 ;%0A
+        if (!Files.exists(historyPath)) %7B%0A           return null;%0A        %7D%0A
         try 
@@ -7429,26 +7429,16 @@
 es.walk(
-Paths.get(
 historyP
@@ -7441,17 +7441,16 @@
 oryPath)
-)
 %0A       

fix bug of finding alter zip path