/*
 * RiskScape™ Copyright New Zealand Institute for Earth Science Limited
 * (Earth Sciences New Zealand) is distributed for research purposes only
 * under the terms of AGPLv3.
 *
 * RiskScape™ Copyright 2026 New Zealand Institute for Earth Science
 * Limited (Earth Sciences New Zealand). All rights reserved. Source code
 * available under the AGPLv3.
 * 
 * This program is free software: you can redistribute it and/or modify it under
 *  the terms of the GNU Affero General Public License as published by the Free
 *  Software Foundation, either version 3 of the License, or (at your option) any
 *  later version.
 * 
 * This program is distributed for RESEARCH PURPOSES ONLY, in the hope that it will
 * be useful for research and education initiatives.
 * 
 * If you are not a researcher, or you are a researcher who wishes to use this
 * program on terms other than AGPLv3 (including those who wish to restrict the
 * distribution of any source code created using this program), please contact:
 * https://riskscape.org.nz
 * 
 * This program is distributed WITHOUT ANY WARRANTY; without even the implied
 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Affero General Public License for more details.  You should have received a copy
 * of the GNU Affero General Public License along with this program.  If not, see
 * <http://www.gnu.org/licenses/>.
 * 
 * By way of summary only, under the AGPLv3:
 *     • Permissions of this strongest copyleft license are conditioned
 *       on making available complete source code of licensed works and
 *       modifications, which include larger works using a licensed work,
 *       under the same license.
 *     • Copyright and license notices must be preserved.
 *     • Contributors provide an express grant of patent rights.
 *     • When a modified version is used to provide a service over a
 *       network, the complete source code of the modified version must be made
 *       available.
 */
package nz.org.riskscape.engine.cli.tests;

import static nz.org.riskscape.engine.Assert.*;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;

import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.List;

import org.junit.Test;

import com.google.common.collect.Lists;

import nz.org.riskscape.engine.test.EngineTestSettings;

/**
 * Exercise the getting-started examples in the docs to check they still work
 */
@EngineTestSettings("pipeline-threads=6") // to reproduce GL979 bug in canRunTotalExposedModelAfterFailedModelRun
public class GettingStartedTutorialTest extends BaseModelRunCommandTest {

  @Override
  public Path stdhome() {
    return Paths.get("..", "..", "examples", "getting-started");
  }

  @Test
  public void canRunTotalExposedModel() throws Exception {
    runCommand.modelId = "total-exposed";

    runCommand.run();

    List<List<String>> results = openCsv("summary.csv", "Results", "Number_Exposed", "Total_buildings",
        "Depth.range_<_1.count", "Depth.range_1_2.count", "Depth.range_2_3.count", "Depth.range_3_+.count");

    assertThat(results, contains(Arrays.asList("Total", "2059", "6260", "473", "394", "472", "720")));
  }

  @Test
  public void canRunTotalExposedModelAfterFailedModelRun() throws Exception {
    runCommand.modelId = "total-exposed";
    // Use_Cat is a string field. So parsing it as an int will make the pipeline blow up.
    runCommand.parameters = Lists.newArrayList(
        "report-summary.aggregate[1] = max(int(exposure.Use_Cat)) as Total_buildings");
    assertThrows(Exception.class, () -> runCommand.run());

    // The model run above has previously resulted in a scheduler deadlock (GL979)
    // we we're checking that the scheduler is still good to go for the next model run here.
    runCommand.parameters = Lists.newArrayList();
    runCommand.run();

    List<List<String>> results = openCsv("summary-1.csv", "Results", "Number_Exposed", "Total_buildings",
        "Depth.range_<_1.count", "Depth.range_1_2.count", "Depth.range_2_3.count", "Depth.range_3_+.count");

    assertThat(results, contains(Arrays.asList("Total", "2059", "6260", "473", "394", "472", "720")));
  }

  @Test
  public void canRunExposedByRegionModel() throws Exception {
    runCommand.modelId = "exposed-by-region";

    runCommand.run();

    List<List<String>> results = openCsv("summary.csv", "Region", "Number_Exposed", "Total_buildings",
        "Depth.range_<_1.count", "Depth.range_1_2.count", "Depth.range_2_3.count", "Depth.range_3_+.count");

    assertThat(results, contains(
        Arrays.asList("Aleipata Itupa i Lalo", "531", "863", "89", "154", "166", "122"),
        Arrays.asList("Aleipata Itupa i Luga", "345", "555", "74", "21", "42", "208"),
        Arrays.asList("Anoamaa East", "0", "322", "0", "0", "0", "0"),
        Arrays.asList("Anoamaa West", "0", "32", "0", "0", "0", "0"),
        Arrays.asList("Falealili", "749", "1762", "225", "166", "150", "208"),
        Arrays.asList("Lepa", "288", "520", "41", "31", "76", "140"),
        Arrays.asList("Lotofaga", "146", "423", "44", "22", "38", "42"),
        Arrays.asList("Safata", "0", "595", "0", "0", "0", "0"),
        Arrays.asList("Siumu", "0", "682", "0", "0", "0", "0"),
        Arrays.asList("Vaa o Fonoti", "0", "405", "0", "0", "0", "0"),
        Arrays.asList("Vaimauga West", "0", "101", "0", "0", "0", "0")
    ));
  }

