/*
 * 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.problem;

import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;

import java.io.File;

import org.junit.Test;

import nz.org.riskscape.engine.problem.ProblemFactory;
import nz.org.riskscape.engine.problem.SeverityLevel;
import nz.org.riskscape.problem.Problem.Severity;

public class ProblemFactoryProxyTest {

  interface TestFactory extends ProblemFactory {
    Problem fileError(File file, String arg2);

    @SeverityLevel(Severity.INFO)
    Problem info();

    @SeverityLevel(Severity.WARNING)
    Problem warning();

    @SeverityLevel(Severity.ERROR)
    Problem error();

    Problem withKids(String meta, Problem... kids);
    Problem withKidsNoMeta(Problem... kids);

    @SeverityLevel(Severity.WARNING)
    Problem withKidsWarning(Problem... kids);
  }

  TestFactory subject = Problems.get(TestFactory.class);

  @Test
  public void canCreateProblemsFromFactoryProxy() {
    File someFile = new File("foo.txt");
    Problem problem = subject.fileError(someFile, "bar");
    // check code matches class name + function name
    assertThat(problem.getCode().name(), is("FILE_ERROR"));
    assertThat(problem.getCode().toKey(), is(TestFactory.class.getCanonicalName() + ".FILE_ERROR"));
    // check default severity is error
    assertThat(problem.getSeverity(), is(Severity.ERROR));
    // check that problem can have meta-data associated with it
    assertThat(problem.getAffectedObject().get(), is(someFile));
  }

  @Test
  public void twoIdenticalProblemsAreEqual() throws Exception {
    File someFile = new File("foo.txt");
    Problem problem0 = subject.fileError(someFile, "foo");
    Problem problem1 = subject.fileError(someFile, "foo");

    assertEquals(problem0, problem1);
  }

  @Test
  public void twoDifferentProblemsAreNotEqual() throws Exception {
    File someFile = new File("foo.txt");
    Problem problem0 = subject.fileError(someFile, "foo");
    Problem problem1 = subject.fileError(someFile, "bar");

    assertNotEquals(problem0, problem1);
  }

  @Test
  public void canSetSeverityViaAnnotation() throws Exception {
    Problem problem = subject.info();
    assertThat(problem.getSeverity(), is(Severity.INFO));
  }

  @Test
  public void canSupplyChildrenViaVarArgs() throws Exception {
    Problem problem = subject.withKids("foo", subject.error());
    assertThat(problem.getArguments(), equalTo(new Object[] {"foo"}));
    assertThat(problem.getChildren(), contains(subject.error()));
    assertThat(problem.getAffectedObject().get(), equalTo("foo"));
    assertThat(problem.getSeverity(), equalTo(Severity.ERROR));

    // make sure it behaves when no children given
    problem = subject.withKids("foo");
    assertThat(problem.getArguments(), equalTo(new Object[] {"foo"}));
    assertThat(problem.getChildren(), empty());
    assertThat(problem.getAffectedObject().get(), equalTo("foo"));
    assertThat(problem.getSeverity(), equalTo(Severity.ERROR));
  }

  @Test
  public void noAffectingIsSetIfNoArgButChildren() throws Exception {
    Problem problem = subject.withKidsNoMeta(subject.error());

    assertThat(problem.getArguments(), equalTo(new Object[0]));
    assertThat(problem.getChildren(), contains(subject.error()));
    assertTrue(problem.getAffectedObject().isEmpty());
  }

  @Test
  public void severitySetFromChildrenWhenGiven() {
    Problem problem = subject.withKidsNoMeta(subject.info(), subject.warning());

    assertThat(problem.getChildren(), contains(subject.info(), subject.warning()));
    assertThat(problem.getSeverity(), equalTo(Severity.WARNING));
  }

  @Test
  public void severityDefaultRespectedWhenNoChildrenGiven() {
    assertThat(subject.withKidsWarning().getSeverity(), equalTo(Severity.WARNING));

    assertThat(subject.withKidsWarning(subject.info()).getSeverity(), equalTo(Severity.INFO));
    assertThat(subject.withKidsWarning(subject.info(), subject.error()).getSeverity(), equalTo(Severity.ERROR));
  }
}