  @Test
  public void canRunExposedByRegionModelWithGeopackageData() throws Exception {
    runCommand.modelId = "exposed-by-region";
    runCommand.parameters = Arrays.asList(
        "input-exposures.layer=Buildings_SE_Upolu.gpkg",
        "input-areas.layer=Samoa_constituencies.gpkg");

    runCommand.run();

    List<List<String>> results = openCsv("summary.csv", "Region", "Number_Exposed", "Total_buildings",
        "Depth.range_<_1.count", "Depth.range_1_2.count", "Depth.range_2_3.count", "Depth.range_3_+.count");

    assertThat(results, contains(
        Arrays.asList("Aleipata Itupa i Lalo", "531", "863", "89", "154", "166", "122"),
        Arrays.asList("Aleipata Itupa i Luga", "345", "555", "74", "21", "42", "208"),
        Arrays.asList("Anoamaa East", "0", "322", "0", "0", "0", "0"),
        Arrays.asList("Anoamaa West", "0", "32", "0", "0", "0", "0"),
        Arrays.asList("Falealili", "749", "1762", "225", "166", "150", "208"),
        Arrays.asList("Lepa", "288", "520", "41", "31", "76", "140"),
        Arrays.asList("Lotofaga", "146", "423", "44", "22", "38", "42"),
        Arrays.asList("Safata", "0", "595", "0", "0", "0", "0"),
        Arrays.asList("Siumu", "0", "682", "0", "0", "0", "0"),
        Arrays.asList("Vaa o Fonoti", "0", "405", "0", "0", "0", "0"),
        Arrays.asList("Vaimauga West", "0", "101", "0", "0", "0", "0")
    ));
  }

  @Test
  public void canRunTotalExposedModelAndChangeTheHazard() throws Exception {
    runCommand.modelId = "total-exposed";
    runCommand.parameters = Arrays.asList("input-hazards.layer=MaxEnv_All_Scenarios_10m.tif");

    runCommand.run();

    List<List<String>> results = openCsv("summary.csv", "Results", "Number_Exposed", "Total_buildings",
        "Depth.range_<_1.count", "Depth.range_1_2.count", "Depth.range_2_3.count", "Depth.range_3_+.count");

    assertThat(results, contains(
        Arrays.asList("Total", "1568", "6260", "596", "405", "334", "233")
    ));
  }

  @Test
  public void canRunTotalExposedModelWithDifferentParameters() throws Exception {
    runCommand.modelId = "total-exposed";
    runCommand.runnerOptions.replace = true;

    runCommand.parameters = Arrays.asList("report-summary.group-by[0]=exposure.Use_Cat");
    runCommand.run();
    List<List<String>> results = openCsv("summary.csv", "Use_Cat", "Number_Exposed", "Total_buildings",
        "Depth.range_<_1.count", "Depth.range_1_2.count", "Depth.range_2_3.count", "Depth.range_3_+.count");
    // spot-check a row of output
    assertThat(results, hasItem(Arrays.asList("Residential", "930", "1862", "247", "182", "227", "274")));

    runCommand.parameters = Arrays.asList("report-summary.group-by[0]=exposure.Cons_Frame");
    runCommand.run();
    results = openCsv("summary.csv", "Cons_Frame", "Number_Exposed", "Total_buildings",
        "Depth.range_<_1.count", "Depth.range_1_2.count", "Depth.range_2_3.count", "Depth.range_3_+.count");
    assertThat(results, hasItem(Arrays.asList("Masonry", "1608", "3385", "452", "318", "336", "502")));

    runCommand.parameters = Arrays.asList("report-summary.filter=exposure.Use_Cat != 'Outbuilding'");
    runCommand.run();
    results = openCsv("summary.csv", "Results", "Number_Exposed", "Total_buildings",
        "Depth.range_<_1.count", "Depth.range_1_2.count", "Depth.range_2_3.count", "Depth.range_3_+.count");
    assertThat(results, contains(Arrays.asList("Total", "1436", "4917", "289", "276", "361", "510")));

    runCommand.parameters = Arrays.asList("report-summary.filter=hazard >= 0.5 and exposure.Use_Cat = 'Residential'");
    runCommand.run();
    results = openCsv("summary.csv", "Results", "Number_Exposed", "Total_buildings",
        "Depth.range_<_1.count", "Depth.range_1_2.count", "Depth.range_2_3.count", "Depth.range_3_+.count");
    assertThat(results, contains(Arrays.asList("Total", "743", "743", "60", "182", "227", "274")));
  }

  @Test
  public void canRunBuildingDamageModel() throws Exception {
    runCommand.modelId = "building-damage";

    runCommand.run();

    // this model randomly allocates the damage-state to buildings. We could set the
    // random-seed, but the main thing is just checking that the model runs successfully
    openCsv("summary.csv", "Region", "Number_Exposed", "Total_buildings",
        "Damage.None.count", "Damage.Light.count", "Damage.Minor.count", "Damage.Moderate.count", "Damage.Severe.count",
        "Damage.Collapse.count");
  }

}
